Merge pull request #1059 from poanetwork/decode-transaction-input-data

feat: decode transaction input on overview
pull/1057/head
Luke Imhoff 6 years ago committed by GitHub
commit d8ff3286a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      apps/block_scout_web/assets/css/app.scss
  2. 9
      apps/block_scout_web/assets/css/components/_transaction-input.scss
  3. 1
      apps/block_scout_web/assets/js/app.js
  4. 3
      apps/block_scout_web/assets/js/lib/clipboard_buttons.js
  5. 21
      apps/block_scout_web/assets/js/lib/swappable_item.js
  6. 1
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex
  7. 1
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex
  8. 88
      apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
  9. 4
      apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
  10. 47
      apps/block_scout_web/priv/gettext/default.pot
  11. 47
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  12. 2
      apps/ethereum_jsonrpc/mix.exs
  13. 52
      apps/explorer/lib/explorer/chain/transaction.ex
  14. 26
      apps/explorer/test/explorer/chain/transaction_test.exs
  15. 69
      apps/explorer/test/support/factory.ex
  16. 4
      mix.lock

@ -81,6 +81,7 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
@import "components/token_tile_view_more"; @import "components/token_tile_view_more";
@import "components/dropdown"; @import "components/dropdown";
@import "components/loading-spinner"; @import "components/loading-spinner";
@import "components/transaction-input";
:export { :export {
primary: $primary; primary: $primary;

@ -0,0 +1,9 @@
.raw-transaction-input{
display: none;
}
.transaction-input-text{
resize: vertical;
overflow: auto;
word-break: break-all;
}

@ -44,3 +44,4 @@ import './lib/token_balance_dropdown_search'
import './lib/token_transfers_toggle' import './lib/token_transfers_toggle'
import './lib/tooltip' import './lib/tooltip'
import './lib/try_api' import './lib/try_api'
import './lib/swappable_item'

@ -5,11 +5,14 @@ const clipboard = new ClipboardJS('[data-clipboard-text]')
clipboard.on('success', ({trigger}) => { clipboard.on('success', ({trigger}) => {
const copyButton = $(trigger) const copyButton = $(trigger)
copyButton.tooltip('dispose')
copyButton.tooltip({ copyButton.tooltip({
title: 'Copied!', title: 'Copied!',
trigger: 'click', trigger: 'click',
placement: 'top' placement: 'top'
}).tooltip('show') }).tooltip('show')
setTimeout(() => { setTimeout(() => {
copyButton.tooltip('dispose') copyButton.tooltip('dispose')
}, 1000) }, 1000)

@ -0,0 +1,21 @@
import $ from 'jquery'
const swapItems = (element, event) => {
const $element = $(element)
const item = $element.parent().closest('[swappable-item]')
const next = item.nextAll('[swappable-item]:first')
item.hide()
if (next.length) {
next.show()
} else {
item.parent().find('[swappable-item]:first').show()
}
return false
}
$('[swappable-item] [swapper]').on('click', function (event) {
swapItems(this, event)
})

@ -16,6 +16,7 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do
[created_contract_address: :names] => :optional, [created_contract_address: :names] => :optional,
[from_address: :names] => :optional, [from_address: :names] => :optional,
[to_address: :names] => :optional, [to_address: :names] => :optional,
[to_address: :smart_contract] => :optional,
:token_transfers => :optional :token_transfers => :optional
} }
) do ) do

@ -16,6 +16,7 @@ defmodule BlockScoutWeb.TransactionLogController do
[created_contract_address: :names] => :optional, [created_contract_address: :names] => :optional,
[from_address: :names] => :required, [from_address: :names] => :required,
[to_address: :names] => :optional, [to_address: :names] => :optional,
[to_address: :smart_contract] => :optional,
:token_transfers => :optional :token_transfers => :optional
} }
) do ) do

@ -76,14 +76,100 @@
<!-- Input --> <!-- Input -->
<dl class="row mb-0"> <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> <dt class="col-sm-3 text-muted"> <%= gettext "Input" %> </dt>
<dd class="col-sm-9"> <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">
<div swappable-item>
<button swapper class="button button-primary"><%= gettext "Show Raw Input"%></button>
</div>
<div swappable-item class="raw-transaction-input">
<button swapper type="button" class="close pr-2" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<div class="tile tile-muted"> <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"> <pre class="pre-scrollable pre-scrollable-shorty pre-wrap mb-0">
<code><%= @transaction.input %></code> <code><%= @transaction.input %></code>
</pre> </pre>
</div> </div>
</div>
</dd> </dd>
<% end %>
</dl> </dl>
</div> </div>
</div> </div>

