Merge pull request #1119 from poanetwork/decode-tx-logs

feat: fix ABI decoding, and decode tx logs
pull/1126/head
Andrew Cravenho 6 years ago committed by GitHub
commit 958540de74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 25
      apps/block_scout_web/assets/css/components/_transaction-input.scss
  2. 69
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex
  3. 103
      apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
  4. 127
      apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex
  5. 115
      apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex
  6. 6
      apps/block_scout_web/lib/block_scout_web/views/transaction_log_view.ex
  7. 10
      apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
  8. 170
      apps/block_scout_web/priv/gettext/default.pot
  9. 172
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  10. 123
      apps/block_scout_web/test/block_scout_web/views/abi_encoded_value_view_test.exs
  11. 2
      apps/ethereum_jsonrpc/mix.exs
  12. 63
      apps/explorer/lib/explorer/chain/log.ex
  13. 64
      apps/explorer/test/explorer/chain/log_test.exs
  14. 2
      mix.lock

@ -1,9 +1,26 @@
.raw-transaction-input{
display: none;
display: none;
}
.raw-transaction-log-topics{
display: none;
}
.raw-transaction-log-data{
display: none;
}
.transaction-input-text{
resize: vertical;
overflow: auto;
word-break: break-all;
white-space: pre;
color: black;
pre{
code{
color: black;
}
}
}
.transaction-input-table{
overflow-x: scroll;
}

@ -0,0 +1,69 @@
<div class="card">
<div class="card-body">
<h1 class="card-title"><%= gettext "Input" %> </h1>
<!-- Input -->
<%= case @decoded_input_data do %>
<% {:error, :contract_not_verified} -> %>
<div class="alert alert-danger">
<%= gettext "To see decoded input data, the contract must be verified." %>
<%= case @transaction do %>
<% %{to_address: %{hash: hash}} -> %>
<%= gettext "Verify the contract " %><a href="<%= address_verify_contract_path(@conn, :new, hash)%>"><%= gettext "here" %></a>
<% _ -> %>
<%= nil %>
<% end %>
</div>
<% {:ok, method_id, text, mapping} -> %>
<table summary="<%= gettext "Transaction Info" %>" class="table thead-light table-bordered table-responsive transaction-info-table">
<tr>
<td><%= gettext "Method Id" %></td>
<td colspan="3"><code>0x<%= method_id %></code></td>
</tr>
<tr>
<td>Call</td>
<td colspan="3"><code><%= text %></code></td>
</tr>
</table>
<table summary="<%= gettext "Transaction Inputs" %>" class="table thead-light table-bordered table-responsive">
<tr>
<th scope="col"></th>
<th scope="col"><%= gettext "Name" %></th>
<th scope="col"><%= gettext "Type" %></th>
<th scope="col"><%= gettext "Data" %></th>
<tr>
<%= for {name, type, value} <- mapping do %>
<tr>
<th scope="row">
<%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %>
<% :error -> %>
<%= nil %>
<% copy_text -> %>
<button type="button" class="copy icon-link" data-toggle="tooltip" data-placement="top" data-clipboard-text="<%= copy_text %>" aria-label="<%= gettext "Copy Value" %>">
<i class="fas fa-clone"></i>
</button>
<% end %>
</th>
<td><%= name %></td>
<td><%= type %></td>
<td>
<%= case BlockScoutWeb.ABIEncodedValueView.value_html(type, value) do %>
<% :error -> %>
<div class="alert alert-danger">
<%= gettext "Error rendering value" %>
</div>
<% value -> %>
<pre class="transaction-input-text tile"><code><%= value %></code></pre>
<% end %>
</td>
</tr>
<% end %>
</table>
<% _ -> %>
<div class="alert alert-danger">
<%= gettext "Failed to decode input data." %>
</div>
<% end %>
</div>
</div>

