parent
1908097a9b
commit
48f363e0c6
@ -0,0 +1,47 @@ |
||||
<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> |
||||
|
||||
<%= unless Enum.empty?(@mapping) do %> |
||||
<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> |
||||
<% end %> |
@ -0,0 +1,74 @@ |
||||
defmodule Explorer.Chain.ContractMethod do |
||||
@moduledoc """ |
||||
The representation of an individual item from the ABI of a verified smart contract. |
||||
""" |
||||
|
||||
require Logger |
||||
|
||||
use Explorer.Schema |
||||
|
||||
alias Explorer.Chain.{Hash, MethodIdentifier} |
||||
alias Explorer.Repo |
||||
|
||||
@type t :: %__MODULE__{ |
||||
identifier: MethodIdentifier.t(), |
||||
abi: map(), |
||||
type: String.t() |
||||
} |
||||
|
||||
schema "contract_methods" do |
||||
field(:identifier, MethodIdentifier) |
||||
field(:abi, :map) |
||||
field(:type, :string) |
||||
|
||||
timestamps() |
||||
end |
||||
|
||||
def upsert_from_abi(abi, address_hash) do |
||||
{successes, errors} = |
||||
abi |
||||
|> Enum.reject(fn selector -> |
||||
Map.get(selector, "type") in ["fallback", "constructor"] |
||||
end) |
||||
|> Enum.reduce({[], []}, fn selector, {successes, failures} -> |
||||
case abi_element_to_contract_method(selector) do |
||||
{:error, message} -> |
||||
{successes, [message | failures]} |
||||
|
||||
selector -> |
||||
{[selector | successes], failures} |
||||
end |
||||
end) |
||||
|
||||
unless Enum.empty?(errors) do |
||||
Logger.error(fn -> |
||||
["Error parsing some abi elements at ", Hash.to_iodata(address_hash), ": ", Enum.intersperse(errors, "\n")] |
||||
end) |
||||
end |
||||
|
||||
Repo.insert_all(__MODULE__, successes, on_conflict: :nothing, conflict_target: [:identifier, :abi]) |
||||
end |
||||
|
||||
defp abi_element_to_contract_method(element) do |
||||
case ABI.parse_specification([element], include_events?: true) do |
||||
[selector] -> |
||||
now = DateTime.utc_now() |
||||
|
||||
%{ |
||||
identifier: selector.method_id, |
||||
abi: element, |
||||
type: Atom.to_string(selector.type), |
||||
inserted_at: now, |
||||
updated_at: now |
||||
} |
||||
|
||||
_ -> |
||||
{:error, "Failed to parse abi row."} |
||||
end |
||||
rescue |
||||
e -> |
||||
message = Exception.format(:error, e) |
||||
|
||||
{:error, message} |
||||
end |
||||
end |
@ -0,0 +1,36 @@ |
||||
defmodule Explorer.Chain.MethodIdentifier do |
||||
@moduledoc """ |
||||
The first four bytes of the [KECCAK-256](https://en.wikipedia.org/wiki/SHA-3) hash of a contract method or event. |
||||
|
||||
Represented in the database as a 4 byte integer, decodes into a 4 byte bitstring |
||||
""" |
||||
|
||||
@behaviour Ecto.Type |
||||
|
||||
@type t :: binary |
||||
|
||||
@impl true |
||||
def type, do: :integer |
||||
|
||||
@impl true |
||||
@spec load(integer) :: {:ok, t()} |
||||
def load(value) do |
||||
{:ok, <<value::integer-signed-32>>} |
||||
end |
||||
|
||||
@impl true |
||||
@spec cast(binary) :: {:ok, t()} | :error |
||||
def cast(<<_::binary-size(4)>> = identifier) do |
||||
{:ok, identifier} |
||||
end |
||||
|
||||
def cast(_), do: :error |
||||
|
||||
@impl true |
||||
@spec dump(t()) :: {:ok, integer} | :error |
||||
def dump(<<num::integer-signed-32>>) do |
||||
{:ok, num} |
||||
end |
||||
|
||||
def dump(_), do: :error |
||||
end |
@ -0,0 +1,15 @@ |
||||
defmodule Explorer.Repo.Migrations.AddContractMethods do |
||||
use Ecto.Migration |
||||
|
||||
def change do |
||||
create table(:contract_methods) do |
||||
add(:identifier, :integer, null: false) |
||||
add(:abi, :map, null: false) |
||||
add(:type, :string, null: false) |
||||
|
||||
timestamps() |
||||
end |
||||
|
||||
create(unique_index(:contract_methods, [:identifier, :abi])) |
||||
end |
||||
end |
Loading…
Reference in new issue