@ -73,6 +73,10 @@ defmodule BlockScoutWeb.TransactionView do
Cldr.Number.to_string!(gas) Cldr.Number.to_string!(gas)
end end
def decoded_input_data(transaction) do
Transaction.decoded_input_data(transaction)
end
@doc """ @doc """
Converts a transaction's gas price to a displayable value. Converts a transaction's gas price to a displayable value.
""" """

@ -283,12 +283,12 @@ msgid "Contract Address Pending"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:127 #: lib/block_scout_web/views/transaction_view.ex:131
msgid "Contract Call" msgid "Contract Call"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:126 #: lib/block_scout_web/views/transaction_view.ex:130
msgid "Contract Creation" msgid "Contract Creation"
msgstr "" msgstr ""
@ -403,7 +403,7 @@ msgstr ""
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:16 #: 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/_pending_tile.html.eex:19
#: lib/block_scout_web/templates/transaction/_tile.html.eex:26 #: lib/block_scout_web/templates/transaction/_tile.html.eex:26
#: lib/block_scout_web/templates/transaction/overview.html.eex:96 #: lib/block_scout_web/templates/transaction/overview.html.eex:182
#: lib/block_scout_web/views/wei_helpers.ex:72 #: lib/block_scout_web/views/wei_helpers.ex:72
msgid "Ether" msgid "Ether"
msgstr "" msgstr ""
@ -442,7 +442,7 @@ msgid "GET"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:109 #: lib/block_scout_web/templates/transaction/overview.html.eex:195
msgid "Gas" msgid "Gas"
msgstr "" msgstr ""
@ -486,7 +486,9 @@ msgid "Indexing Tokens"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:79 #: 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
msgid "Input" msgid "Input"
msgstr "" msgstr ""
@ -505,7 +507,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:43 #: 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/templates/transaction_internal_transaction/index.html.eex:10
#: lib/block_scout_web/views/address_view.ex:213 #: lib/block_scout_web/views/address_view.ex:213
#: lib/block_scout_web/views/transaction_view.ex:176 #: lib/block_scout_web/views/transaction_view.ex:180
msgid "Internal Transactions" msgid "Internal Transactions"
msgstr "" msgstr ""
@ -523,7 +525,7 @@ msgid "Less than"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:121 #: lib/block_scout_web/templates/transaction/overview.html.eex:207
msgid "Limit" msgid "Limit"
msgstr "" msgstr ""
@ -531,7 +533,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:21 #: 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/_tabs.html.eex:48
#: lib/block_scout_web/templates/transaction_log/index.html.eex:10 #: lib/block_scout_web/templates/transaction_log/index.html.eex:10
#: lib/block_scout_web/views/transaction_view.ex:177 #: lib/block_scout_web/views/transaction_view.ex:181
msgid "Logs" msgid "Logs"
msgstr "" msgstr ""
@ -680,7 +682,7 @@ msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44 #: lib/block_scout_web/templates/layout/_topnav.html.eex:44
#: lib/block_scout_web/templates/transaction/overview.html.eex:54 #: 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:57
#: lib/block_scout_web/views/transaction_view.ex:83 #: lib/block_scout_web/views/transaction_view.ex:87
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
@ -896,7 +898,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:4 #: 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/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:125 #: lib/block_scout_web/views/transaction_view.ex:129
msgid "Token Transfer" msgid "Token Transfer"
msgstr "" msgstr ""
@ -908,7 +910,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:36 #: 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/templates/transaction_token_transfer/index.html.eex:10
#: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:175 #: lib/block_scout_web/views/transaction_view.ex:179
msgid "Token Transfers" msgid "Token Transfers"
msgstr "" msgstr ""
@ -950,7 +952,7 @@ msgid "Total transactions"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:128 #: lib/block_scout_web/views/transaction_view.ex:132
msgid "Transaction" msgid "Transaction"
msgstr "" msgstr ""
@ -1018,7 +1020,7 @@ msgid "Unique Token"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:114 #: lib/block_scout_web/templates/transaction/overview.html.eex:200
msgid "Used" msgid "Used"
msgstr "" msgstr ""
@ -1039,7 +1041,7 @@ msgid "Validations"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:96 #: lib/block_scout_web/templates/transaction/overview.html.eex:182
msgid "Value" msgid "Value"
msgstr "" msgstr ""
@ -1216,3 +1218,20 @@ msgstr ""
#: lib/block_scout_web/templates/api_docs/index.html.eex:7 #: lib/block_scout_web/templates/api_docs/index.html.eex:7
msgid "This API is provided for developers transitioning their applications from Etherscan to BlockScout. It supports GET and POST requests." msgid "This API is provided for developers transitioning their applications from Etherscan to BlockScout. It supports GET and POST requests."
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:153
msgid "Raw Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:156
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 ""