@ -1,4 +1,5 @@
<% block = @transaction.block %>
<% decoded_input_data = decoded_input_data(@transaction) %>
<section data-page="transaction-details" data-page-transaction-hash="<%= @transaction %>">
<div class="row">
<div class="col-md-12 col-lg-8">
@ -74,84 +75,10 @@
</dd>
</dl>
<!-- Input -->
<dl class="row mb-0">
<%= case decoded_input_data(@transaction) do %>
<% {:error, :contract_not_verified} -> %>
<dt class="col-sm-3 text-muted"> <%= gettext "Input" %> </dt>
<dd class="col-sm-9">
<div class="alert alert-danger">
To see decoded input data, the <a href="<%= address_verify_contract_path(@conn, :new, @transaction.to_address.hash)%>">contract must be verified.</a>
</div>
</dd>
<% {:error, :could_not_decode} -> %>
<dt class="col-sm-3 text-muted"> <%= gettext "Input" %> </dt>
<dd class="col-sm-9">
<div class="alert alert-danger">
Failed to decode input data. Some dynamic types are not currently supported.
</div>
</dd>
<% {:ok, method_id, text, mapping} -> %>
<dt class="col-sm-3 text-muted"> <%= gettext "Input" %> </dt>
<dd class="col-sm-9">
<table summary="Transaction Info" class="table thead-light table-bordered">
<tr>
<td>Method Id</td>
<td colspan="3"><code>0x<%= method_id %></code></td>
</tr>
<tr>
<td>Call</td>
<td colspan="3"><code><%= text %></code></td>
</tr>
</table>
<table summary="Transaction Inputs" class="table thead-light table-bordered table-responsive">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Type</th>
<th scope="col">Data</th>
<th scope="col"></th>
<tr>
<%= for {{name, type, value}, index} <- Enum.with_index(mapping) do %>
<tr>
<th scope="row"><%= index %></th>
<td><%= name %></td>
<td><%= type %></td>
<%= case type do %>
<% "address" -> %>
<% address = "0x" <> Base.encode16(value, case: :lower) %>
<td>
<div class="transaction-input-text">
<a href="<%= address_path(@conn, :show, address) %>" target="_blank"> <%= address %> </a>
</div>
</td>
<td>
<button type="button" class="copy" id="button" data-toggle="tooltip" data-placement="top" data-clipboard-text="<%= address %>" aria-label="Copy Value">
<%= gettext "copy"%>
</button>
</td>
<% _ -> %>
<td>
<div class="transaction-input-text"><%= value %></textarea>
</td>
<td>
<button type="button" class="copy" id="button" data-toggle="tooltip" data-placement="top" data-clipboard-text="<%= value %>" aria-label="Copy Value">
<%= gettext "copy"%>
</button>
</td>
<% end %>
</tr>
<% end %>
</table>
</dd>
<% _ -> %>
<%= nil %>
<% end %>
<%= unless @transaction.input.bytes in [<<>>, nil] do %>
<dt class="col-sm-3 text-muted"><%= gettext "Raw Input" %></dt>
<dd class="col-sm-9">
<%= unless value_transfer?(@transaction) do %>
<dl class="row">
<dt class="col-sm-3 text-muted"><%= gettext "Raw Input" %></dt>
<dd class="col-sm-9">
<div swappable-item>
<button swapper class="button button-primary"><%= gettext "Show Raw Input"%></button>
</div>
@ -159,18 +86,19 @@
<button swapper type="button" class="close pr-2" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<button type="button" class="copy icon-link mb-1" data-toggle="tooltip" data-placement="top" data-clipboard-text="<%= @transaction.input %>" aria-label="Copy Value">
<i class="fas fa-clone"></i>
</button>
<div class="tile tile-muted">
<button type="button" class="copy" id="button" data-toggle="tooltip" data-placement="top" data-clipboard-text="<%= @transaction.input %>" aria-label="Copy Value">
<%= gettext "copy"%>
</button>
<pre class="pre-scrollable pre-scrollable-shorty pre-wrap mb-0">
<code><%= @transaction.input %></code>
</pre>
</div>
</div>
</dd>
<% end %>
</dd>
</dl>
<% end %>
</div>
</div>
</div>
@ -212,4 +140,13 @@
</div>
</div>
</div>
<%= unless should_decode?(@transaction) do %>
<div class="row">
<div class="col-md-12">
<%= render BlockScoutWeb.TransactionView, "_decoded_input.html", Map.put(assigns, :decoded_input_data, decoded_input_data) %>
</div>
</div>
<% end %>
</section>

