Backfill receipts for transactions

pull/3/head
CJ Bryan, Doc Ritezel and Matt Olenick 7 years ago
parent 67341f15bc
commit bb4ba872a4
  1. 3
      Procfile
  2. 1
      assets/css/components/_all.scss
  3. 47
      assets/css/components/_transaction_log.scss
  4. 13
      lib/explorer/forms/transaction_form.ex
  5. 58
      lib/explorer/importers/transaction_receipt_importer.ex
  6. 35
      lib/explorer/log.ex
  7. 20
      lib/explorer/skipped_receipts.ex
  8. 4
      lib/explorer/transaction.ex
  9. 35
      lib/explorer/transaction_receipt.ex
  10. 4
      lib/explorer_web/controllers/pending_transaction_controller.ex
  11. 10
      lib/explorer_web/controllers/transaction_controller.ex
  12. 17
      lib/explorer_web/controllers/transaction_log_controller.ex
  13. 4
      lib/explorer_web/router.ex
  14. 6
      lib/explorer_web/templates/pending_transaction/index.html.eex
  15. 7
      lib/explorer_web/templates/transaction/show.html.eex
  16. 37
      lib/explorer_web/templates/transaction_log/index.html.eex
  17. 4
      lib/explorer_web/views/transaction_log_view.ex
  18. 2
      lib/mix/tasks/scrape.blocks.ex
  19. 23
      lib/mix/tasks/scrape.receipts.ex
  20. 95
      priv/gettext/default.pot
  21. 95
      priv/gettext/en/LC_MESSAGES/default.po
  22. 17
      priv/repo/migrations/20180212214442_create_transaction_receipts.exs
  23. 17
      priv/repo/migrations/20180212222309_create_logs.exs
  24. 42
      test/explorer/forms/transaction_form_test.exs
  25. 33
      test/explorer/importers/transaction_receipt_importer_test.exs
  26. 19
      test/explorer/log_test.exs
  27. 60
      test/explorer/skipped_transactions_test.exs
  28. 21
      test/explorer/transaction_receipt_test.exs
  29. 15
      test/explorer_web/controllers/pending_transaction_controller_test.exs
  30. 9
      test/explorer_web/controllers/transaction_controller_test.exs
  31. 25
      test/explorer_web/controllers/transaction_log_controller_test.exs
  32. 35
      test/explorer_web/features/contributor_browsing_test.exs
  33. 16
      test/support/factories/log_factory.ex
  34. 14
      test/support/factories/transaction_receipt_factory.ex
  35. 8
      test/support/factory.ex
  36. 1
      test/support/fixture/vcr_cassettes/transaction_importer_import_1_receipt.json

@ -1,4 +1,5 @@
web: bin/start-pgbouncer-stunnel mix phx.server
worker: bin/start-pgbouncer-stunnel mix exq.start
scheduler: bin/start-pgbouncer-stunnel mix exq.start scheduler
scraper: bin/start-pgbouncer-stunnel mix scrape 1000000
blocks: bin/start-pgbouncer-stunnel mix scrape.blocks 1000000
receipts: bin/start-pgbouncer-stunnel mix scrape.receipts 1000000

@ -8,4 +8,5 @@
@import "header";
@import "pagination";
@import "transaction";
@import "transaction_log";
@import "transactions";

@ -0,0 +1,47 @@
.transaction-log {
@extend %paper;
&__container {
padding: explorer-size(-1) explorer-size(0);
& + & { padding-top: 0; }
&--title { padding-top: explorer-size(0); }
}
&__header { @extend %section-header; }
&__heading { @extend %section-header__heading; }
&__subheading { @extend %section-header__subheading; }
&__tabs { @extend %section-tabs; }
&__tab {
@extend %section-tabs__tab;
&--active { @extend %section-tabs__tab--active; }
}
&__attributes { padding: explorer-size(-1) explorer-size(1); }
&__link { color: explorer-color("blue", "500"); }
&__table {
@extend %table;
@include explorer-typography("body1");
color: explorer-color("slate", "100");
}
&__column-header { @include explorer-typography("body1"); }
}
@media (min-width: $explorer-breakpoint-lg) {
.transaction {
&__attributes {
display: flex;
align-items: top;
justify-content: top;
}
&__column {
width: explorer-size(1);
flex: 1;
margin-right: explorer-size(1);
& + & { margin-left: explorer-size(1); }
}
}
}

@ -7,9 +7,11 @@ defmodule Explorer.TransactionForm do
alias Cldr.Number
alias Explorer.Block
alias Explorer.Repo
alias Explorer.TransactionReceipt
def build(transaction) do
block = Ecto.assoc_loaded?(transaction.block) && transaction.block || nil
receipt = Ecto.assoc_loaded?(transaction.receipt) && transaction.receipt
Map.merge(transaction, %{
block_number: block |> block_number,
@ -20,7 +22,7 @@ defmodule Explorer.TransactionForm do
to_address_hash: transaction |> to_address_hash,
from_address_hash: transaction |> from_address_hash,
confirmations: block |> confirmations,
status: block |> status,
status: status(block, receipt || TransactionReceipt.null),
first_seen: transaction |> first_seen,
last_seen: transaction |> last_seen,
})
@ -59,8 +61,13 @@ defmodule Explorer.TransactionForm do
block && Repo.one(query) - block.number || 0
end
def status(block) do
block && gettext("Success") || gettext("Pending")
def status(block, receipt) do
statuses = %{0 => gettext("Success"), 1 => gettext("Failure")}
if is_nil(block) || is_nil(receipt.status) do
gettext("Pending")
else
statuses[receipt.status]
end
end
def first_seen(transaction) do