@ -283,12 +283,12 @@ msgid "Contract Address Pending"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:127 #: lib/block_scout_web/views/transaction_view.ex:131
msgid "Contract Call" msgid "Contract Call"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:126 #: lib/block_scout_web/views/transaction_view.ex:130
msgid "Contract Creation" msgid "Contract Creation"
msgstr "" msgstr ""
@ -403,7 +403,7 @@ msgstr ""
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:16 #: 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/_pending_tile.html.eex:19
#: lib/block_scout_web/templates/transaction/_tile.html.eex:26 #: lib/block_scout_web/templates/transaction/_tile.html.eex:26
#: lib/block_scout_web/templates/transaction/overview.html.eex:96 #: lib/block_scout_web/templates/transaction/overview.html.eex:182
#: lib/block_scout_web/views/wei_helpers.ex:72 #: lib/block_scout_web/views/wei_helpers.ex:72
msgid "Ether" msgid "Ether"
msgstr "POA" msgstr "POA"
@ -442,7 +442,7 @@ msgid "GET"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:109 #: lib/block_scout_web/templates/transaction/overview.html.eex:195
msgid "Gas" msgid "Gas"
msgstr "" msgstr ""
@ -486,7 +486,9 @@ msgid "Indexing Tokens"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:79 #: 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
msgid "Input" msgid "Input"
msgstr "" msgstr ""
@ -505,7 +507,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:43 #: 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/templates/transaction_internal_transaction/index.html.eex:10
#: lib/block_scout_web/views/address_view.ex:213 #: lib/block_scout_web/views/address_view.ex:213
#: lib/block_scout_web/views/transaction_view.ex:176 #: lib/block_scout_web/views/transaction_view.ex:180
msgid "Internal Transactions" msgid "Internal Transactions"
msgstr "" msgstr ""
@ -523,7 +525,7 @@ msgid "Less than"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:121 #: lib/block_scout_web/templates/transaction/overview.html.eex:207
msgid "Limit" msgid "Limit"
msgstr "" msgstr ""
@ -531,7 +533,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:21 #: 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/_tabs.html.eex:48
#: lib/block_scout_web/templates/transaction_log/index.html.eex:10 #: lib/block_scout_web/templates/transaction_log/index.html.eex:10
#: lib/block_scout_web/views/transaction_view.ex:177 #: lib/block_scout_web/views/transaction_view.ex:181
msgid "Logs" msgid "Logs"
msgstr "" msgstr ""
@ -680,7 +682,7 @@ msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44 #: lib/block_scout_web/templates/layout/_topnav.html.eex:44
#: lib/block_scout_web/templates/transaction/overview.html.eex:54 #: 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:57
#: lib/block_scout_web/views/transaction_view.ex:83 #: lib/block_scout_web/views/transaction_view.ex:87
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
@ -896,7 +898,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:4 #: 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/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:125 #: lib/block_scout_web/views/transaction_view.ex:129
msgid "Token Transfer" msgid "Token Transfer"
msgstr "" msgstr ""
@ -908,7 +910,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:36 #: 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/templates/transaction_token_transfer/index.html.eex:10
#: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:175 #: lib/block_scout_web/views/transaction_view.ex:179
msgid "Token Transfers" msgid "Token Transfers"
msgstr "" msgstr ""
@ -950,7 +952,7 @@ msgid "Total transactions"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:128 #: lib/block_scout_web/views/transaction_view.ex:132
msgid "Transaction" msgid "Transaction"
msgstr "" msgstr ""
@ -1018,7 +1020,7 @@ msgid "Unique Token"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:114 #: lib/block_scout_web/templates/transaction/overview.html.eex:200
msgid "Used" msgid "Used"
msgstr "" msgstr ""
@ -1039,7 +1041,7 @@ msgid "Validations"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:96 #: lib/block_scout_web/templates/transaction/overview.html.eex:182
msgid "Value" msgid "Value"
msgstr "" msgstr ""
@ -1216,3 +1218,20 @@ msgstr ""
#: lib/block_scout_web/templates/api_docs/index.html.eex:7 #: lib/block_scout_web/templates/api_docs/index.html.eex:7
msgid "This API is provided for developers transitioning their applications from Etherscan to BlockScout. It supports GET and POST requests." msgid "This API is provided for developers transitioning their applications from Etherscan to BlockScout. It supports GET and POST requests."
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:153
msgid "Raw Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:156
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 ""

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