@ -23,39 +23,116 @@
) %>
</h3>
</dd>
<dt class="col-md-1"><%= gettext "Topics" %></dt>
<dt class="col-md-1"><%= gettext "Decoded" %></dt>
<dd class="col-md-11">
<%= unless is_nil(log.first_topic) do %>
<div class="text-dark">
<span class="text-dark">[0]</span>
<%= log.first_topic %>
</div>
<% end %>
<%= unless is_nil(log.second_topic) do %>
<div class="text-dark">
<span class="">[1] </span>
<%= log.second_topic %>
</div>
<% end %>
<%= unless is_nil(log.third_topic) do %>
<div class="text-dark">
<span>[2]</span>
<%= log.third_topic %>
</div>
<% end %>
<%= unless is_nil(log.fourth_topic) do %>
<div class="text-dark">
<span>[3]</span>
<%= log.fourth_topic %>
</div>
<%= case decode(log, @transaction) do %>
<% {:error, :contract_not_verified} -> %>
<div class="alert alert-danger">
<%= gettext "To see decoded input data, the contract must be verified." %>
<%= case @transaction do %>
<% %{to_address: %{hash: hash}} -> %>
<%= gettext "Verify the contract " %><a href="<%= address_verify_contract_path(@conn, :new, hash)%>"><%= gettext "here" %></a>
<% _ -> %>
<%= nil %>
<% end %>
</div>
<% {:error, :could_not_decode} -> %>
<div class="alert alert-danger">
<%= gettext "Failed to decode log data." %>
</div>
<% {:ok, method_id, text, mapping} -> %>
<table summary="Transaction Info" class="table thead-light table-bordered transaction-input-table">
<tr>
<td>Method Id</td>
<td colspan="3"><code>0x<%= method_id %></code></td>
</tr>
<tr>
<td>Call</td>
<td colspan="3"><code><%= text %></code></td>
</tr>
</table>
<table style="color: black;" summary="<%= gettext "Log Data" %>" class="table thead-light table-bordered table-responsive">
<tr>
<th scope="col"></th>
<th scope="col"><%= gettext "Name" %></th>
<th scope="col"><%= gettext "Type" %></th>
<th scope="col"><%= gettext "Indexed?" %></th>
<th scope="col"><%= gettext "Data" %></th>
<tr>
<%= for {name, type, indexed?, value} <- mapping do %>
<tr>
<th scope="row">
<%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %>
<% :error -> %>
<%= nil %>
<% copy_text -> %>
<button type="button" class="copy icon-link" data-toggle="tooltip" data-placement="top" data-clipboard-text="<%= copy_text %>" aria-label="<%= gettext "Copy Value" %>">
<i class="fas fa-clone"></i>
</button>
<% end %>
</th>
<td><%= name %></td>
<td><%= type %></td>
<td><%= indexed? %></td>
<td>
<pre class="transaction-input-text tile"><code><%= BlockScoutWeb.ABIEncodedValueView.value_html(type, value) %></code></pre>
</td>
</tr>
<% end %>
</table>
<% _ -> %>
<%= nil %>
<% end %>
</dd>
<dt class="col-md-1"><%= gettext "Topics" %></dt>
<dd class="col-md-11">
<div swappable-item>
<button swapper class="button button-primary"><%= gettext "Show Raw Topics"%></button>
</div>
<div swappable-item class="raw-transaction-log-topics">
<button swapper type="button" class="close pr-2" aria-label="<%= gettext "Close" %>">
<span aria-hidden="true">&times;</span>
</button>
<%= unless is_nil(log.first_topic) do %>
<div class="text-dark">
<span class="text-dark">[0]</span>
<%= log.first_topic %>
</div>
<% end %>
<%= unless is_nil(log.second_topic) do %>
<div class="text-dark">
<span class="">[1] </span>
<%= log.second_topic %>
</div>
<% end %>
<%= unless is_nil(log.third_topic) do %>
<div class="text-dark">
<span>[2]</span>
<%= log.third_topic %>
</div>
<% end %>
<%= unless is_nil(log.fourth_topic) do %>
<div class="text-dark">
<span>[3]</span>
<%= log.fourth_topic %>
</div>
<% end %>
</div>
</dd>
<dt class="col-md-1">
<%= gettext "Data" %>
</dt>
<dd class="col-md-11">
<%= unless is_nil(log.data) do %>
<div class="text-dark">
<div swappable-item>
<button swapper class="button button-primary"><%= gettext "Show Raw Data"%></button>
</div>
<div swappable-item class="text-dark raw-transaction-log-data">
<button swapper type="button" class="close pr-2" aria-label="<%= gettext "Close" %>">
<span aria-hidden="true">&times;</span>
</button>
<%= log.data %>
</div>
<% end %>

@ -0,0 +1,115 @@
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
alias ABI.FunctionSelector
alias Phoenix.HTML
require Logger
def value_html(type, value) do
decoded_type = 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)
:error
end
def copy_text(type, value) do
decoded_type = 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)
:error
end
defp do_copy_text({:bytes, _type}, value) do
hex(value)
end
defp do_copy_text({:array, type, _}, value) do
do_copy_text({:array, type}, value)
end
defp do_copy_text({:array, type}, value) do
values =
value
|> Enum.map(&do_copy_text(type, &1))
|> Enum.intersperse(", ")
~E|[<%= values %>]|
end
defp do_copy_text(_, {:dynamic, value}) do
hex(value)
end
defp do_copy_text(type, value) when type in [:bytes, :address] do
hex(value)
end
defp 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
defp base_value_html(_, {:dynamic, value}) do
hex(value)
end
defp base_value_html(:address, value) do
address = hex(value)
~E|<a href="<%= address_path(BlockScoutWeb.Endpoint, :show, address) %>" target="_blank"><%= address %></a>|
end
defp base_value_html(:bytes, value) do
hex(value)
end
defp base_value_html(_, value), do: 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

@ -20,6 +20,12 @@ defmodule BlockScoutWeb.TransactionView do
def block_timestamp(%Transaction{block_number: nil, inserted_at: time}), do: time
def block_timestamp(%Transaction{block: %Block{timestamp: time}}), do: time
def value_transfer?(%Transaction{input: %{bytes: bytes}}) when bytes in [<<>>, nil] do
true
end
def value_transfer?(_), do: false
def confirmations(%Transaction{block: block}, named_arguments) when is_list(named_arguments) do
case block do
nil -> 0
@ -73,6 +79,10 @@ defmodule BlockScoutWeb.TransactionView do
Cldr.Number.to_string!(gas)
end
def should_decode?(transaction) do
contract_creation?(transaction) || value_transfer?(transaction)
end
def decoded_input_data(transaction) do
Transaction.decoded_input_data(transaction)
end