@ -0,0 +1,58 @@
defmodule Explorer.TransactionReceiptImporter do
@moduledoc "Imports a transaction receipt given a transaction hash."
import Ecto.Query
import Ethereumex.HttpClient, only: [eth_get_transaction_receipt: 1]
alias Explorer.Repo
alias Explorer.Transaction
alias Explorer.TransactionReceipt
def import(hash) do
hash
|> download_receipt()
|> ingest_receipt()
|> save_receipt()
end
@dialyzer {:nowarn_function, download_receipt: 1}
defp download_receipt(hash) do
{:ok, receipt} = eth_get_transaction_receipt(hash)
receipt
end
defp ingest_receipt(%{} = receipt) do
hash = String.downcase(receipt["transactionHash"])
query = from transaction in Transaction,
left_join: receipt in assoc(transaction, :receipt),
where: transaction.hash == ^hash,
where: is_nil(receipt.id),
limit: 1
transaction = Repo.one(query) || Transaction.null
receipt
|> extract_receipt()
|> Map.put(:transaction_id, transaction.id)
end
defp save_receipt(receipt) do
unless is_nil(receipt.transaction_id) do
%TransactionReceipt{}
|> TransactionReceipt.changeset(receipt)
|> Repo.insert()
end
end
defp extract_receipt(receipt) do
%{
index: receipt["transactionIndex"] |> decode_integer_field(),
cumulative_gas_used: receipt["cumulativeGasUsed"] |> decode_integer_field(),
gas_used: receipt["gasUsed"] |> decode_integer_field(),
status: receipt["status"] |> decode_integer_field(),
}
end
defp decode_integer_field(hex) do
{"0x", base_16} = String.split_at(hex, 2)
String.to_integer(base_16, 16)
end
end

@ -0,0 +1,35 @@
defmodule Explorer.Log do
@moduledoc "Captures a Web3 log entry generated by a transaction"
use Ecto.Schema
import Ecto.Changeset
alias Explorer.Address
alias Explorer.Log
alias Explorer.TransactionReceipt
@timestamps_opts [type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}]
@required_attrs ~w(index data removed)a
schema "logs" do
belongs_to :transaction_receipt, TransactionReceipt
belongs_to :address, Address
has_one :transaction, through: [:transaction_receipt, :transaction]
field :index, :integer
field :data, :string
field :removed, :boolean
field :first_topic, :string
field :second_topic, :string
field :third_topic, :string
timestamps()
end
def changeset(%Log{} = log, attrs \\ %{}) do
log
|> cast(attrs, @required_attrs)
|> validate_required(@required_attrs)
end
end

@ -0,0 +1,20 @@
defmodule Explorer.SkippedReceipts do
@moduledoc """
Find transactions that do not have a receipt.
"""
import Ecto.Query, only: [from: 2]
alias Explorer.Transaction
alias Explorer.Repo.NewRelic, as: Repo
def first, do: first(1)
def first(count) do
transactions = from transaction in Transaction,
left_join: receipt in assoc(transaction, :receipt),
select: fragment("hash"),
where: is_nil(receipt.id),
order_by: [desc: fragment("lower(hash)")],
limit: ^count
Repo.all(transactions)
end
end

@ -9,11 +9,13 @@ defmodule Explorer.Transaction do
alias Explorer.FromAddress
alias Explorer.ToAddress
alias Explorer.Transaction
alias Explorer.TransactionReceipt
@timestamps_opts [type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}]
schema "transactions" do
has_one :receipt, TransactionReceipt
has_one :block_transaction, BlockTransaction
has_one :block, through: [:block_transaction, :block]
has_one :to_address_join, ToAddress
@ -48,4 +50,6 @@ defmodule Explorer.Transaction do
|> update_change(:hash, &String.downcase/1)
|> unique_constraint(:hash)
end
def null, do: %Transaction{}
end

@ -0,0 +1,35 @@
defmodule Explorer.TransactionReceipt do
@moduledoc "Captures a Web3 Transaction Receipt."
use Ecto.Schema
import Ecto.Changeset
alias Explorer.Transaction
alias Explorer.TransactionReceipt
@timestamps_opts [type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}]
@required_attrs ~w(cumulative_gas_used gas_used status index)a
schema "transaction_receipts" do
belongs_to :transaction, Transaction
field :cumulative_gas_used, :decimal
field :gas_used, :decimal
field :status, :integer
field :index, :integer
timestamps()
end
def changeset(%TransactionReceipt{} = transaction_receipt, attrs \\ %{}) do
transaction_receipt
|> cast(attrs, [:transaction_id | @required_attrs])
|> cast_assoc(:transaction)
|> validate_required(@required_attrs)
|> foreign_key_constraint(:transaction_id)
|> unique_constraint(:transaction_id)
end
def null, do: %TransactionReceipt{}
end