@ -3,8 +3,12 @@ defmodule Explorer.Chain.Transaction do
use Explorer.Schema use Explorer.Schema
require Logger
import Ecto.Query, only: [dynamic: 2, from: 2, preload: 3, subquery: 1, where: 3] import Ecto.Query, only: [dynamic: 2, from: 2, preload: 3, subquery: 1, where: 3]
alias ABI.FunctionSelector
alias Ecto.Changeset alias Ecto.Changeset
alias Explorer.Chain.{ alias Explorer.Chain.{
@ -415,6 +419,54 @@ defmodule Explorer.Chain.Transaction do
preload(query, [tt], token_transfers: ^token_transfers_query) preload(query, [tt], token_transfers: ^token_transfers_query)
end end
# Because there is no contract association, we know the contract was not verified
def decoded_input_data(%__MODULE__{to_address: nil}), do: {:error, :no_to_address}
def decoded_input_data(%__MODULE__{input: %{bytes: bytes}}) when bytes in [nil, <<>>], do: {:error, :no_input_data}
def decoded_input_data(%__MODULE__{to_address: %{contract_code: nil}}), do: {:error, :not_a_contract_call}
def decoded_input_data(%__MODULE__{to_address: %{smart_contract: nil}}), do: {:error, :contract_not_verified}
def decoded_input_data(%__MODULE__{input: %{bytes: data}, to_address: %{smart_contract: %{abi: abi}}, hash: hash}) do
with {:ok, {selector, values}} <- find_and_decode(abi, data, hash),
{:ok, mapping} <- selector_mapping(selector, values, hash),
identifier <- Base.encode16(selector.method_id, case: :lower),
text <- function_call(selector.function, mapping),
do: {:ok, identifier, text, mapping}
end
defp function_call(name, mapping) do
text =
mapping
|> Stream.map(fn {name, type, _} -> [type, " ", name] end)
|> Enum.intersperse(", ")
IO.iodata_to_binary([name, "(", text, ")"])
end
defp find_and_decode(abi, data, hash) do
result =
abi
|> ABI.parse_specification()
|> ABI.find_and_decode(data)
{:ok, result}
rescue
_ ->
Logger.warn(fn -> ["Could not decode input data for transaction: ", Hash.to_iodata(hash)] end)
{:error, :could_not_decode}
end
defp selector_mapping(selector, values, hash) do
types = Enum.map(selector.types, &FunctionSelector.encode_type/1)
mapping = Enum.zip([selector.input_names, types, values])
{:ok, mapping}
rescue
_ ->
Logger.warn(fn -> ["Could not decode input data for transaction: ", Hash.to_iodata(hash)] end)
{:error, :could_not_decode}
end
@doc """ @doc """
Adds to the given transaction's query a `where` with one of the conditions that the matched Adds to the given transaction's query a `where` with one of the conditions that the matched
function returns. function returns.

@ -213,4 +213,30 @@ defmodule Explorer.Chain.TransactionTest do
assert last_nonce == nil assert last_nonce == nil
end end
end end
describe "decoded_input_data/1" do
test "that a tranasction that is not a contract call returns a commensurate error" do
transaction = insert(:transaction)
assert Transaction.decoded_input_data(transaction) == {:error, :not_a_contract_call}
end
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)
assert Transaction.decoded_input_data(transaction) == {:error, :contract_not_verified}
end
test "that a contract call transaction that has a verified contract returns the decoded input data" do
transaction =
:transaction_to_verified_contract
|> insert()
|> Repo.preload(to_address: :smart_contract)
assert Transaction.decoded_input_data(transaction) == {:ok, "60fe47b1", "set(uint256 x)", [{"x", "uint256", 50}]}
end
end
end end

@ -92,6 +92,26 @@ defmodule Explorer.Factory do
} }
} }
""", """,
abi: [
%{
"constant" => false,
"inputs" => [%{"name" => "x", "type" => "uint256"}],
"name" => "set",
"outputs" => [],
"payable" => false,
"stateMutability" => "nonpayable",
"type" => "function"
},
%{
"constant" => true,
"inputs" => [],
"name" => "get",
"outputs" => [%{"name" => "", "type" => "uint256"}],
"payable" => false,
"stateMutability" => "view",
"type" => "function"
}
],
version: "v0.4.24+commit.e67f0147", version: "v0.4.24+commit.e67f0147",
optimized: false optimized: false
} }
@ -419,6 +439,23 @@ defmodule Explorer.Factory do
} }
end end
def transaction_to_verified_contract_factory do
smart_contract = build(:smart_contract)
address = %Address{
hash: address_hash(),
contract_code: contract_code_info().bytecode,
smart_contract: smart_contract
}
input_data =
"set(uint)"
|> ABI.encode([50])
|> Base.encode16(case: :lower)
build(:transaction, to_address: address, input: "0x" <> input_data)
end
def transaction_hash do def transaction_hash do
{:ok, transaction_hash} = {:ok, transaction_hash} =
"transaction_hash" "transaction_hash"
@ -441,33 +478,15 @@ defmodule Explorer.Factory do
end end
def smart_contract_factory() do def smart_contract_factory() do
contract_code_info = contract_code_info()
%SmartContract{ %SmartContract{
address_hash: insert(:address).hash, address_hash: insert(:address).hash,
compiler_version: "0.4.24", compiler_version: contract_code_info.version,
name: "SimpleStorage", name: contract_code_info.name,
contract_source_code: contract_source_code: contract_code_info.source_code,
"pragma solidity ^0.4.24; contract SimpleStorage {uint storedData; function set(uint x) public {storedData = x; } function get() public constant returns (uint) {return storedData; } }", optimization: contract_code_info.optimized,
optimization: false, abi: contract_code_info.abi
abi: [
%{
"constant" => false,
"inputs" => [%{"name" => "x", "type" => "uint256"}],
"name" => "set",
"outputs" => [],
"payable" => false,
"stateMutability" => "nonpayable",
"type" => "function"
},
%{
"constant" => true,
"inputs" => [],
"name" => "get",
"outputs" => [%{"name" => "", "type" => "uint256"}],
"payable" => false,
"stateMutability" => "view",
"type" => "function"
}
]
} }
end end