@ -56,7 +56,7 @@ msgid "%{subnetwork} Explorer - BlockScout"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:58
#: lib/block_scout_web/views/transaction_view.ex:64
msgid "(Awaiting internal transactions for status)"
msgstr ""
@ -151,7 +151,7 @@ msgid "Block %{block_number} - %{subnetwork} Explorer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:59
#: lib/block_scout_web/templates/transaction/overview.html.eex:60
msgid "Block Confirmations"
msgstr ""
@ -166,7 +166,7 @@ msgid "Block Mined, awaiting import..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:45
#: lib/block_scout_web/templates/transaction/overview.html.eex:46
msgid "Block Number"
msgstr ""
@ -218,6 +218,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/overview.html.eex:82
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:84
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:92
#: lib/block_scout_web/templates/transaction_log/index.html.eex:94
#: lib/block_scout_web/templates/transaction_log/index.html.eex:133
msgid "Close"
msgstr ""
@ -283,12 +285,12 @@ msgid "Contract Address Pending"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:131
#: lib/block_scout_web/views/transaction_view.ex:141
msgid "Contract Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:130
#: lib/block_scout_web/views/transaction_view.ex:140
msgid "Contract Creation"
msgstr ""
@ -329,13 +331,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:9
#: lib/block_scout_web/templates/transaction/overview.html.eex:10
#: lib/block_scout_web/templates/transaction/overview.html.eex:11
msgid "Copy Transaction Hash"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:9
#: lib/block_scout_web/templates/transaction/overview.html.eex:10
#: lib/block_scout_web/templates/transaction/overview.html.eex:11
msgid "Copy Txn Hash"
msgstr ""
@ -355,7 +357,9 @@ msgid "Curl"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:54
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:33
#: lib/block_scout_web/templates/transaction_log/index.html.eex:60
#: lib/block_scout_web/templates/transaction_log/index.html.eex:124
msgid "Data"
msgstr ""
@ -392,12 +396,12 @@ msgid "Error trying to fetch balances."
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:62
#: lib/block_scout_web/views/transaction_view.ex:68
msgid "Error: %{reason}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:60
#: lib/block_scout_web/views/transaction_view.ex:66
msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
@ -405,7 +409,7 @@ msgstr ""
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:16
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:19
#: lib/block_scout_web/templates/transaction/_tile.html.eex:26
#: lib/block_scout_web/templates/transaction/overview.html.eex:182
#: lib/block_scout_web/templates/transaction/overview.html.eex:110
#: lib/block_scout_web/views/wei_helpers.ex:72
msgid "Ether"
msgstr ""
@ -444,7 +448,7 @@ msgid "GET"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:195
#: lib/block_scout_web/templates/transaction/overview.html.eex:123
msgid "Gas"
msgstr ""
@ -488,9 +492,7 @@ msgid "Indexing Tokens"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:81
#: lib/block_scout_web/templates/transaction/overview.html.eex:88
#: lib/block_scout_web/templates/transaction/overview.html.eex:95
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:3
msgid "Input"
msgstr ""
@ -509,7 +511,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:43
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:10
#: lib/block_scout_web/views/address_view.ex:213
#: lib/block_scout_web/views/transaction_view.ex:180
#: lib/block_scout_web/views/transaction_view.ex:190
msgid "Internal Transactions"
msgstr ""
@ -527,7 +529,7 @@ msgid "Less than"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:207
#: lib/block_scout_web/templates/transaction/overview.html.eex:135
msgid "Limit"
msgstr ""
@ -535,7 +537,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:21
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:48
#: lib/block_scout_web/templates/transaction_log/index.html.eex:10
#: lib/block_scout_web/views/transaction_view.ex:181
#: lib/block_scout_web/views/transaction_view.ex:191
msgid "Logs"
msgstr ""
@ -547,7 +549,7 @@ msgid "Market Cap"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:49
#: lib/block_scout_web/views/transaction_view.ex:55
msgid "Max of"
msgstr ""
@ -588,6 +590,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:29
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:31
#: lib/block_scout_web/templates/transaction_log/index.html.eex:57
msgid "Name"
msgstr ""
@ -599,7 +603,7 @@ msgstr ""
#, elixir-format
#:
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:23
#: lib/block_scout_web/templates/transaction_log/index.html.eex:74
#: lib/block_scout_web/templates/transaction_log/index.html.eex:151
msgid "Newer"
msgstr ""
@ -622,7 +626,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:69
#: lib/block_scout_web/templates/transaction/overview.html.eex:66
#: lib/block_scout_web/templates/transaction/overview.html.eex:67
msgid "Nonce"
msgstr ""
@ -681,9 +685,9 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44
#: lib/block_scout_web/templates/transaction/overview.html.eex:54
#: lib/block_scout_web/views/transaction_view.ex:57
#: lib/block_scout_web/views/transaction_view.ex:87
#: lib/block_scout_web/templates/transaction/overview.html.eex:55
#: lib/block_scout_web/views/transaction_view.ex:63
#: lib/block_scout_web/views/transaction_view.ex:97
msgid "Pending"
msgstr ""
@ -788,14 +792,14 @@ msgid "Showing 250 addresses of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:59
#: lib/block_scout_web/views/transaction_view.ex:65
msgid "Success"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/_tile.html.eex:29
#: lib/block_scout_web/templates/transaction/overview.html.eex:71
#: lib/block_scout_web/templates/transaction/overview.html.eex:72
msgid "TX Fee"
msgstr ""
@ -821,7 +825,7 @@ msgid "There are no internal transactions for this transaction."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:68
#: lib/block_scout_web/templates/transaction_log/index.html.eex:145
msgid "There are no logs for this transaction."
msgstr ""
@ -861,7 +865,7 @@ msgid "There are no transfers for this Token."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:23
#: lib/block_scout_web/templates/transaction/overview.html.eex:24
msgid "This transaction is pending confirmation."
msgstr ""
@ -899,7 +903,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:129
#: lib/block_scout_web/views/transaction_view.ex:139
msgid "Token Transfer"
msgstr ""
@ -911,7 +915,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:36
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:10
#: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:179
#: lib/block_scout_web/views/transaction_view.ex:189
msgid "Token Transfers"
msgstr ""
@ -933,7 +937,7 @@ msgid "Top Accounts - %{subnetwork} Explorer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:26
#: lib/block_scout_web/templates/transaction_log/index.html.eex:88
msgid "Topics"
msgstr ""
@ -953,7 +957,7 @@ msgid "Total transactions"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:132
#: lib/block_scout_web/views/transaction_view.ex:142
msgid "Transaction"
msgstr ""
@ -969,7 +973,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:14
#: lib/block_scout_web/templates/transaction/overview.html.eex:15
#: lib/block_scout_web/templates/transaction/overview.html.eex:16
msgid "Transaction Details"
msgstr ""
@ -1022,7 +1026,7 @@ msgid "Unique Token"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:200
#: lib/block_scout_web/templates/transaction/overview.html.eex:128
msgid "Used"
msgstr ""
@ -1043,7 +1047,7 @@ msgid "Validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:182
#: lib/block_scout_web/templates/transaction/overview.html.eex:110
msgid "Value"
msgstr ""
@ -1227,22 +1231,15 @@ msgid "This API is provided for developers transitioning their applications from
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:153
#: lib/block_scout_web/templates/transaction/overview.html.eex:80
msgid "Raw Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:156
#: lib/block_scout_web/templates/transaction/overview.html.eex:83
msgid "Show Raw Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:130
#: lib/block_scout_web/templates/transaction/overview.html.eex:140
#: lib/block_scout_web/templates/transaction/overview.html.eex:164
msgid "copy"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:64
msgid "Error trying to fetch transactions."
@ -1322,3 +1319,88 @@ msgstr ""
#: lib/block_scout_web/views/internal_transaction_view.ex:24
msgid "Static Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:26
msgid "Decoded"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:59
msgid "Indexed?"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:129
msgid "Show Raw Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:91
msgid "Show Raw Topics"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:32
#: lib/block_scout_web/templates/transaction_log/index.html.eex:58
msgid "Type"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19
msgid "Method Id"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:8
#: lib/block_scout_web/templates/transaction_log/index.html.eex:31
msgid "To see decoded input data, the contract must be verified."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:17
msgid "Transaction Info"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:28
msgid "Transaction Inputs"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:11
#: lib/block_scout_web/templates/transaction_log/index.html.eex:34
msgid "Verify the contract "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:11
#: lib/block_scout_web/templates/transaction_log/index.html.eex:34
msgid "here"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:65
msgid "Failed to decode input data."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:53
msgid "Error rendering value"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:42
#: lib/block_scout_web/templates/transaction_log/index.html.eex:69
msgid "Copy Value"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:41
msgid "Failed to decode log data."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:54
msgid "Log Data"
msgstr ""