@ -9,7 +9,7 @@ defmodule ExplorerWeb.PendingTransactionController do
def index(conn, params) do
query = from transaction in Transaction,
left_join: block_transaction in assoc(transaction, :block_transaction),
left_join: transaction_receipt in assoc(transaction, :receipt),
join: to_address_join in assoc(transaction, :to_address_join),
join: to_address in assoc(to_address_join, :address),
join: from_address_join in assoc(transaction, :from_address_join),
@ -19,7 +19,7 @@ defmodule ExplorerWeb.PendingTransactionController do
from_address: from_address
],
order_by: [desc: transaction.inserted_at],
where: is_nil(block_transaction.transaction_id)
where: is_nil(transaction_receipt.transaction_id)
transactions = query |> Repo.paginate(params)
render(

@ -9,11 +9,9 @@ defmodule ExplorerWeb.TransactionController do
def index(conn, params) do
query = from transaction in Transaction,
join: block_transaction in assoc(transaction, :block_transaction),
join: block in assoc(block_transaction, :block),
preload: [
block: block,
],
join: receipt in assoc(transaction, :receipt),
join: block in assoc(transaction, :block),
preload: [block: block, receipt: receipt],
order_by: [desc: block.timestamp]
transactions = Repo.paginate(query, params)
@ -25,6 +23,7 @@ defmodule ExplorerWeb.TransactionController do
hash = String.downcase(params["id"])
query = from transaction in Transaction,
left_join: block_transaction in assoc(transaction, :block_transaction),
left_join: receipt in assoc(transaction, :receipt),
left_join: block in assoc(block_transaction, :block),
left_join: to_address_join in assoc(transaction, :to_address_join),
left_join: to_address in assoc(to_address_join, :address),
@ -32,6 +31,7 @@ defmodule ExplorerWeb.TransactionController do
left_join: from_address in assoc(from_address_join, :address),
preload: [
block: block,
receipt: receipt,
to_address: to_address,
from_address: from_address
],

@ -0,0 +1,17 @@
defmodule ExplorerWeb.TransactionLogController do
use ExplorerWeb, :controller
import Ecto.Query
alias Explorer.Log
alias Explorer.Repo.NewRelic, as: Repo
def index(conn, params) do
hash = params["transaction_id"]
logs = from log in Log,
join: transaction in assoc(log, :transaction),
preload: [:address],
where: transaction.hash == ^hash
render(conn, "index.html", logs: Repo.paginate(logs), transaction_id: hash)
end
end

@ -63,7 +63,9 @@ defmodule ExplorerWeb.Router do
resources "/", ChainController, only: [:show], singleton: true, as: :chain
resources "/blocks", BlockController, only: [:index, :show]
resources "/pending_transactions", PendingTransactionController, only: [:index]
resources "/transactions", TransactionController, only: [:index, :show]
resources "/transactions", TransactionController, only: [:index, :show] do
resources "/logs", TransactionLogController, only: [:index], as: :log
end
resources "/addresses", AddressController, only: [:show]
end
end

@ -21,14 +21,14 @@
<%= for transaction <- @transactions do %>
<tr class="transactions__row">
<td class="transactions__column transactions__column--hash">
<div class="transactions__hash"><%= link(transaction.hash, to: transaction_path(@conn, :show, @conn.assigns.locale, transaction.hash)) %></div>
<div class="transactions__hash"><%= link(transaction.hash, to: transaction_path(@conn, :show, @conn.assigns.locale, transaction.hash), class: "transactions__link") %></div>
</td>
<td class="transactions__column transactions__column--last-seen"><%= transaction.last_seen %></td>
<td class="transactions__column transactions__column--gas-price"><%= transaction.gas_price %></td>
<td class="transactions__column transactions__column--gas-limit"><%= transaction.gas |> Cldr.Number.to_string! %></td>
<td class="transactions__column transactions__column--optional transactions__column--from-address"><%= link(transaction.from_address_hash, to: address_path(@conn, :show, @conn.assigns.locale, transaction.from_address_hash)) %>
<td class="transactions__column transactions__column--optional transactions__column--from-address"><%= link(transaction.from_address_hash, to: address_path(@conn, :show, @conn.assigns.locale, transaction.from_address_hash), class: "transactions__link") %>
</td>
<td class="transactions__column transactions__column--optional transactions__column--to-address"><%= link(transaction.to_address_hash, to: address_path(@conn, :show, @conn.assigns.locale, transaction.to_address_hash)) %>
<td class="transactions__column transactions__column--optional transactions__column--to-address"><%= link(transaction.to_address_hash, to: address_path(@conn, :show, @conn.assigns.locale, transaction.to_address_hash), class: "transactions__link") %>
</td>
<td class="transactions__column transactions__column--optional transactions__column--value"><%= Decimal.div(Decimal.new(transaction.value), Decimal.new(1_000_000_000_000_000_000)) |> Decimal.to_string(:normal) %> <%= gettext "POA" %></td>
</tr>

@ -5,7 +5,8 @@
</div>
<div class="transaction__container">
<div class="transaction__tabs">
<h2 class="transaction__tab transaction__tab--active"><%= gettext "Overview" %></h2>
<h2 class="transaction__tab transaction__tab--active"><%= link(gettext("Overview"), to: transaction_path(@conn, :show, @conn.assigns.locale, @transaction.hash), class: "transaction__link transaction__link--active") %></h2>
<h2 class="transaction__tab"><%= link(gettext("Logs"), to: transaction_log_path(@conn, :index, @conn.assigns.locale, @transaction.hash), class: "transaction__link") %></h2>
</div>
<div class="transaction__attributes">
<div class="transaction__column">
@ -41,7 +42,7 @@
<dt class="transaction__item-key"><%= gettext "From" %></dt>
<dd class="transaction__item-value">
<%= if @transaction.from_address do %>
<%= link(@transaction.from_address.hash, to: address_path(@conn, :show, @conn.assigns.locale, @transaction.from_address.hash)) %>
<%= link(@transaction.from_address.hash, to: address_path(@conn, :show, @conn.assigns.locale, @transaction.from_address.hash), class: "transaction__link") %>
<% else %>
<%= gettext "Pending" %>
<% end %>
@ -51,7 +52,7 @@
<dt class="transaction__item-key"><%= gettext "To" %></dt>
<dd class="transaction__item-value">
<%= if @transaction.to_address do %>
<%= link(@transaction.to_address.hash, to: address_path(@conn, :show, @conn.assigns.locale, @transaction.to_address.hash)) %>
<%= link(@transaction.to_address.hash, to: address_path(@conn, :show, @conn.assigns.locale, @transaction.to_address.hash), class: "transaction__link") %>
<% else %>
<%= gettext "Pending" %>
<% end %>

@ -0,0 +1,37 @@
<section class="container__section">
<div class="transaction-log__header">
<h1 class="transaction-log__heading"><%= gettext "Transaction Logs" %></h1>
<h3 class="transaction-log__subheading"><%= @transaction_id %></h3>
</div>
<div class="transaction-log">
<div class="transaction-log__tabs">
<h2 class="transaction-log__tab"><%= link(gettext("Overview"), to: transaction_path(@conn, :show, @conn.assigns.locale, @transaction_id), class: "transaction-log__link") %></h2>
<h2 class="transaction-log__tab transaction-log__tab--active"><%= link(gettext("Logs"), to: transaction_log_path(@conn, :index, @conn.assigns.locale, @transaction_id), class: "transaction-log__link transaction-log__link--active") %></h2>
</div>
<div class="transaction-log__container">
<table class="transaction-log__table">
<thead>
<th class="transaction-log__column-header"><%= gettext "Address" %></th>
<th class="transaction-log__column-header"><%= gettext "Topic" %></th>
</thead>
<%= for log <- @logs.entries do %>
<tgroup>
<tr>
<td><%= link(log.address.hash, to: address_path(@conn, :show, @conn.assigns.locale, log.address.hash), class: "transaction-log__link") %></td>
<td><%= log.first_topic %></td>
</tr>
<% unless is_nil(log.second_topic) do %>
<tr><td>topic[1]</td><td><%= log.second_topic %></td></tr>
<% end %>
<% unless is_nil(log.third_topic) do %>
<tr><td>topic[2]</td><td><%= log.third_topic %></td></tr>
<% end %>
<% unless is_nil(log.data) do %>
<tr><td>↠</td><td><%= log.data %></td></tr>
<% end %>
</tgroup>
<% end %>
</table>
</div>
</div>
</section>

@ -0,0 +1,4 @@
defmodule ExplorerWeb.TransactionLogView do
use ExplorerWeb, :view
@dialyzer :no_match
end

@ -1,4 +1,4 @@
defmodule Mix.Tasks.Scrape do
defmodule Mix.Tasks.Scrape.Blocks do
@moduledoc "Scrapes blocks from web3"
use Mix.Task
alias Explorer.Repo

@ -0,0 +1,23 @@
defmodule Mix.Tasks.Scrape.Receipts do
@moduledoc "Scrapes blocks from web3"
use Mix.Task
alias Explorer.Repo
alias Explorer.SkippedReceipts
alias Explorer.TransactionReceiptImporter
def run([]), do: run(1)
def run(count) do
[:postgrex, :ecto, :ethereumex, :tzdata]
|> Enum.each(&Application.ensure_all_started/1)
Repo.start_link()
"#{count}"
|> String.to_integer()
|> SkippedReceipts.first()
|> Enum.shuffle()
|> Flow.from_enumerable()
|> Flow.map(&TransactionReceiptImporter.import/1)
|> Enum.to_list()
end
end

@ -2,7 +2,7 @@
#: lib/explorer_web/templates/chain/show.html.eex:11
#: lib/explorer_web/templates/chain/show.html.eex:39
#: lib/explorer_web/templates/transaction/index.html.eex:13
#: lib/explorer_web/templates/transaction/show.html.eex:33
#: lib/explorer_web/templates/transaction/show.html.eex:34
msgid "Age"
msgstr ""
@ -12,7 +12,7 @@ msgid "Block"
msgstr ""
#: lib/explorer_web/templates/chain/show.html.eex:4
#: lib/explorer_web/templates/layout/_header.html.eex:16
#: lib/explorer_web/templates/layout/_header.html.eex:20
msgid "Blocks"
msgstr ""
@ -28,6 +28,7 @@ msgstr ""
#: lib/explorer_web/templates/block/show.html.eex:25
#: lib/explorer_web/templates/chain/show.html.eex:37
#: lib/explorer_web/templates/pending_transaction/index.html.eex:11
#: lib/explorer_web/templates/transaction/index.html.eex:11
msgid "Hash"
msgstr ""
@ -51,9 +52,9 @@ msgid "Transactions"
msgstr ""
#: lib/explorer_web/templates/chain/show.html.eex:40
#: lib/explorer_web/templates/pending_transaction/index.html.eex:17
#: lib/explorer_web/templates/transaction/index.html.eex:14
#: lib/explorer_web/templates/transaction/show.html.eex:37
#: lib/explorer_web/templates/transaction/show.html.eex:49
#: lib/explorer_web/templates/transaction/show.html.eex:38
msgid "Value"
msgstr ""
@ -67,7 +68,8 @@ msgstr ""
#: lib/explorer_web/templates/block/index.html.eex:15
#: lib/explorer_web/templates/block/show.html.eex:57
#: lib/explorer_web/templates/transaction/show.html.eex:57
#: lib/explorer_web/templates/pending_transaction/index.html.eex:14
#: lib/explorer_web/templates/transaction/show.html.eex:74
msgid "Gas Limit"
msgstr ""
@ -76,7 +78,7 @@ msgid "Miner"
msgstr ""
#: lib/explorer_web/templates/block/show.html.eex:61
#: lib/explorer_web/templates/transaction/show.html.eex:69
#: lib/explorer_web/templates/transaction/show.html.eex:86
msgid "Nonce"
msgstr ""
@ -100,7 +102,7 @@ msgstr ""
msgid "Total Difficulty"
msgstr ""
#: lib/explorer_web/templates/transaction/show.html.eex:22
#: lib/explorer_web/templates/transaction/show.html.eex:23
msgid "Block Number"
msgstr ""
@ -108,7 +110,7 @@ msgstr ""
msgid "Transaction Details"
msgstr ""
#: lib/explorer_web/templates/transaction/show.html.eex:65
#: lib/explorer_web/templates/transaction/show.html.eex:82
msgid "Cumulative Gas Used"
msgstr ""
@ -117,15 +119,16 @@ msgid "Gas"
msgstr ""
#: lib/explorer_web/templates/block/index.html.eex:16
#: lib/explorer_web/templates/transaction/show.html.eex:61
#: lib/explorer_web/templates/pending_transaction/index.html.eex:13
#: lib/explorer_web/templates/transaction/show.html.eex:78
msgid "Gas Price"
msgstr ""
#: lib/explorer_web/templates/transaction/show.html.eex:73
#: lib/explorer_web/templates/transaction/show.html.eex:90
msgid "Input"
msgstr ""
#: lib/explorer_web/templates/transaction/show.html.eex:28
#: lib/explorer_web/templates/transaction/show.html.eex:29
msgid "%{confirmations} block confirmations"
msgstr ""
@ -134,31 +137,35 @@ msgid "%{count} transactions in this block"
msgstr ""
#: lib/explorer_web/templates/address/show.html.eex:3
#: lib/explorer_web/templates/transaction_log/index.html.eex:14
msgid "Address"
msgstr ""
#: lib/explorer_web/templates/transaction/show.html.eex:41
#: lib/explorer_web/templates/pending_transaction/index.html.eex:15
#: lib/explorer_web/templates/transaction/show.html.eex:42
msgid "From"
msgstr ""
#: lib/explorer_web/templates/block/show.html.eex:7
#: lib/explorer_web/templates/transaction/show.html.eex:8
#: lib/explorer_web/templates/transaction_log/index.html.eex:8
msgid "Overview"
msgstr ""
#: lib/explorer/forms/transaction_form.ex:74
#: lib/explorer/forms/transaction_form.ex:65
msgid "Success"
msgstr ""
#: lib/explorer_web/templates/transaction/show.html.eex:45
#: lib/explorer_web/templates/pending_transaction/index.html.eex:16
#: lib/explorer_web/templates/transaction/show.html.eex:52
msgid "To"
msgstr ""
#: lib/explorer_web/templates/transaction/show.html.eex:14
#: lib/explorer_web/templates/transaction/show.html.eex:15
msgid "Transaction Hash"
msgstr ""
#: lib/explorer_web/templates/transaction/show.html.eex:18
#: lib/explorer_web/templates/transaction/show.html.eex:19
msgid "Transaction Status"
msgstr ""
@ -168,8 +175,9 @@ msgstr ""
#: lib/explorer_web/templates/address/show.html.eex:11
#: lib/explorer_web/templates/chain/show.html.eex:53
#: lib/explorer_web/templates/pending_transaction/index.html.eex:33
#: lib/explorer_web/templates/transaction/index.html.eex:37
#: lib/explorer_web/templates/transaction/show.html.eex:38
#: lib/explorer_web/templates/transaction/show.html.eex:39
msgid "POA"
msgstr ""
@ -185,8 +193,55 @@ msgstr ""
msgid "Showing %{count} Transactions"
msgstr ""
#: lib/explorer/forms/transaction_form.ex:76
#: lib/explorer_web/templates/transaction/index.html.eex:27
#: lib/explorer_web/templates/transaction/index.html.eex:34
#: lib/explorer/forms/transaction_form.ex:40
#: lib/explorer/forms/transaction_form.ex:44
#: lib/explorer/forms/transaction_form.ex:48
#: lib/explorer/forms/transaction_form.ex:67
#: lib/explorer_web/templates/transaction/index.html.eex:25
#: lib/explorer_web/templates/transaction/index.html.eex:32
#: lib/explorer_web/templates/transaction/show.html.eex:47
#: lib/explorer_web/templates/transaction/show.html.eex:57
msgid "Pending"
msgstr ""
#, elixir-format
#: lib/explorer/forms/transaction_form.ex:65
msgid "Failure"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/transaction/show.html.eex:66
msgid "First Seen"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/pending_transaction/index.html.eex:12
#: lib/explorer_web/templates/transaction/show.html.eex:70
msgid "Last Seen"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/transaction/show.html.eex:9
#: lib/explorer_web/templates/transaction_log/index.html.eex:9
msgid "Logs"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/layout/_header.html.eex:16
msgid "Pending Transactions"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/pending_transaction/index.html.eex:3
msgid "Showing %{count} Pending Transactions"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/transaction_log/index.html.eex:15
msgid "Topic"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/transaction_log/index.html.eex:3
msgid "Transaction Logs"
msgstr ""

@ -14,7 +14,7 @@ msgstr ""
#: lib/explorer_web/templates/chain/show.html.eex:11
#: lib/explorer_web/templates/chain/show.html.eex:39
#: lib/explorer_web/templates/transaction/index.html.eex:13
#: lib/explorer_web/templates/transaction/show.html.eex:33
#: lib/explorer_web/templates/transaction/show.html.eex:34
msgid "Age"
msgstr "Age"
@ -24,7 +24,7 @@ msgid "Block"
msgstr "Block"
#: lib/explorer_web/templates/chain/show.html.eex:4
#: lib/explorer_web/templates/layout/_header.html.eex:16
#: lib/explorer_web/templates/layout/_header.html.eex:20
msgid "Blocks"
msgstr "Blocks"
@ -40,6 +40,7 @@ msgstr "Gas Used"
#: lib/explorer_web/templates/block/show.html.eex:25
#: lib/explorer_web/templates/chain/show.html.eex:37
#: lib/explorer_web/templates/pending_transaction/index.html.eex:11
#: lib/explorer_web/templates/transaction/index.html.eex:11
msgid "Hash"
msgstr "Hash"
@ -63,9 +64,9 @@ msgid "Transactions"
msgstr "Transactions"
#: lib/explorer_web/templates/chain/show.html.eex:40
#: lib/explorer_web/templates/pending_transaction/index.html.eex:17
#: lib/explorer_web/templates/transaction/index.html.eex:14
#: lib/explorer_web/templates/transaction/show.html.eex:37
#: lib/explorer_web/templates/transaction/show.html.eex:49
#: lib/explorer_web/templates/transaction/show.html.eex:38
msgid "Value"
msgstr "Value"
@ -79,7 +80,8 @@ msgstr "Difficulty"
#: lib/explorer_web/templates/block/index.html.eex:15
#: lib/explorer_web/templates/block/show.html.eex:57
#: lib/explorer_web/templates/transaction/show.html.eex:57
#: lib/explorer_web/templates/pending_transaction/index.html.eex:14
#: lib/explorer_web/templates/transaction/show.html.eex:74
msgid "Gas Limit"
msgstr "Gas Limit"
@ -88,7 +90,7 @@ msgid "Miner"
msgstr "Validator"
#: lib/explorer_web/templates/block/show.html.eex:61
#: lib/explorer_web/templates/transaction/show.html.eex:69
#: lib/explorer_web/templates/transaction/show.html.eex:86
msgid "Nonce"
msgstr "Nonce"
@ -112,7 +114,7 @@ msgstr "Timestamp"
msgid "Total Difficulty"
msgstr "Total Difficulty"
#: lib/explorer_web/templates/transaction/show.html.eex:22
#: lib/explorer_web/templates/transaction/show.html.eex:23
msgid "Block Number"
msgstr "Block Height"
@ -120,7 +122,7 @@ msgstr "Block Height"
msgid "Transaction Details"
msgstr "Transaction Details"
#: lib/explorer_web/templates/transaction/show.html.eex:65
#: lib/explorer_web/templates/transaction/show.html.eex:82
msgid "Cumulative Gas Used"
msgstr "Cumulative Gas Used"
@ -129,15 +131,16 @@ msgid "Gas"
msgstr "Gas"
#: lib/explorer_web/templates/block/index.html.eex:16
#: lib/explorer_web/templates/transaction/show.html.eex:61
#: lib/explorer_web/templates/pending_transaction/index.html.eex:13
#: lib/explorer_web/templates/transaction/show.html.eex:78
msgid "Gas Price"
msgstr "Gas Price"
#: lib/explorer_web/templates/transaction/show.html.eex:73
#: lib/explorer_web/templates/transaction/show.html.eex:90
msgid "Input"
msgstr "Input"
#: lib/explorer_web/templates/transaction/show.html.eex:28
#: lib/explorer_web/templates/transaction/show.html.eex:29
msgid "%{confirmations} block confirmations"
msgstr "%{confirmations} block confirmations"
@ -146,31 +149,35 @@ msgid "%{count} transactions in this block"
msgstr "%{count} transactions in this block"
#: lib/explorer_web/templates/address/show.html.eex:3
#: lib/explorer_web/templates/transaction_log/index.html.eex:14
msgid "Address"
msgstr "Address"
#: lib/explorer_web/templates/transaction/show.html.eex:41
#: lib/explorer_web/templates/pending_transaction/index.html.eex:15
#: lib/explorer_web/templates/transaction/show.html.eex:42
msgid "From"
msgstr "From"
#: lib/explorer_web/templates/block/show.html.eex:7
#: lib/explorer_web/templates/transaction/show.html.eex:8
#: lib/explorer_web/templates/transaction_log/index.html.eex:8
msgid "Overview"
msgstr "Overview"
#: lib/explorer/forms/transaction_form.ex:74
#: lib/explorer/forms/transaction_form.ex:65
msgid "Success"
msgstr "Success"
#: lib/explorer_web/templates/transaction/show.html.eex:45
#: lib/explorer_web/templates/pending_transaction/index.html.eex:16
#: lib/explorer_web/templates/transaction/show.html.eex:52
msgid "To"
msgstr "To"
#: lib/explorer_web/templates/transaction/show.html.eex:14
#: lib/explorer_web/templates/transaction/show.html.eex:15
msgid "Transaction Hash"
msgstr "Transaction Hash"
#: lib/explorer_web/templates/transaction/show.html.eex:18
#: lib/explorer_web/templates/transaction/show.html.eex:19
msgid "Transaction Status"
msgstr "Transaction Status"
@ -180,8 +187,9 @@ msgstr "Balance"
#: lib/explorer_web/templates/address/show.html.eex:11
#: lib/explorer_web/templates/chain/show.html.eex:53
#: lib/explorer_web/templates/pending_transaction/index.html.eex:33
#: lib/explorer_web/templates/transaction/index.html.eex:37
#: lib/explorer_web/templates/transaction/show.html.eex:38
#: lib/explorer_web/templates/transaction/show.html.eex:39
msgid "POA"
msgstr "POA"
@ -197,8 +205,55 @@ msgstr "Showing #%{start_block} to #%{end_block}"
msgid "Showing %{count} Transactions"
msgstr "Showing %{count} Transactions"
#: lib/explorer/forms/transaction_form.ex:76
#: lib/explorer_web/templates/transaction/index.html.eex:27
#: lib/explorer_web/templates/transaction/index.html.eex:34
#: lib/explorer/forms/transaction_form.ex:40
#: lib/explorer/forms/transaction_form.ex:44
#: lib/explorer/forms/transaction_form.ex:48
#: lib/explorer/forms/transaction_form.ex:67
#: lib/explorer_web/templates/transaction/index.html.eex:25
#: lib/explorer_web/templates/transaction/index.html.eex:32
#: lib/explorer_web/templates/transaction/show.html.eex:47
#: lib/explorer_web/templates/transaction/show.html.eex:57
msgid "Pending"
msgstr "Pending"
#, elixir-format
#: lib/explorer/forms/transaction_form.ex:65
msgid "Failure"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/transaction/show.html.eex:66
msgid "First Seen"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/pending_transaction/index.html.eex:12
#: lib/explorer_web/templates/transaction/show.html.eex:70
msgid "Last Seen"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/transaction/show.html.eex:9
#: lib/explorer_web/templates/transaction_log/index.html.eex:9
msgid "Logs"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/layout/_header.html.eex:16
msgid "Pending Transactions"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/pending_transaction/index.html.eex:3
msgid "Showing %{count} Pending Transactions"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/transaction_log/index.html.eex:15
msgid "Topic"
msgstr ""
#, elixir-format
#: lib/explorer_web/templates/transaction_log/index.html.eex:3
msgid "Transaction Logs"
msgstr ""

@ -0,0 +1,17 @@
defmodule Explorer.Repo.Migrations.CreateTransactionReceipts do
use Ecto.Migration
def change do
create table(:transaction_receipts) do
add :transaction_id, references(:transactions), null: false
add :cumulative_gas_used, :numeric, precision: 100, null: false
add :gas_used, :numeric, precision: 100, null: false
add :status, :integer, null: false
add :index, :integer, null: false
timestamps null: false
end
create index(:transaction_receipts, :index)
create unique_index(:transaction_receipts, [:transaction_id, :index])
end
end

@ -0,0 +1,17 @@
defmodule Explorer.Repo.Migrations.CreateLogs do
use Ecto.Migration
def change do
create table(:logs) do
add :transaction_receipt_id, references(:transaction_receipts), null: false
add :address_id, references(:addresses), null: false
add :index, :integer, null: false
add :data, :string, null: false
add :removed, :boolean, null: false
add :first_topic, :string, null: true
add :second_topic, :string, null: true
add :third_topic, :string, null: true
timestamps null: false
end
end
end

@ -4,7 +4,7 @@ defmodule Explorer.TransactionFormTest do
alias Explorer.TransactionForm
describe "build/1" do
test "that it returns the values we expect" do
test "returns a successful transaction when there is a successful receipt" do
insert(:block, number: 24)
time = Timex.now |> Timex.shift(hours: -2)
block = insert(:block, %{
@ -18,7 +18,41 @@ defmodule Explorer.TransactionFormTest do
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"))
|> with_block(block)
|> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
|> Repo.preload([:block, :to_address, :from_address])
insert(:transaction_receipt, status: 0, transaction: transaction)
form = transaction |> Repo.preload([:block, :to_address, :from_address, :receipt]) |> TransactionForm.build()
formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime)
assert(form == Map.merge(transaction |> Repo.preload([:block, :to_address, :from_address, :receipt]), %{
block_number: 1,
age: "2 hours ago",
formatted_age: "2 hours ago (#{formatted_timestamp})",
formatted_timestamp: formatted_timestamp,
cumulative_gas_used: "99,523",
to_address_hash: "0xsleepypuppy",
from_address_hash: "0xilovefrogs",
confirmations: 23,
status: "Success",
first_seen: "48 years ago",
last_seen: "38 years ago",
}))
end
test "returns a pending transaction when there is no receipt" do
insert(:block, number: 24)
time = Timex.now |> Timex.shift(hours: -2)
block = insert(:block, %{
number: 1,
gas_used: 99523,
timestamp: time,
})
transaction =
insert(:transaction,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"))
|> with_block(block)
|> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
|> Repo.preload([:block, :to_address, :from_address, :receipt])
form = TransactionForm.build(transaction)
formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime)
@ -31,13 +65,13 @@ defmodule Explorer.TransactionFormTest do
to_address_hash: "0xsleepypuppy",
from_address_hash: "0xilovefrogs",
confirmations: 23,
status: "Success",
status: "Pending",
first_seen: "48 years ago",
last_seen: "38 years ago",
}))
end
test "works when there is no block" do
test "returns a pending transaction when there is no block" do
transaction = insert(
:transaction,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),

