diff --git a/apps/block_scout_web/assets/css/components/_transaction-input.scss b/apps/block_scout_web/assets/css/components/_transaction-input.scss index 63c3a847b8..12ca615be8 100644 --- a/apps/block_scout_web/assets/css/components/_transaction-input.scss +++ b/apps/block_scout_web/assets/css/components/_transaction-input.scss @@ -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; } diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex new file mode 100644 index 0000000000..87cf28afd6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex @@ -0,0 +1,69 @@ +
+
+

<%= gettext "Input" %>

+ + <%= case @decoded_input_data do %> + <% {:error, :contract_not_verified} -> %> +
+ <%= gettext "To see decoded input data, the contract must be verified." %> + <%= case @transaction do %> + <% %{to_address: %{hash: hash}} -> %> + <%= gettext "Verify the contract " %><%= gettext "here" %> + <% _ -> %> + <%= nil %> + <% end %> +
+ <% {:ok, method_id, text, mapping} -> %> + " class="table thead-light table-bordered table-responsive transaction-info-table"> + + + + + + + + +
<%= gettext "Method Id" %>0x<%= method_id %>
Call<%= text %>
+ + " class="table thead-light table-bordered table-responsive"> + + + + + + + <%= for {name, type, value} <- mapping do %> + + + + + + + <% end %> +
<%= gettext "Name" %><%= gettext "Type" %><%= gettext "Data" %>
+ <%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %> + <% :error -> %> + <%= nil %> + <% copy_text -> %> + + <% end %> + <%= name %><%= type %> + <%= case BlockScoutWeb.ABIEncodedValueView.value_html(type, value) do %> + <% :error -> %> +
+ <%= gettext "Error rendering value" %> +
+ <% value -> %> +
<%= value %>
+ <% end %> + +
+ <% _ -> %> +
+ <%= gettext "Failed to decode input data." %> +
+ <% end %> +
+
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index 21440d0c8b..77663a0cec 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -1,4 +1,5 @@ <% block = @transaction.block %> +<% decoded_input_data = decoded_input_data(@transaction) %>
@@ -74,84 +75,10 @@ - -
- <%= case decoded_input_data(@transaction) do %> - <% {:error, :contract_not_verified} -> %> -
<%= gettext "Input" %>
-
-
- To see decoded input data, the contract must be verified. -
-
- <% {:error, :could_not_decode} -> %> -
<%= gettext "Input" %>
-
-
- Failed to decode input data. Some dynamic types are not currently supported. -
-
- <% {:ok, method_id, text, mapping} -> %> -
<%= gettext "Input" %>
-
- - - - - - - - - -
Method Id0x<%= method_id %>
Call<%= text %>
- - - - - - - - - <%= for {{name, type, value}, index} <- Enum.with_index(mapping) do %> - - - - - <%= case type do %> - <% "address" -> %> - <% address = "0x" <> Base.encode16(value, case: :lower) %> - - - - <% _ -> %> - - - <% end %> - - <% end %> -
#NameTypeData
<%= index %><%= name %><%= type %> - - - - -
<%= value %> -
- -
-
- <% _ -> %> - <%= nil %> - <% end %> - - <%= unless @transaction.input.bytes in [<<>>, nil] do %> -
<%= gettext "Raw Input" %>
-
+ <%= unless value_transfer?(@transaction) do %> +
+
<%= gettext "Raw Input" %>
+
@@ -159,18 +86,19 @@ + +
-
                     <%= @transaction.input %>
                   
- - <% end %> + + <% end %>
@@ -212,4 +140,13 @@ + + <%= unless should_decode?(@transaction) do %> +
+
+ <%= render BlockScoutWeb.TransactionView, "_decoded_input.html", Map.put(assigns, :decoded_input_data, decoded_input_data) %> +
+
+ <% end %> +
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex index 4eb501a5fd..e1862c1447 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex @@ -23,39 +23,116 @@ ) %> -
<%= gettext "Topics" %>
+
<%= gettext "Decoded" %>
- <%= unless is_nil(log.first_topic) do %> -
- [0] - <%= log.first_topic %> -
- <% end %> - <%= unless is_nil(log.second_topic) do %> -
- [1] - <%= log.second_topic %> -
- <% end %> - <%= unless is_nil(log.third_topic) do %> -
- [2] - <%= log.third_topic %> -
- <% end %> - <%= unless is_nil(log.fourth_topic) do %> -
- [3] - <%= log.fourth_topic %> -
+ <%= case decode(log, @transaction) do %> + <% {:error, :contract_not_verified} -> %> +
+ <%= gettext "To see decoded input data, the contract must be verified." %> + <%= case @transaction do %> + <% %{to_address: %{hash: hash}} -> %> + <%= gettext "Verify the contract " %><%= gettext "here" %> + <% _ -> %> + <%= nil %> + <% end %> +
+ <% {:error, :could_not_decode} -> %> +
+ <%= gettext "Failed to decode log data." %> +
+ <% {:ok, method_id, text, mapping} -> %> + + + + + + + + + +
Method Id0x<%= method_id %>
Call<%= text %>
+ " class="table thead-light table-bordered table-responsive"> + + + + + + + + <%= for {name, type, indexed?, value} <- mapping do %> + + + + + + + + <% end %> +
<%= gettext "Name" %><%= gettext "Type" %><%= gettext "Indexed?" %><%= gettext "Data" %>
+ <%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %> + <% :error -> %> + <%= nil %> + <% copy_text -> %> + + <% end %> + <%= name %><%= type %><%= indexed? %> +
<%= BlockScoutWeb.ABIEncodedValueView.value_html(type, value) %>
+
+ <% _ -> %> + <%= nil %> <% end %> + +
+
<%= gettext "Topics" %>
+
+
+ +
+
+ + <%= unless is_nil(log.first_topic) do %> +
+ [0] + <%= log.first_topic %> +
+ <% end %> + <%= unless is_nil(log.second_topic) do %> +
+ [1] + <%= log.second_topic %> +
+ <% end %> + <%= unless is_nil(log.third_topic) do %> +
+ [2] + <%= log.third_topic %> +
+ <% end %> + <%= unless is_nil(log.fourth_topic) do %> +
+ [3] + <%= log.fourth_topic %> +
+ <% end %> +
<%= gettext "Data" %>
<%= unless is_nil(log.data) do %> -
+
+ +
+ +
+ <%= log.data %>
<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex new file mode 100644 index 0000000000..31c4aa1140 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex @@ -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 `
` 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|<%= address %>|
+  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
diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_log_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_log_view.ex
index 96cf62fe9a..50d406433c 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/transaction_log_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_log_view.ex
@@ -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
diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
index 6f630668b4..3fc4ee279e 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
@@ -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
diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot
index c634ffcb5b..9ae3578b23 100644
--- a/apps/block_scout_web/priv/gettext/default.pot
+++ b/apps/block_scout_web/priv/gettext/default.pot
@@ -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 ""
 