@ -56,7 +56,7 @@ msgid "%{subnetwork} Explorer - BlockScout"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:58
#: lib/block_scout_web/views/transaction_view.ex:64
msgid "(Awaiting internal transactions for status)"
msgstr ""
@ -151,7 +151,7 @@ msgid "Block %{block_number} - %{subnetwork} Explorer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:59
#: lib/block_scout_web/templates/transaction/overview.html.eex:60
msgid "Block Confirmations"
msgstr ""
@ -166,7 +166,7 @@ msgid "Block Mined, awaiting import..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:45
#: lib/block_scout_web/templates/transaction/overview.html.eex:46
msgid "Block Number"
msgstr ""
@ -218,6 +218,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/overview.html.eex:82
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:84
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:92
#: lib/block_scout_web/templates/transaction_log/index.html.eex:94
#: lib/block_scout_web/templates/transaction_log/index.html.eex:133
msgid "Close"
msgstr ""
@ -283,12 +285,12 @@ msgid "Contract Address Pending"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:131
#: lib/block_scout_web/views/transaction_view.ex:141
msgid "Contract Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:130
#: lib/block_scout_web/views/transaction_view.ex:140
msgid "Contract Creation"
msgstr ""
@ -329,13 +331,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:9
#: lib/block_scout_web/templates/transaction/overview.html.eex:10
#: lib/block_scout_web/templates/transaction/overview.html.eex:11
msgid "Copy Transaction Hash"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:9
#: lib/block_scout_web/templates/transaction/overview.html.eex:10
#: lib/block_scout_web/templates/transaction/overview.html.eex:11
msgid "Copy Txn Hash"
msgstr ""
@ -355,7 +357,9 @@ msgid "Curl"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:54
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:33
#: lib/block_scout_web/templates/transaction_log/index.html.eex:60
#: lib/block_scout_web/templates/transaction_log/index.html.eex:124
msgid "Data"
msgstr ""
@ -392,12 +396,12 @@ msgid "Error trying to fetch balances."
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:62
#: lib/block_scout_web/views/transaction_view.ex:68
msgid "Error: %{reason}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:60
#: lib/block_scout_web/views/transaction_view.ex:66
msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
@ -405,7 +409,7 @@ msgstr ""
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:16
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:19
#: lib/block_scout_web/templates/transaction/_tile.html.eex:26
#: lib/block_scout_web/templates/transaction/overview.html.eex:182
#: lib/block_scout_web/templates/transaction/overview.html.eex:110
#: lib/block_scout_web/views/wei_helpers.ex:72
msgid "Ether"
msgstr "POA"
@ -444,7 +448,7 @@ msgid "GET"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:195
#: lib/block_scout_web/templates/transaction/overview.html.eex:123
msgid "Gas"
msgstr ""
@ -488,9 +492,7 @@ msgid "Indexing Tokens"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:81
#: lib/block_scout_web/templates/transaction/overview.html.eex:88
#: lib/block_scout_web/templates/transaction/overview.html.eex:95
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:3
msgid "Input"
msgstr ""
@ -509,7 +511,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:43
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:10
#: lib/block_scout_web/views/address_view.ex:213
#: lib/block_scout_web/views/transaction_view.ex:180
#: lib/block_scout_web/views/transaction_view.ex:190
msgid "Internal Transactions"
msgstr ""
@ -527,7 +529,7 @@ msgid "Less than"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:207
#: lib/block_scout_web/templates/transaction/overview.html.eex:135
msgid "Limit"
msgstr ""
@ -535,7 +537,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:21
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:48
#: lib/block_scout_web/templates/transaction_log/index.html.eex:10
#: lib/block_scout_web/views/transaction_view.ex:181
#: lib/block_scout_web/views/transaction_view.ex:191
msgid "Logs"
msgstr ""
@ -547,7 +549,7 @@ msgid "Market Cap"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:49
#: lib/block_scout_web/views/transaction_view.ex:55
msgid "Max of"
msgstr ""
@ -588,6 +590,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:29
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:31
#: lib/block_scout_web/templates/transaction_log/index.html.eex:57
msgid "Name"
msgstr ""
@ -599,7 +603,7 @@ msgstr ""
#, elixir-format
#:
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:23
#: lib/block_scout_web/templates/transaction_log/index.html.eex:74
#: lib/block_scout_web/templates/transaction_log/index.html.eex:151
msgid "Newer"
msgstr ""
@ -622,7 +626,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:69
#: lib/block_scout_web/templates/transaction/overview.html.eex:66
#: lib/block_scout_web/templates/transaction/overview.html.eex:67
msgid "Nonce"
msgstr ""
@ -681,9 +685,9 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44
#: lib/block_scout_web/templates/transaction/overview.html.eex:54
#: lib/block_scout_web/views/transaction_view.ex:57
#: lib/block_scout_web/views/transaction_view.ex:87
#: lib/block_scout_web/templates/transaction/overview.html.eex:55
#: lib/block_scout_web/views/transaction_view.ex:63
#: lib/block_scout_web/views/transaction_view.ex:97
msgid "Pending"
msgstr ""
@ -788,14 +792,14 @@ msgid "Showing 250 addresses of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:59
#: lib/block_scout_web/views/transaction_view.ex:65
msgid "Success"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/_tile.html.eex:29
#: lib/block_scout_web/templates/transaction/overview.html.eex:71
#: lib/block_scout_web/templates/transaction/overview.html.eex:72
msgid "TX Fee"
msgstr ""
@ -821,7 +825,7 @@ msgid "There are no internal transactions for this transaction."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:68
#: lib/block_scout_web/templates/transaction_log/index.html.eex:145
msgid "There are no logs for this transaction."
msgstr ""
@ -861,7 +865,7 @@ msgid "There are no transfers for this Token."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:23
#: lib/block_scout_web/templates/transaction/overview.html.eex:24
msgid "This transaction is pending confirmation."
msgstr ""
@ -899,7 +903,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:129
#: lib/block_scout_web/views/transaction_view.ex:139
msgid "Token Transfer"
msgstr ""
@ -911,7 +915,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:36
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:10
#: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:179
#: lib/block_scout_web/views/transaction_view.ex:189
msgid "Token Transfers"
msgstr ""
@ -933,7 +937,7 @@ msgid "Top Accounts - %{subnetwork} Explorer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:26
#: lib/block_scout_web/templates/transaction_log/index.html.eex:88
msgid "Topics"
msgstr ""
@ -953,7 +957,7 @@ msgid "Total transactions"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:132
#: lib/block_scout_web/views/transaction_view.ex:142
msgid "Transaction"
msgstr ""
@ -969,7 +973,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:14
#: lib/block_scout_web/templates/transaction/overview.html.eex:15
#: lib/block_scout_web/templates/transaction/overview.html.eex:16
msgid "Transaction Details"
msgstr ""
@ -1022,7 +1026,7 @@ msgid "Unique Token"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:200
#: lib/block_scout_web/templates/transaction/overview.html.eex:128
msgid "Used"
msgstr ""
@ -1043,7 +1047,7 @@ msgid "Validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:182
#: lib/block_scout_web/templates/transaction/overview.html.eex:110
msgid "Value"
msgstr ""
@ -1170,7 +1174,7 @@ msgstr ""
msgid "Reward"
msgstr ""
#, elixir-format, fuzzy
#, elixir-format
#: lib/block_scout_web/views/internal_transaction_view.ex:26
msgid "Self-Destruct"
msgstr ""
@ -1227,22 +1231,15 @@ msgid "This API is provided for developers transitioning their applications from
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:153
#: lib/block_scout_web/templates/transaction/overview.html.eex:80
msgid "Raw Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:156
#: lib/block_scout_web/templates/transaction/overview.html.eex:83
msgid "Show Raw Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:130
#: lib/block_scout_web/templates/transaction/overview.html.eex:140
#: lib/block_scout_web/templates/transaction/overview.html.eex:164
msgid "copy"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:64
msgid "Error trying to fetch transactions."
@ -1322,3 +1319,88 @@ msgstr ""
#: lib/block_scout_web/views/internal_transaction_view.ex:24
msgid "Static Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:26
msgid "Decoded"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:59
msgid "Indexed?"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:129
msgid "Show Raw Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:91
msgid "Show Raw Topics"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:32
#: lib/block_scout_web/templates/transaction_log/index.html.eex:58
msgid "Type"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19
msgid "Method Id"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:8
#: lib/block_scout_web/templates/transaction_log/index.html.eex:31
msgid "To see decoded input data, the contract must be verified."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:17
msgid "Transaction Info"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:28
msgid "Transaction Inputs"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:11
#: lib/block_scout_web/templates/transaction_log/index.html.eex:34
msgid "Verify the contract "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:11
#: lib/block_scout_web/templates/transaction_log/index.html.eex:34
msgid "here"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:65
msgid "Failed to decode input data."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:53
msgid "Error rendering value"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:42
#: lib/block_scout_web/templates/transaction_log/index.html.eex:69
msgid "Copy Value"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:41
msgid "Failed to decode log data."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:54
msgid "Log Data"
msgstr ""