@ -0,0 +1,33 @@
defmodule Explorer.TransactionReceiptImporterTest do
use Explorer.DataCase
alias Explorer.TransactionReceipt
alias Explorer.TransactionReceiptImporter
describe "import/1" do
test "imports and saves a transaction receipt to the database" do
transaction = insert(:transaction, hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291")
use_cassette "transaction_importer_import_1_receipt" do
TransactionReceiptImporter.import("0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291")
receipt = TransactionReceipt |> order_by(desc: :inserted_at) |> preload([:transaction]) |> Repo.one
assert receipt.transaction == transaction
end
end
test "does not import a receipt for a transaction that already has one" do
transaction = insert(:transaction, hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291")
insert(:transaction_receipt, transaction: transaction)
use_cassette "transaction_importer_import_1_receipt" do
TransactionReceiptImporter.import("0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291")
assert Repo.all(TransactionReceipt) |> Enum.count() == 1
end
end
test "does not import a receipt for a nonexistent transaction" do
use_cassette "transaction_importer_import_1_receipt" do
TransactionReceiptImporter.import("0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291")
assert Repo.all(TransactionReceipt) |> Enum.count() == 0
end
end
end
end

@ -0,0 +1,19 @@
defmodule Explorer.LogTest do
use Explorer.DataCase
alias Explorer.Log
describe "changeset/2" do
test "accepts valid attributes" do
params = params_for(:log)
changeset = Log.changeset(%Log{}, params)
assert changeset.valid?
end
test "rejects missing attributes" do
params = params_for(:log, data: nil)
changeset = Log.changeset(%Log{}, params)
refute changeset.valid?
end
end
end

@ -0,0 +1,60 @@
defmodule Explorer.SkippedReceiptsTest do
use Explorer.DataCase
alias Explorer.SkippedReceipts
describe "first/0 when there are no transactions" do
test "returns no transactions" do
assert SkippedReceipts.first() == []
end
end
describe "first/0 when there are no skipped transactions" do
test "returns no transactions" do
transaction = insert(:transaction)
insert(:transaction_receipt, transaction: transaction)
assert SkippedReceipts.first() == []
end
end
describe "first/0 when a transaction has been skipped" do
test "returns the first skipped transaction hash" do
insert(:transaction, %{hash: "0xBEE75"})
assert SkippedReceipts.first() == ["0xBEE75"]
end
end
describe "first/1 when there are no transactions" do
test "returns no transactions" do
assert SkippedReceipts.first(1) == []
end
end
describe "first/1 when there are no skipped transactions" do
test "returns no transactions" do
transaction = insert(:transaction)
insert(:transaction_receipt, transaction: transaction)
assert SkippedReceipts.first(1) == []
end
end
describe "first/1 when a transaction has been skipped" do
test "returns the skipped transaction number" do
insert(:transaction, %{hash: "0xBEE75"})
assert SkippedReceipts.first(1) == ["0xBEE75"]
end
test "returns up to the requested number of skipped transaction hashes in insert order" do
insert(:transaction, %{hash: "0xBEE75"})
insert(:transaction, %{hash: "0xBE475"})
assert SkippedReceipts.first(1) == ["0xBEE75"]
end
test "returns all the skipped transaction hashes in random order" do
insert(:transaction, %{hash: "0xBEE75"})
insert(:transaction, %{hash: "0xBE475"})
transaction_hashes = SkippedReceipts.first(100)
assert("0xBEE75" in transaction_hashes and "0xBE475" in transaction_hashes)
end
end
end

@ -0,0 +1,21 @@
defmodule Explorer.TransactionReceiptTest do
use Explorer.DataCase
alias Explorer.TransactionReceipt
describe "changeset/2" do
test "accepts valid attributes" do
transaction = insert(:transaction)
params = params_for(:transaction_receipt, transaction: transaction)
changeset = TransactionReceipt.changeset(%TransactionReceipt{}, params)
assert changeset.valid?
end
test "rejects missing attributes" do
transaction = insert(:transaction)
params = params_for(:transaction_receipt, transaction: transaction, cumulative_gas_used: nil)
changeset = TransactionReceipt.changeset(%TransactionReceipt{}, params)
refute changeset.valid?
end
end
end

@ -5,7 +5,7 @@ defmodule ExplorerWeb.PendingTransactionControllerTest do
describe "GET index/2" do
test "returns pending transactions with addresses", %{conn: conn} do
transaction = insert(:transaction) |> with_addresses(%{to: "0xfritos", from: "0xmunchos"})
transaction = insert(:transaction) |> with_block |> with_addresses(%{to: "0xfritos", from: "0xmunchos"})
conn = get(conn, pending_transaction_path(ExplorerWeb.Endpoint, :index, :en))
first_transaction = List.first(conn.assigns.transactions.entries)
assert first_transaction.id == transaction.id
@ -13,18 +13,11 @@ defmodule ExplorerWeb.PendingTransactionControllerTest do
assert first_transaction.from_address.hash == "0xmunchos"
end
test "doesn't return pending transactions without addresses", %{conn: conn} do
insert(:transaction)
conn = get(conn, pending_transaction_path(ExplorerWeb.Endpoint, :index, :en))
assert length(conn.assigns.transactions.entries) == 0
end
test "does not return transactions with a block", %{conn: conn} do
block = insert(:block)
test "does not return transactions with receipts", %{conn: conn} do
transaction = insert(:transaction)
insert(:block_transaction, block: block, transaction: transaction)
insert(:transaction_receipt, transaction: transaction)
conn = get(conn, pending_transaction_path(ExplorerWeb.Endpoint, :index, :en))
assert conn.assigns.transactions |> Enum.count == 0
assert length(conn.assigns.transactions.entries) == 0
end
end
end

@ -2,10 +2,13 @@ defmodule ExplorerWeb.TransactionControllerTest do
use ExplorerWeb.ConnCase
describe "GET index/2" do
test "returns all transactions", %{conn: conn} do
transaction_ids = insert_list(4, :transaction) |> list_with_block |> Enum.map(fn (transaction) -> transaction.id end)
test "returns a transaction with a receipt", %{conn: conn} do
transaction = insert(:transaction)
block = insert(:block)
insert(:transaction_receipt, transaction: transaction)
insert(:block_transaction, transaction: transaction, block: block)
conn = get(conn, "/en/transactions")
assert conn.assigns.transactions |> Enum.map(fn (transaction) -> transaction.id end) |> Enum.reverse == transaction_ids
assert List.first(conn.assigns.transactions.entries).id == transaction.id
end
test "returns no pending transactions", %{conn: conn} do

@ -0,0 +1,25 @@
defmodule ExplorerWeb.TransactionLogControllerTest do
use ExplorerWeb.ConnCase
import ExplorerWeb.Router.Helpers, only: [transaction_log_path: 4]
describe "GET index/2" do
test "returns logs for the transaction", %{conn: conn} do
transaction = insert(:transaction)
transaction_receipt = insert(:transaction_receipt, transaction: transaction)
address = insert(:address)
insert(:log, transaction_receipt: transaction_receipt, address: address)
path = transaction_log_path(ExplorerWeb.Endpoint, :index, :en, transaction.hash)
conn = get(conn, path)
first_log = List.first(conn.assigns.logs.entries)
assert first_log.transaction_receipt_id == transaction_receipt.id
end
test "assigns no logs when there are none", %{conn: conn} do
transaction = insert(:transaction)
path = transaction_log_path(ExplorerWeb.Endpoint, :index, :en, transaction.hash)
conn = get(conn, path)
assert Enum.count(conn.assigns.logs.entries) == 0
end
end
end

@ -58,8 +58,11 @@ defmodule ExplorerWeb.UserListTest do
gas_used: 123987,
})
for _ <- 0..3, do: insert(:transaction) |> with_block(block) |> with_addresses
insert(:transaction, hash: "0xC001", gas: 5891) |> with_block |> with_addresses
insert(:transaction, %{
to_address = insert(:address, hash: "0xlincoln")
from_address = insert(:address, hash: "0xhowardtaft")
transaction = insert(:transaction,
hash: "0xSk8",
value: 5656,
gas: 1230000000000123123,
@ -67,12 +70,13 @@ defmodule ExplorerWeb.UserListTest do
input: "0x00012",
nonce: 99045,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"),
})
|> with_block(block)
|> with_addresses(%{to: "0xabelincoln", from: "0xhowardtaft"})
insert(:transaction, hash: "0xC001", gas: 5891) |> with_addresses
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")
)
insert(:block_transaction, block: block, transaction: transaction)
insert(:from_address, address: from_address, transaction: transaction)
insert(:to_address, address: to_address, transaction: transaction)
transaction_receipt = insert(:transaction_receipt, transaction: transaction, status: 0)
insert(:log, address: to_address, transaction_receipt: transaction_receipt)
session
|> visit("/en")
@ -80,12 +84,17 @@ defmodule ExplorerWeb.UserListTest do
|> assert_has(css(".transactions__column--hash", count: 5))
|> assert_has(css(".transactions__column--value", count: 5))
|> assert_has(css(".transactions__column--age", count: 5))
|> click(css(".header__link-name--pending-transactions", text: "Pending Transactions"))
|> assert_has(css(".transactions__column--hash", text: "0xC001"))
|> assert_has(css(".transactions__column--gas-limit", text: "5,891"))
|> assert_has(css(".transactions__column--last-seen"))
|> click(css(".transactions__link", text: "0xC001"))
|> assert_has(css(".transaction__item-value--status", text: "Pending"))
|> click(css(".header__link-name--transactions", text: "Transactions"))
|> refute_has(css(".transactions__column--block", text: "Pending"))
|> click(link("0xSk8"))
|> assert_has(css(".transaction__subheading", text: "0xSk8"))
|> assert_has(css(".transaction__item", text: "123,987"))
@ -96,14 +105,16 @@ defmodule ExplorerWeb.UserListTest do
|> assert_has(css(".transaction__item", text: "0x00012"))
|> assert_has(css(".transaction__item", text: "99045"))
|> assert_has(css(".transaction__item", text: "123,987"))
|> assert_has(css(".transaction__item", text: "0xabelincoln"))
|> assert_has(css(".transaction__item", text: "0xlincoln"))
|> assert_has(css(".transaction__item", text: "0xhowardtaft"))
|> assert_has(css(".transaction__item", text: "block confirmations"))
|> assert_has(css(".transaction__item", text: "48 years ago"))
|> assert_has(css(".transaction__item", text: "38 years ago"))
session
|> click(link("0xabelincoln"))
|> assert_has(css(".address__subheading", text: "0xabelincoln"))
|> click(link("Logs"))
|> assert_has(css(".transaction-log__link", text: "0xlincoln"))
|> click(link("0xlincoln"))
|> assert_has(css(".address__subheading", text: "0xlincoln"))
end
end