@@ -406,7 +410,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 ""
@@ -445,7 +449,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 ""
 
@@ -489,9 +493,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 ""
 
@@ -510,7 +512,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 ""
 
@@ -528,7 +530,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 ""
 
@@ -536,7 +538,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 ""
 
@@ -548,7 +550,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 ""
 
@@ -589,6 +591,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 ""
 
@@ -600,7 +604,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 ""
 
@@ -623,7 +627,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 ""
 
@@ -682,9 +686,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 ""
 
@@ -789,14 +793,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 ""
 
@@ -822,7 +826,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 ""
 
@@ -862,7 +866,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 ""
 
@@ -900,7 +904,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 ""
 
@@ -912,7 +916,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 ""
 
@@ -934,7 +938,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 ""
 
@@ -954,7 +958,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 ""
 
@@ -970,7 +974,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 ""
 
@@ -1023,7 +1027,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 ""
 
@@ -1044,7 +1048,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 ""
 
@@ -1228,22 +1232,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."
@@ -1323,3 +1320,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 ""
diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
index b8f82addb3..5e81eec55b 100644
--- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
+++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
@@ -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 ""
 
@@ -406,7 +410,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"
@@ -445,7 +449,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 ""
 
@@ -489,9 +493,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 ""
 
@@ -510,7 +512,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 ""
 
@@ -528,7 +530,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 ""
 
@@ -536,7 +538,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 ""
 
@@ -548,7 +550,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 ""
 
@@ -589,6 +591,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 ""
 
@@ -600,7 +604,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 ""
 
@@ -623,7 +627,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 ""
 
@@ -682,9 +686,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 ""
 
@@ -789,14 +793,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 ""
 
@@ -822,7 +826,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 ""
 
@@ -862,7 +866,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 ""
 
@@ -900,7 +904,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 ""
 
@@ -912,7 +916,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 ""
 
@@ -934,7 +938,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 ""
 
@@ -954,7 +958,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 ""
 
@@ -970,7 +974,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 ""
 
@@ -1023,7 +1027,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 ""
 
@@ -1044,7 +1048,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 ""
 
@@ -1171,7 +1175,7 @@ msgstr ""
 msgid "Reward"
 msgstr ""
 
-#, elixir-format, fuzzy
+#, elixir-format
 #: lib/block_scout_web/views/internal_transaction_view.ex:26
 msgid "Self-Destruct"
 msgstr ""
@@ -1228,22 +1232,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."
@@ -1323,3 +1320,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 ""
diff --git a/apps/block_scout_web/test/block_scout_web/views/abi_encoded_value_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/abi_encoded_value_view_test.exs
new file mode 100644
index 0000000000..c095bb4de4
--- /dev/null
+++ b/apps/block_scout_web/test/block_scout_web/views/abi_encoded_value_view_test.exs
@@ -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(#{address})
+
+      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(#{address})
+
+      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
diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs
index 16608e0942..02c8ccfe53 100644
--- a/apps/ethereum_jsonrpc/mix.exs
+++ b/apps/ethereum_jsonrpc/mix.exs
@@ -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`
diff --git a/apps/explorer/lib/explorer/chain/log.ex b/apps/explorer/lib/explorer/chain/log.ex
index 9dcb31405a..b2dc8ca639 100644
--- a/apps/explorer/lib/explorer/chain/log.ex
+++ b/apps/explorer/lib/explorer/chain/log.ex
@@ -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
diff --git a/apps/explorer/test/explorer/chain/log_test.exs b/apps/explorer/test/explorer/chain/log_test.exs
index 38f264705a..28be44a1af 100644
--- a/apps/explorer/test/explorer/chain/log_test.exs
+++ b/apps/explorer/test/explorer/chain/log_test.exs
@@ -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
diff --git a/mix.lock b/mix.lock
index a1d0a1c603..3a6bf7bd26 100644
--- a/mix.lock
+++ b/mix.lock
@@ -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]}]},