@ -0,0 +1,123 @@
defmodule BlockScoutWeb.ABIEncodedValueViewTest do
use BlockScoutWeb.ConnCase, async: true
alias BlockScoutWeb.ABIEncodedValueView
defp value_html(type, value) do
type
|> ABIEncodedValueView.value_html(value)
|> case do
:error ->
raise "failed to generate html"
other ->
other
end
|> Phoenix.HTML.Safe.to_iodata()
|> IO.iodata_to_binary()
end
defp copy_text(type, value) do
type
|> ABIEncodedValueView.copy_text(value)
|> case do
:error ->
raise "failed to generate copy text"
other ->
other
end
|> 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[4]", [address_bytes, address_bytes, address_bytes, address_bytes]) == expected
end
test "it renders :dynamic values as bytes" do
assert value_html("uint", {:dynamic, <<1>>}) == "0x01"
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[4]", [1, 2, 3, 4]) == "[1, 2, 3, 4]"
end
test "it copies bytes as their hex representation" do
hex = "0xffffff"
bytes = hex |> String.trim_leading("0x") |> Base.decode16!(case: :lower)
assert copy_text("bytes", bytes) == hex
end
test "it copies :dynamic values as bytes" do
assert copy_text("uint", {:dynamic, <<1>>}) == "0x01"
end
end
end