@ -27,7 +27,7 @@
"earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"}, "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"}, "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"}, "elixir_make": {:hex, :elixir_make, "0.4.2", "332c649d08c18bc1ecc73b1befc68c647136de4f340b548844efc796405743bf", [:mix], [], "hexpm"},
"ex_abi": {:hex, :ex_abi, "0.1.16", "3bb346968995f505b0f2fed1704321c572349e29614f5a2dbe2ae8fc0f18a89e", [:mix], [{:exth_crypto, "~> 0.1.4", [hex: :exth_crypto, repo: "hexpm", optional: false]}], "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_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": {: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_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]}]}, "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]}]},
@ -37,7 +37,7 @@
"exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], []}, "exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], []},
"excoveralls": {:git, "https://github.com/KronicDeth/excoveralls.git", "0a859b68851eeba9b43eba59fbc8f9098299cfe1", [branch: "circle-workflows"]}, "excoveralls": {:git, "https://github.com/KronicDeth/excoveralls.git", "0a859b68851eeba9b43eba59fbc8f9098299cfe1", [branch: "circle-workflows"]},
"exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]},
"exth_crypto": {:hex, :exth_crypto, "0.1.4", "11f9084dfd70d4f9e96f2710a472f4e6b23044b97530c719550c2b0450ffeb61", [:mix], [{:binary, "~> 0.0.4", [hex: :binary, optional: false]}, {:keccakf1600, "~> 2.0.0", [hex: :keccakf1600_orig, optional: false]}, {:libsecp256k1, "~> 0.1.3", [hex: :libsecp256k1, optional: false]}]}, "exth_crypto": {:hex, :exth_crypto, "0.1.6", "8e636a9bcb75d8e32451be96e547a495121ed2178d078db294edb0f81f7cf2e8", [:mix], [{:binary, "~> 0.0.4", [hex: :binary, repo: "hexpm", optional: false]}, {:keccakf1600, "~> 2.0.0", [hex: :keccakf1600_orig, repo: "hexpm", optional: false]}, {:libsecp256k1, "~> 0.1.9", [hex: :libsecp256k1, repo: "hexpm", optional: false]}], "hexpm"},
"exvcr": {:hex, :exvcr, "0.10.3", "1ae3b97560430acfa88ebc737c85b2b7a9dbacd8a2b26789a19718b51ae3522c", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, "exvcr": {:hex, :exvcr, "0.10.3", "1ae3b97560430acfa88ebc737c85b2b7a9dbacd8a2b26789a19718b51ae3522c", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
"file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"}, "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"},
"floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"}, "floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},

Loading…
Cancel
Save