@ -0,0 +1,16 @@
defmodule Explorer.LogFactory do
defmacro __using__(_opts) do
quote do
def log_factory do
%Explorer.Log{
index: sequence(""),
data: sequence("0x"),
removed: Enum.random([true, false]),
first_topic: sequence("0x"),
second_topic: sequence("0x"),
third_topic: sequence("0x"),
}
end
end
end
end

@ -0,0 +1,14 @@
defmodule Explorer.TransactionReceiptFactory do
defmacro __using__(_opts) do
quote do
def transaction_receipt_factory do
%Explorer.TransactionReceipt{
cumulative_gas_used: Enum.random(21_000..100_000),
gas_used: Enum.random(21_000..100_000),
status: Enum.random(1..2),
index: sequence(""),
}
end
end
end
end

@ -1,10 +1,12 @@
defmodule Explorer.Factory do
@dialyzer {:nowarn_function, fields_for: 1}
use ExMachina.Ecto, repo: Explorer.Repo
use Explorer.AddressFactory
use Explorer.BlockFactory
use Explorer.TransactionFactory
use Explorer.BlockTransactionFactory
use Explorer.AddressFactory
use Explorer.ToAddressFactory
use Explorer.FromAddressFactory
use Explorer.LogFactory
use Explorer.ToAddressFactory
use Explorer.TransactionFactory
use Explorer.TransactionReceiptFactory
end

Loading…
Cancel
Save