@ -79,7 +79,7 @@ defmodule EthereumJsonrpc.MixProject do
# Convert unix timestamps in JSONRPC to DateTimes
{:timex, "~> 3.4"},
# Encode/decode function names and arguments
{:ex_abi, "~> 0.1.17"},
{:ex_abi, "~> 0.1.18"},
# `:verify_fun` for `Socket.Web.connect`
{:ssl_verify_fun, "~> 1.1"},
# `EthereumJSONRPC.WebSocket`

@ -3,6 +3,9 @@ defmodule Explorer.Chain.Log do
use Explorer.Schema
require Logger
alias ABI.Event
alias Explorer.Chain.{Address, Data, Hash, Transaction}
@required_attrs ~w(address_hash data index transaction_hash)a
@ -98,4 +101,64 @@ defmodule Explorer.Chain.Log do
|> cast(attrs, @optional_attrs)
|> validate_required(@required_attrs)
end
@doc """
Decode transaction log data.
"""
def decode(_log, %Transaction{to_address: nil}), do: {:error, :no_to_address}
def decode(log, transaction = %Transaction{to_address: %{smart_contract: %{abi: abi}}}) when not is_nil(abi) do
with {:ok, selector, mapping} <- find_and_decode(abi, log, transaction),
identifier <- Base.encode16(selector.method_id, case: :lower),
text <- function_call(selector.function, mapping),
do: {:ok, identifier, text, mapping}
end
def decode(_log, _transaction), do: {:error, :contract_not_verified}
defp find_and_decode(abi, log, transaction) do
with {selector, mapping} <-
abi
|> ABI.parse_specification(include_events?: true)
|> Event.find_and_decode(
decode16!(log.first_topic),
decode16!(log.second_topic),
decode16!(log.third_topic),
decode16!(log.fourth_topic),
log.data.bytes
) do
{:ok, selector, mapping}
end
rescue
_ ->
Logger.warn(fn -> ["Could not decode input data for log from transaction: ", Hash.to_iodata(transaction.hash)] end)
{:error, :could_not_decode}
end
defp function_call(name, mapping) do
text =
mapping
|> Stream.map(fn {name, type, indexed?, _value} ->
indexed_keyword =
if indexed? do
["indexed "]
else
[]
end
[type, " ", indexed_keyword, name]
end)
|> Enum.intersperse(", ")
IO.iodata_to_binary([name, "(", text, ")"])
end
def decode16!(nil), do: nil
def decode16!(value) do
value
|> String.trim_leading("0x")
|> Base.decode16!(case: :lower)
end
end

@ -37,4 +37,68 @@ defmodule Explorer.Chain.LogTest do
assert changeset.changes.first_topic === "ham"
end
end
describe "decode/2" do
test "that a contract call transaction that has no verified contract returns a commensurate error" do
transaction =
:transaction
|> insert(to_address: insert(:contract_address))
|> Repo.preload(to_address: :smart_contract)
log = insert(:log, transaction: transaction)
assert Log.decode(log, transaction) == {:error, :contract_not_verified}
end
test "that a contract call transaction that has a verified contract returns the decoded input data" do
smart_contract =
insert(:smart_contract,
abi: [
%{
"anonymous" => false,
"inputs" => [
%{"indexed" => true, "name" => "_from_human", "type" => "string"},
%{"indexed" => false, "name" => "_number", "type" => "uint256"},
%{"indexed" => true, "name" => "_belly", "type" => "bool"}
],
"name" => "WantsPets",
"type" => "event"
}
]
)
topic1 = "0x" <> Base.encode16(:keccakf1600.hash(:sha3_256, "WantsPets(string,uint256,bool)"), case: :lower)
topic2 = "0x" <> Base.encode16(:keccakf1600.hash(:sha3_256, "bob"), case: :lower)
topic3 = "0x0000000000000000000000000000000000000000000000000000000000000001"
data = "0x0000000000000000000000000000000000000000000000000000000000000000"
to_address = insert(:address, smart_contract: smart_contract)
transaction =
:transaction_to_verified_contract
|> insert(to_address: to_address)
|> Repo.preload(to_address: :smart_contract)
log =
insert(:log,
transaction: transaction,
first_topic: topic1,
second_topic: topic2,
third_topic: topic3,
fourth_topic: nil,
data: data
)
assert Log.decode(log, transaction) ==
{:ok, "eb9b3c4c", "WantsPets(string indexed _from_human, uint256 _number, bool indexed _belly)",
[
{"_from_human", "string", true,
{:dynamic,
<<56, 228, 122, 123, 113, 157, 206, 99, 102, 42, 234, 244, 52, 64, 50, 111, 85, 27, 138, 126, 225,
152, 206, 227, 92, 181, 213, 23, 242, 210, 150, 162>>}},
{"_number", "uint256", false, 0},
{"_belly", "bool", true, true}
]}
end
end
end

@ -28,7 +28,7 @@
"earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "2.2.11", "4bb8f11718b72ba97a2696f65d247a379e739a0ecabf6a13ad1face79844791c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"elixir_make": {:hex, :elixir_make, "0.4.2", "332c649d08c18bc1ecc73b1befc68c647136de4f340b548844efc796405743bf", [:mix], [], "hexpm"},
"ex_abi": {:hex, :ex_abi, "0.1.17", "11822f88b3ed70773c64858a49321b3c51ed913128a3f9fc7a05fa7ceb19d8fa", [:mix], [{:exth_crypto, "~> 0.1.4", [hex: :exth_crypto, repo: "hexpm", optional: false]}], "hexpm"},
"ex_abi": {:hex, :ex_abi, "0.1.18", "19db9bffdd201edbdff97d7dd5849291218b17beda045c1b76bff5248964f37d", [:mix], [{:exth_crypto, "~> 0.1.4", [hex: :exth_crypto, repo: "hexpm", optional: false]}], "hexpm"},
"ex_cldr": {:hex, :ex_cldr, "1.3.2", "8f4a00c99d1c537b8e8db7e7903f4bd78d82a7289502d080f70365392b13921b", [:mix], [{:abnf2, "~> 0.1", [hex: :abnf2, optional: false]}, {:decimal, "~> 1.4", [hex: :decimal, optional: false]}, {:gettext, "~> 0.13", [hex: :gettext, optional: true]}, {:poison, "~> 2.1 or ~> 3.0", [hex: :poison, optional: true]}]},
"ex_cldr_numbers": {:hex, :ex_cldr_numbers, "1.2.0", "ef27299922da913ffad1ed296cacf28b6452fc1243b77301dc17c03276c6ee34", [:mix], [{:decimal, "~> 1.4", [hex: :decimal, optional: false]}, {:ex_cldr, "~> 1.3", [hex: :ex_cldr, optional: false]}, {:poison, "~> 2.1 or ~> 3.1", [hex: :poison, optional: false]}]},
"ex_cldr_units": {:hex, :ex_cldr_units, "1.1.1", "b3c7256709bdeb3740a5f64ce2bce659eb9cf4cc1afb4cf94aba033b4a18bc5f", [:mix], [{:ex_cldr, "~> 1.0", [hex: :ex_cldr, optional: false]}, {:ex_cldr_numbers, "~> 1.0", [hex: :ex_cldr_numbers, optional: false]}]},

Loading…
Cancel
Save