Merge branch 'master' into release_build

pull/1131/head
Andrew Cravenho 6 years ago committed by GitHub
commit a44fb28619
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      README.md
  2. 12
      apps/block_scout_web/lib/block_scout_web/chain.ex
  3. 39
      apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex
  4. 59
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex
  5. 46
      apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex
  6. 57
      apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex
  7. 2
      apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex
  8. 16
      apps/block_scout_web/priv/gettext/default.pot
  9. 16
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  10. 149
      apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs
  11. 167
      apps/block_scout_web/test/block_scout_web/controllers/address_token_transfer_controller_test.exs
  12. 18
      apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs
  13. 86
      apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs
  14. 8
      apps/ethereum_jsonrpc/README.md
  15. 4
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
  16. 2
      apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/parity/http_websocket.ex
  17. 2
      apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/case/parity.ex
  18. 2
      apps/explorer/config/dev.exs
  19. 10
      apps/explorer/config/dev/parity.exs
  20. 10
      apps/explorer/config/prod/parity.exs
  21. 5
      apps/explorer/lib/explorer/chain.ex
  22. 6
      apps/explorer/lib/explorer/chain/address.ex
  23. 14
      apps/explorer/lib/explorer/chain/import/addresses.ex
  24. 30
      apps/explorer/lib/explorer/chain/token_transfer.ex
  25. 26
      apps/explorer/priv/repo/migrations/20181106152300_add_nonce_to_addresses.exs
  26. 18
      apps/explorer/priv/repo/migrations/20181121170616_add_block_number_to_token_transfers.exs
  27. 13
      apps/explorer/priv/repo/migrations/20181126203826_add_index_to_addresses.exs
  28. 39
      apps/explorer/priv/repo/migrations/scripts/20181121170616_token_transfers_update_block_number_in_batches.sql
  29. 55
      apps/explorer/priv/repo/migrations/scripts/20181126182700_migrate_address_nonce.sql
  30. 4
      apps/explorer/test/explorer/chain/import_test.exs
  31. 43
      apps/explorer/test/explorer/chain/token_transfer_test.exs
  32. 10
      apps/explorer/test/explorer/chain_test.exs
  33. 1
      apps/explorer/test/support/factory.ex
  34. 2
      apps/explorer/test/support/fixture/vcr_cassettes/block_importer_download_block_1_downloads_the_block.json
  35. 2
      apps/explorer/test/support/fixture/vcr_cassettes/block_importer_import_1_duplicate_block.json
  36. 2
      apps/explorer/test/support/fixture/vcr_cassettes/block_importer_import_1_pending.json
  37. 2
      apps/explorer/test/support/fixture/vcr_cassettes/block_importer_import_1_saves_the_block.json
  38. 2
      apps/explorer/test/support/fixture/vcr_cassettes/ethereumex_extensions_trace_transaction_1.json
  39. 2
      apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_duplicate.json
  40. 2
      apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_earliest.json
  41. 2
      apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_integer.json
  42. 2
      apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_latest.json
  43. 2
      apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_string.json
  44. 2
      apps/explorer/test/support/fixture/vcr_cassettes/import_receipt_perform_1.json
  45. 2
      apps/explorer/test/support/fixture/vcr_cassettes/internal_transaction_importer_import_1.json
  46. 2
      apps/explorer/test/support/fixture/vcr_cassettes/internal_transaction_importer_import_1_with_contract_creation.json
  47. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_binds_internal_transactions.json
  48. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_creates_a_from_address.json
  49. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_creates_a_to_address.json
  50. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_creates_a_to_address_from_creates.json
  51. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_download_transaction.json
  52. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_download_transaction_with_a_bad_hash.json
  53. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_1_failed.json
  54. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_1_out_of_gas.json
  55. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_1_pending.json
  56. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_1_receipt.json
  57. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_saves_the_transaction.json
  58. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_saves_the_association.json
  59. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_txn_without_block.json
  60. 2
      apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_updates_the_association.json
  61. 4
      apps/indexer/README.md
  62. 2
      apps/indexer/config/dev.exs
  63. 10
      apps/indexer/config/dev/parity.exs
  64. 10
      apps/indexer/config/prod/parity.exs
  65. 99
      apps/indexer/lib/indexer/address_extraction.ex
  66. 16
      apps/indexer/test/indexer/address_extraction_test.exs
  67. 8
      apps/indexer/test/indexer/block/realtime/fetcher_test.exs
  68. 6
      docker/README.md

@ -90,7 +90,13 @@ The [development stack page](https://github.com/poanetwork/blockscout/wiki/Devel
`cd apps/block_scout_web/assets && npm install; cd -` `cd apps/block_scout_web/assets && npm install; cd -`
`cd apps/explorer && npm install; cd -` `cd apps/explorer && npm install; cd -`
7. Start Phoenix Server. 7. Update your JSON RPC Variant in `apps/explorer/config/dev.exs` and `apps/indexer/config/dev.exs`.
For `variant`, enter `ganache`, `geth`, or `parity`
8. Update your JSON RPC Endpoint in `apps/explorer/config/dev/` and `apps/indexer/config/dev/`
For the `variant` chosen in step 7, enter the correct information for the corresponding JSON RPC Endpoint in `parity.exs`, `geth.exs`, or `ganache.exs`
9. Start Phoenix Server.
`mix phx.server` `mix phx.server`
Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
@ -204,8 +210,8 @@ mix coveralls.html --umbrella --exclude no_parity
| Protocol | URL | | Protocol | URL |
|:----------|:-----------------------------------| |:----------|:-----------------------------------|
| HTTP | `https://sokol-trace.poa.network` | | HTTP | `http://localhost:8545` |
| WebSocket | `wss://sokol-ws.poa.network/ws` | | WebSocket | `ws://localhost:8546` |
##### Geth ##### Geth

@ -130,9 +130,6 @@ defmodule BlockScoutWeb.Chain do
end end
end end
def paging_options(%{"inserted_at" => inserted_at}),
do: [paging_options: %{@default_paging_options | key: inserted_at}]
def paging_options(%{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at}), def paging_options(%{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at}),
do: [paging_options: %{@default_paging_options | key: {name, type, inserted_at}}] do: [paging_options: %{@default_paging_options | key: {name, type, inserted_at}}]
@ -180,13 +177,8 @@ defmodule BlockScoutWeb.Chain do
%{"block_number" => block_number, "index" => index} %{"block_number" => block_number, "index" => index}
end end
defp paging_params(%TokenTransfer{inserted_at: inserted_at}) do defp paging_params(%TokenTransfer{block_number: block_number, log_index: index}) do
inserted_at_datetime = %{"block_number" => block_number, "index" => index}
inserted_at
|> DateTime.from_naive!("Etc/UTC")
|> DateTime.to_iso8601()
%{"inserted_at" => inserted_at_datetime}
end end
defp paging_params(%Address.Token{name: name, type: type, inserted_at: inserted_at}) do defp paging_params(%Address.Token{name: name, type: type, inserted_at: inserted_at}) do

@ -8,10 +8,12 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1] import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias BlockScoutWeb.InternalTransactionView
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
alias Phoenix.View
def index(conn, %{"address_id" => address_hash_string} = params) do def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do {:ok, address} <- Chain.hash_to_address(address_hash) do
full_options = full_options =
@ -28,14 +30,45 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
internal_transactions_plus_one = Chain.address_to_internal_transactions(address, full_options) internal_transactions_plus_one = Chain.address_to_internal_transactions(address, full_options)
{internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one) {internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one)
next_page_path =
case next_page_params(next_page, internal_transactions, params) do
nil ->
nil
next_page_params ->
address_internal_transaction_path(conn, :index, address_hash, Map.delete(next_page_params, "type"))
end
internal_transactions_json =
Enum.map(internal_transactions, fn internal_transaction ->
View.render_to_string(
InternalTransactionView,
"_tile.html",
current_address: address,
internal_transaction: internal_transaction
)
end)
json(conn, %{items: internal_transactions_json, next_page_path: next_page_path})
else
:error ->
not_found(conn)
{:error, :not_found} ->
not_found(conn)
end
end
def index(conn, %{"address_id" => address_hash_string} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
render( render(
conn, conn,
"index.html", "index.html",
address: address, address: address,
next_page_params: next_page_params(next_page, internal_transactions, params), current_path: current_path(conn),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"], filter: params["filter"],
internal_transactions: internal_transactions,
transaction_count: transaction_count(address), transaction_count: transaction_count(address),
validation_count: validation_count(address) validation_count: validation_count(address)
) )

@ -1,8 +1,10 @@
defmodule BlockScoutWeb.AddressTokenTransferController do defmodule BlockScoutWeb.AddressTokenTransferController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
alias Explorer.{Chain, Market} alias BlockScoutWeb.TransactionView
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
alias Explorer.{Chain, Market}
alias Phoenix.View
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
@ -11,12 +13,16 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
def index( def index(
conn, conn,
%{"address_id" => address_hash_string, "address_token_id" => token_hash_string} = params %{
"address_id" => address_hash_string,
"address_token_id" => token_hash_string,
"type" => "JSON"
} = params
) do ) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token_hash} <- Chain.string_to_address_hash(token_hash_string), {:ok, token_hash} <- Chain.string_to_address_hash(token_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash), {:ok, address} <- Chain.hash_to_address(address_hash),
{:ok, token} <- Chain.token_from_address_hash(token_hash) do {:ok, _} <- Chain.token_from_address_hash(token_hash) do
transactions = transactions =
Chain.address_to_transactions_with_token_transfers( Chain.address_to_transactions_with_token_transfers(
address_hash, address_hash,
@ -26,15 +32,58 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
{transactions_paginated, next_page} = split_list_by_page(transactions) {transactions_paginated, next_page} = split_list_by_page(transactions)
next_page_path =
case next_page_params(next_page, transactions_paginated, params) do
nil ->
nil
next_page_params ->
address_token_transfers_path(
conn,
:index,
address_hash_string,
token_hash_string,
Map.delete(next_page_params, "type")
)
end
transfers_json =
Enum.map(transactions_paginated, fn transaction ->
View.render_to_string(
TransactionView,
"_tile.html",
conn: conn,
transaction: transaction,
current_address: address
)
end)
json(conn, %{items: transfers_json, next_page_path: next_page_path})
else
:error ->
unprocessable_entity(conn)
{:error, :not_found} ->
not_found(conn)
end
end
def index(
conn,
%{"address_id" => address_hash_string, "address_token_id" => token_hash_string}
) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token_hash} <- Chain.string_to_address_hash(token_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash),
{:ok, token} <- Chain.token_from_address_hash(token_hash) do
render( render(
conn, conn,
"index.html", "index.html",
address: address, address: address,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
next_page_params: next_page_params(next_page, transactions_paginated, params), current_path: current_path(conn),
token: token, token: token,
transaction_count: transaction_count(address), transaction_count: transaction_count(address),
transactions: transactions_paginated,
validation_count: validation_count(address) validation_count: validation_count(address)
) )
else else

@ -7,7 +7,7 @@
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %> <%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
</div> </div>
<div class="card-body"> <div class="card-body" data-async-listing="<%= @current_path %>">
<div data-selector="channel-batching-message" style="display: none;"> <div data-selector="channel-batching-message" style="display: none;">
<div data-selector="reload-button" class="alert alert-info"> <div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More internal transactions have come in" %></a> <a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More internal transactions have come in" %></a>
@ -54,31 +54,33 @@
) %> ) %>
</div> </div>
</div> </div>
<h2 class="card-title"><%= gettext "Internal Transactions" %></h2> <h2 class="card-title"><%= gettext "Internal Transactions" %></h2>
<%= if Enum.count(@internal_transactions) > 0 do %> <button data-error-message class="alert alert-danger col-12 text-left" style="display: none;">
<span data-selector="internal-transactions-list"> <span href="#" class="alert-link"><%= gettext("Something went wrong, click to reload.") %></span>
<%= for internal_transaction <- @internal_transactions do %> </button>
<%= render BlockScoutWeb.InternalTransactionView, "_tile.html", current_address: @address, internal_transaction: internal_transaction %> <div data-empty-response-message style="display: none;">
<% end %>
</span>
<% else %>
<div class="tile tile-muted text-center"> <div class="tile tile-muted text-center">
<span data-selector="empty-internal-transactions-list"><%= gettext "There are no internal transactions for this address." %></span> <span data-selector="empty-internal-transactions-list"><%= gettext "There are no internal transactions for this address." %></span>
</div> </div>
<% end %> </div>
<div> <div data-loading-message class="tile tile-muted text-center mt-3">
<%= if @next_page_params do %> <span class="loading-spinner-small mr-2">
<%= link( <span class="loading-spinner-block-1"></span>
gettext("Older"), <span class="loading-spinner-block-2"></span>
class: "button button-secondary button-sm float-right mt-3", </span>
to: address_internal_transaction_path( <%= gettext("Loading") %>...
@conn, </div>
:index, <div data-items></div>
@address, <a href="#" class="button button-secondary button-small float-right mt-4" data-next-page-button style="display: none;">
@next_page_params <%= gettext("Older") %>
) </a>
) %> <div class="button button-secondary button-small float-right mt-4" data-loading-button style="display: none;">
<% end %> <span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading") %>...
</div> </div>
</div> </div>
</div> </div>

@ -7,41 +7,36 @@
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %> <%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
</div> </div>
<div class="card-body"> <div data-async-listing="<%= @current_path %>" class="card-body">
<h2 class="card-title"> <h2 class="card-title">
<span class="text-muted"><%= gettext "Tokens" %></span> / <%= token_name(@token) %> <span class="text-muted"><%= gettext "Tokens" %></span> / <%= token_name(@token) %>
</h2> </h2>
<div data-loading-message class="tile tile-muted text-center mt-3">
<%= if Enum.any?(@transactions) do %> <span class="loading-spinner-small mr-2">
<span data-selector="transactions-list"> <span class="loading-spinner-block-1"></span>
<%= for transaction <- @transactions do %> <span class="loading-spinner-block-2"></span>
<%= render(
BlockScoutWeb.TransactionView,
"_tile.html",
transaction: transaction,
current_address: @address
) %>
<% end %>
</span> </span>
<% else %> <%= gettext("Loading...") %>
<div class="tile tile-muted text-center"> </div>
<span><%= gettext "There are no token transfers for this address." %></span> <div data-empty-response-message class="tile tile-muted text-center" style="display: none;">
</div> <span><%= gettext "There are no token transfers for this address." %></span>
<% end %> </div>
<button data-error-message class="alert alert-danger col-12 text-left" style="display: none;">
<%= if @next_page_params do %> <span class="alert-link">
<%= link( <%= gettext "Something went wrong, click to reload." %>
gettext("Next"), </span>
class: "button button-secondary button-sm float-right mt-3", </button>
to: address_token_transfers_path( <div data-items></div>
@conn, <a data-next-page-button href="#" class="button button-secondary button-small float-right mt-4" style="display: none;">
:index, <%= gettext("Older") %>
@address.hash, </a>
@token.contract_address_hash, <div data-loading-button class="button button-secondary button-small float-right mt-4" style="display: none;">
@next_page_params <span class="loading-spinner-small mr-2">
) <span class="loading-spinner-block-1"></span>
) %> <span class="loading-spinner-block-2"></span>
<% end %> </span>
<%= gettext("Loading") %>...
</div>
</div> </div>
</div> </div>
</section> </section>

@ -43,7 +43,7 @@
<%= link( <%= link(
gettext( gettext(
"Block #%{number}", "Block #%{number}",
number: @transfer.transaction.block_number number: @transfer.block_number
), ),
class: "mr-2 mr-sm-0 text-muted", class: "mr-2 mr-sm-0 text-muted",
to: block_path(BlockScoutWeb.Endpoint, :show, @transfer.transaction.block_number) to: block_path(BlockScoutWeb.Endpoint, :show, @transfer.transaction.block_number)

@ -505,7 +505,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21 #: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address/_tabs.html.eex:81 #: lib/block_scout_web/templates/address/_tabs.html.eex:81
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:57 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
#: lib/block_scout_web/templates/address_validation/index.html.eex:24 #: lib/block_scout_web/templates/address_validation/index.html.eex:24
#: lib/block_scout_web/templates/address_validation/index.html.eex:75 #: lib/block_scout_web/templates/address_validation/index.html.eex:75
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
@ -610,7 +610,6 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:25 #: lib/block_scout_web/templates/address_token/index.html.eex:25
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:34
msgid "Next" msgid "Next"
msgstr "" msgstr ""
@ -638,7 +637,8 @@ msgid "OUT"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:31
#: lib/block_scout_web/templates/address_validation/index.html.eex:117 #: lib/block_scout_web/templates/address_validation/index.html.eex:117
#: lib/block_scout_web/templates/block/index.html.eex:30 #: lib/block_scout_web/templates/block/index.html.eex:30
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50 #: lib/block_scout_web/templates/block_transaction/index.html.eex:50
@ -815,7 +815,7 @@ msgid "There are no holders for this Token."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:66 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:64
msgid "There are no internal transactions for this address." msgid "There are no internal transactions for this address."
msgstr "" msgstr ""
@ -831,7 +831,7 @@ msgid "There are no logs for this transaction."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:22
msgid "There are no token transfers for this address." msgid "There are no token transfers for this address."
msgstr "" msgstr ""
@ -1187,6 +1187,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:17 #: lib/block_scout_web/templates/address_read_contract/index.html.eex:17
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
@ -1214,6 +1215,9 @@ msgid "GraphQL"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:83
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:38
#: lib/block_scout_web/templates/address_transaction/index.html.eex:61 #: lib/block_scout_web/templates/address_transaction/index.html.eex:61
#: lib/block_scout_web/templates/block/index.html.eex:22 #: lib/block_scout_web/templates/block/index.html.eex:22
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:33 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:33
@ -1409,6 +1413,8 @@ msgid "Log Data"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21
msgid "Something went wrong, click to reload." msgid "Something went wrong, click to reload."
msgstr "" msgstr ""

@ -505,7 +505,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21 #: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address/_tabs.html.eex:81 #: lib/block_scout_web/templates/address/_tabs.html.eex:81
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:57 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
#: lib/block_scout_web/templates/address_validation/index.html.eex:24 #: lib/block_scout_web/templates/address_validation/index.html.eex:24
#: lib/block_scout_web/templates/address_validation/index.html.eex:75 #: lib/block_scout_web/templates/address_validation/index.html.eex:75
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
@ -610,7 +610,6 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:25 #: lib/block_scout_web/templates/address_token/index.html.eex:25
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:34
msgid "Next" msgid "Next"
msgstr "" msgstr ""
@ -638,7 +637,8 @@ msgid "OUT"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:76
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:31
#: lib/block_scout_web/templates/address_validation/index.html.eex:117 #: lib/block_scout_web/templates/address_validation/index.html.eex:117
#: lib/block_scout_web/templates/block/index.html.eex:30 #: lib/block_scout_web/templates/block/index.html.eex:30
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50 #: lib/block_scout_web/templates/block_transaction/index.html.eex:50
@ -815,7 +815,7 @@ msgid "There are no holders for this Token."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:66 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:64
msgid "There are no internal transactions for this address." msgid "There are no internal transactions for this address."
msgstr "" msgstr ""
@ -831,7 +831,7 @@ msgid "There are no logs for this transaction."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:22
msgid "There are no token transfers for this address." msgid "There are no token transfers for this address."
msgstr "" msgstr ""
@ -1187,6 +1187,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:17 #: lib/block_scout_web/templates/address_read_contract/index.html.eex:17
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
@ -1214,6 +1215,9 @@ msgid "GraphQL"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:83
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:38
#: lib/block_scout_web/templates/address_transaction/index.html.eex:61 #: lib/block_scout_web/templates/address_transaction/index.html.eex:61
#: lib/block_scout_web/templates/block/index.html.eex:22 #: lib/block_scout_web/templates/block/index.html.eex:22
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:33 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:33
@ -1409,6 +1413,8 @@ msgid "Log Data"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21
msgid "Something went wrong, click to reload." msgid "Something went wrong, click to reload."
msgstr "" msgstr ""

@ -1,7 +1,8 @@
defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [address_internal_transaction_path: 3] import BlockScoutWeb.Router.Helpers,
only: [address_internal_transaction_path: 3, address_internal_transaction_path: 4]
alias Explorer.Chain.{Block, InternalTransaction, Transaction} alias Explorer.Chain.{Block, InternalTransaction, Transaction}
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
@ -21,6 +22,14 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
assert html_response(conn, 404) assert html_response(conn, 404)
end end
test "includes USD exchange rate value for address in assigns", %{conn: conn} do
address = insert(:address)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash))
assert %Token{} = conn.assigns.exchange_rate
end
test "returns internal transactions for the address", %{conn: conn} do test "returns internal transactions for the address", %{conn: conn} do
address = insert(:address) address = insert(:address)
@ -47,29 +56,101 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction.index transaction_index: transaction.index
) )
path = address_internal_transaction_path(conn, :index, address) path = address_internal_transaction_path(conn, :index, address, %{"type" => "JSON"})
conn = get(conn, path) conn = get(conn, path)
actual_internal_transaction_primary_keys = internal_transaction_tiles = json_response(conn, 200)["items"]
Enum.map(conn.assigns.internal_transactions, &{&1.transaction_hash, &1.index})
assert Enum.all?([from_internal_transaction, to_internal_transaction], fn internal_transaction ->
Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{internal_transaction.index}\"")
end)
end)
end
test "returns internal transactions coming from the address", %{conn: conn} do
address = insert(:address)
transaction =
:transaction
|> insert()
|> with_block(insert(:block, number: 1))
from_internal_transaction =
insert(:internal_transaction,
transaction: transaction,
from_address: address,
index: 1,
block_number: transaction.block_number,
transaction_index: transaction.index
)
to_internal_transaction =
insert(:internal_transaction,
transaction: transaction,
to_address: address,
index: 2,
block_number: transaction.block_number,
transaction_index: transaction.index
)
path = address_internal_transaction_path(conn, :index, address, %{"filter" => "from", "type" => "JSON"})
conn = get(conn, path)
assert Enum.member?( internal_transaction_tiles = json_response(conn, 200)["items"]
actual_internal_transaction_primary_keys,
{from_internal_transaction.transaction_hash, from_internal_transaction.index}
)
assert Enum.member?( assert Enum.any?(internal_transaction_tiles, fn tile ->
actual_internal_transaction_primary_keys, String.contains?(tile, to_string(from_internal_transaction.transaction_hash)) &&
{to_internal_transaction.transaction_hash, to_internal_transaction.index} String.contains?(tile, "data-internal-transaction-index=\"#{from_internal_transaction.index}\"")
) end)
refute Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(to_internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{to_internal_transaction.index}\"")
end)
end end
test "includes USD exchange rate value for address in assigns", %{conn: conn} do test "returns internal transactions going to the address", %{conn: conn} do
address = insert(:address) address = insert(:address)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash)) transaction =
:transaction
|> insert()
|> with_block(insert(:block, number: 1))
assert %Token{} = conn.assigns.exchange_rate from_internal_transaction =
insert(:internal_transaction,
transaction: transaction,
from_address: address,
index: 1,
block_number: transaction.block_number,
transaction_index: transaction.index
)
to_internal_transaction =
insert(:internal_transaction,
transaction: transaction,
to_address: address,
index: 2,
block_number: transaction.block_number,
transaction_index: transaction.index
)
path = address_internal_transaction_path(conn, :index, address, %{"filter" => "to", "type" => "JSON"})
conn = get(conn, path)
internal_transaction_tiles = json_response(conn, 200)["items"]
assert Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(to_internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{to_internal_transaction.index}\"")
end)
refute Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(from_internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{from_internal_transaction.index}\"")
end)
end end
test "returns next page of results based on last seen internal transaction", %{conn: conn} do test "returns next page of results based on last seen internal transaction", %{conn: conn} do
@ -105,7 +186,6 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction_1.index transaction_index: transaction_1.index
) )
end) end)
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
transaction_2_hashes = transaction_2_hashes =
1..20 1..20
@ -119,7 +199,6 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction_2.index transaction_index: transaction_2.index
) )
end) end)
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
transaction_3_hashes = transaction_3_hashes =
1..10 1..10
@ -133,9 +212,8 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
transaction_index: transaction_3.index transaction_index: transaction_3.index
) )
end) end)
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
second_page_hashes = transaction_1_hashes ++ transaction_2_hashes ++ transaction_3_hashes second_page = transaction_1_hashes ++ transaction_2_hashes ++ transaction_3_hashes
%InternalTransaction{index: index} = %InternalTransaction{index: index} =
insert( insert(
@ -151,15 +229,18 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash), %{ get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash), %{
"block_number" => Integer.to_string(b_block.number), "block_number" => Integer.to_string(b_block.number),
"transaction_index" => Integer.to_string(transaction_3.index), "transaction_index" => Integer.to_string(transaction_3.index),
"index" => Integer.to_string(index) "index" => Integer.to_string(index),
"type" => "JSON"
}) })
actual_hashes = internal_transaction_tiles = json_response(conn, 200)["items"]
conn.assigns.internal_transactions
|> Enum.map(&"#{&1.transaction_hash}.#{&1.index}")
|> Enum.reverse()
assert second_page_hashes == actual_hashes assert Enum.all?(second_page, fn internal_transaction ->
Enum.any?(internal_transaction_tiles, fn tile ->
String.contains?(tile, to_string(internal_transaction.transaction_hash)) &&
String.contains?(tile, "data-internal-transaction-index=\"#{internal_transaction.index}\"")
end)
end)
end end
test "next_page_params exist if not on last page", %{conn: conn} do test "next_page_params exist if not on last page", %{conn: conn} do
@ -184,10 +265,17 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
) )
end) end)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash)) conn =
get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{"type" => "JSON"}))
assert %{"block_number" => ^number, "index" => 11, "transaction_index" => ^transaction_index} = expected_response =
conn.assigns.next_page_params address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{
"block_number" => number,
"index" => 11,
"transaction_index" => transaction_index
})
assert expected_response == json_response(conn, 200)["next_page_path"]
end end
test "next_page_params are empty if on last page", %{conn: conn} do test "next_page_params are empty if on last page", %{conn: conn} do
@ -208,9 +296,10 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
) )
end) end)
conn = get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash)) conn =
get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{"type" => "JSON"}))
refute conn.assigns.next_page_params assert %{"next_page_path" => nil} = json_response(conn, 200)
end end
end end
end end

@ -1,7 +1,8 @@
defmodule BlockScoutWeb.AddressTokenTransferControllerTest do defmodule BlockScoutWeb.AddressTokenTransferControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [address_token_transfers_path: 4] import BlockScoutWeb.Router.Helpers,
only: [address_token_transfers_path: 4, address_token_transfers_path: 5]
alias Explorer.Chain.{Address, Token} alias Explorer.Chain.{Address, Token}
@ -15,6 +16,7 @@ defmodule BlockScoutWeb.AddressTokenTransferControllerTest do
test "with invalid token hash", %{conn: conn} do test "with invalid token hash", %{conn: conn} do
address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
conn = get(conn, address_token_transfers_path(conn, :index, address_hash, "invalid_address")) conn = get(conn, address_token_transfers_path(conn, :index, address_hash, "invalid_address"))
assert html_response(conn, 422) assert html_response(conn, 422)
@ -35,25 +37,80 @@ defmodule BlockScoutWeb.AddressTokenTransferControllerTest do
assert html_response(conn, 404) assert html_response(conn, 404)
end end
end
describe "GET index/2 JSON" do
test "without token transfers for a token", %{conn: conn} do test "without token transfers for a token", %{conn: conn} do
%Address{hash: address_hash} = insert(:address) %Address{hash: address_hash} = insert(:address)
%Token{contract_address_hash: token_hash} = insert(:token) %Token{contract_address_hash: token_hash} = insert(:token)
conn = get(conn, address_token_transfers_path(conn, :index, address_hash, token_hash)) conn =
get(conn, address_token_transfers_path(conn, :index, address_hash, token_hash), %{
type: "JSON"
})
assert json_response(conn, 200) == %{"items" => [], "next_page_path" => nil}
end
test "returns the correct number of transactions", %{conn: conn} do
address = insert(:address)
token = insert(:token)
inserted_transactions =
Enum.map(1..5, fn index ->
block = insert(:block, number: 1000 - index)
transaction =
:transaction
|> insert()
|> with_block(block)
insert(
:token_transfer,
to_address: address,
transaction: transaction,
token_contract_address: token.contract_address
)
transaction
end)
assert html_response(conn, 200) conn =
assert conn.assigns.transactions == [] get(
conn,
address_token_transfers_path(conn, :index, address.hash, token.contract_address_hash),
%{type: "JSON"}
)
response_items =
conn
|> json_response(200)
|> Map.get("items")
items_length = length(response_items)
assert items_length == 5
assert Enum.all?(inserted_transactions, fn transaction ->
Enum.any?(response_items, fn item ->
String.contains?(
item,
"data-transaction-hash=\"#{to_string(transaction.hash)}\""
)
end)
end)
end end
test "returns the transactions that have token transfers for the given address and token", %{conn: conn} do test "returns next_page_path as null when there are no more pages", %{conn: conn} do
address = insert(:address) address = insert(:address)
token = insert(:token) token = insert(:token)
block = insert(:block, number: 1000)
transaction = transaction =
:transaction :transaction
|> insert() |> insert()
|> with_block() |> with_block(block)
insert( insert(
:token_transfer, :token_transfer,
@ -62,19 +119,21 @@ defmodule BlockScoutWeb.AddressTokenTransferControllerTest do
token_contract_address: token.contract_address token_contract_address: token.contract_address
) )
conn = get(conn, address_token_transfers_path(conn, :index, address.hash, token.contract_address_hash)) conn =
get(
transaction_hashes = Enum.map(conn.assigns.transactions, & &1.hash) conn,
address_token_transfers_path(conn, :index, address.hash, token.contract_address_hash),
%{type: "JSON"}
)
assert html_response(conn, 200) assert Map.get(json_response(conn, 200), "next_page_path") == nil
assert transaction_hashes == [transaction.hash]
end end
test "returns next page of results based on last seen transactions", %{conn: conn} do test "returns next_page_path when there are more items", %{conn: conn} do
address = insert(:address) address = insert(:address)
token = insert(:token) token = insert(:token)
second_page_transactions = page_last_transfer =
1..50 1..50
|> Enum.map(fn index -> |> Enum.map(fn index ->
block = insert(:block, number: 1000 - index) block = insert(:block, number: 1000 - index)
@ -93,22 +152,84 @@ defmodule BlockScoutWeb.AddressTokenTransferControllerTest do
transaction transaction
end) end)
|> Enum.map(& &1.hash) |> List.last()
transaction = Enum.each(51..60, fn index ->
:transaction block = insert(:block, number: 1000 - index)
|> insert()
|> with_block(insert(:block, number: 1002)) transaction =
:transaction
|> insert()
|> with_block(block)
insert(
:token_transfer,
to_address: address,
transaction: transaction,
token_contract_address: token.contract_address
)
end)
conn = conn =
get(conn, address_token_transfers_path(conn, :index, address.hash, token.contract_address_hash), %{ get(
"block_number" => Integer.to_string(transaction.block_number), conn,
"index" => Integer.to_string(transaction.index) address_token_transfers_path(conn, :index, address.hash, token.contract_address_hash),
%{type: "JSON"}
)
expected_path =
address_token_transfers_path(conn, :index, address.hash, token.contract_address_hash, %{
block_number: page_last_transfer.block_number,
index: page_last_transfer.index
}) })
actual_transactions = Enum.map(conn.assigns.transactions, & &1.hash) assert Map.get(json_response(conn, 200), "next_page_path") == expected_path
end
test "with invalid address hash", %{conn: conn} do
token_hash = "0xc8982771dd50285389c352c175ada74d074427c7"
conn =
get(conn, address_token_transfers_path(conn, :index, "invalid_address", token_hash), %{
type: "JSON"
})
assert html_response(conn, 422)
end
test "with invalid token hash", %{conn: conn} do
address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
conn =
get(conn, address_token_transfers_path(conn, :index, address_hash, "invalid_address"), %{
type: "JSON"
})
assert html_response(conn, 422)
end
test "with an address that doesn't exist in our database", %{conn: conn} do
address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
%Token{contract_address_hash: token_hash} = insert(:token)
conn =
get(conn, address_token_transfers_path(conn, :index, address_hash, token_hash), %{
type: "JSON"
})
assert html_response(conn, 404)
end
test "with a token that doesn't exist in our database", %{conn: conn} do
%Address{hash: address_hash} = insert(:address)
token_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
assert second_page_transactions == actual_transactions conn =
get(conn, address_token_transfers_path(conn, :index, address_hash, token_hash), %{
type: "JSON"
})
assert html_response(conn, 404)
end end
end end
end end

@ -82,25 +82,21 @@ defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do
|> insert() |> insert()
|> with_block() |> with_block()
{:ok, first_transfer_time} = NaiveDateTime.new(2000, 1, 1, 0, 0, 5) token_transfer = insert(:token_transfer, transaction: transaction, block_number: 1000, log_index: 1)
{:ok, remaining_transfers_time} = NaiveDateTime.new(1999, 1, 1, 0, 0, 0)
insert(:token_transfer, transaction: transaction, inserted_at: first_transfer_time)
1..5 Enum.each(2..5, fn item ->
|> Enum.each(fn log_index -> insert(:token_transfer, transaction: transaction, block_number: item + 1001, log_index: item + 1)
insert(:token_transfer, transaction: transaction, inserted_at: remaining_transfers_time, log_index: log_index)
end) end)
conn = conn =
get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{ get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{
"inserted_at" => first_transfer_time |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601() "block_number" => "1000",
"index" => "1"
}) })
actual_times = actual_log_indexes = Enum.map(conn.assigns.token_transfers, & &1.log_index)
conn.assigns.token_transfers
|> Enum.map(& &1.inserted_at)
refute Enum.any?(actual_times, fn time -> first_transfer_time == time end) refute Enum.any?(actual_log_indexes, fn log_index -> log_index == token_transfer.log_index end)
end end
test "next_page_params exist if not on last page", %{conn: conn} do test "next_page_params exist if not on last page", %{conn: conn} do

@ -147,35 +147,6 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
|> assert_has(AddressPage.transaction(transactions.from_taft)) |> assert_has(AddressPage.transaction(transactions.from_taft))
end end
test "contract creation is shown for to_address on list page", %{
addresses: addresses,
block: block,
session: session
} do
lincoln = addresses.lincoln
contract_address = insert(:contract_address)
from_lincoln =
:transaction
|> insert(from_address: lincoln, to_address: nil)
|> with_contract_creation(contract_address)
|> with_block(block)
internal_transaction =
:internal_transaction_create
|> insert(
transaction: from_lincoln,
from_address: lincoln,
index: 1
)
|> with_contract_creation(contract_address)
session
|> AddressPage.visit_page(addresses.lincoln)
|> assert_has(AddressPage.contract_creation(internal_transaction))
end
test "only addresses not matching the page are links", %{ test "only addresses not matching the page are links", %{
addresses: addresses, addresses: addresses,
session: session, session: session,
@ -213,29 +184,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
{:ok, %{internal_transaction_lincoln_to_address: internal_transaction_lincoln_to_address}} {:ok, %{internal_transaction_lincoln_to_address: internal_transaction_lincoln_to_address}}
end end
test "can see internal transactions for an address", %{addresses: addresses, session: session} do @tag :skip
session
|> AddressPage.visit_page(addresses.lincoln)
|> AddressPage.click_internal_transactions()
|> assert_has(AddressPage.internal_transactions(count: 2))
end
test "can filter to only see internal transactions from an address", %{addresses: addresses, session: session} do
session
|> AddressPage.visit_page(addresses.lincoln)
|> AddressPage.click_internal_transactions()
|> AddressPage.apply_filter("From")
|> assert_has(AddressPage.internal_transactions(count: 1))
end
test "can filter to only see internal transactions to an address", %{addresses: addresses, session: session} do
session
|> AddressPage.visit_page(addresses.lincoln)
|> AddressPage.click_internal_transactions()
|> AddressPage.apply_filter("To")
|> assert_has(AddressPage.internal_transactions(count: 1))
end
test "only addresses not matching the page are links", %{ test "only addresses not matching the page are links", %{
addresses: addresses, addresses: addresses,
internal_transaction_lincoln_to_address: internal_transaction, internal_transaction_lincoln_to_address: internal_transaction,
@ -248,6 +197,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
|> refute_has(AddressPage.internal_transaction_address_link(internal_transaction, :to)) |> refute_has(AddressPage.internal_transaction_address_link(internal_transaction, :to))
end end
@tag :skip
test "viewing new internal transactions via live update", %{addresses: addresses, session: session} do test "viewing new internal transactions via live update", %{addresses: addresses, session: session} do
transaction = transaction =
:transaction :transaction
@ -276,37 +226,6 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
end end
end end
test "contract creation is shown for to_address on list page", %{
addresses: addresses,
block: block,
session: session
} do
lincoln = addresses.lincoln
contract_address = insert(:contract_address)
from_lincoln =
:transaction
|> insert(from_address: lincoln, to_address: nil)
|> with_block(block)
|> with_contract_creation(contract_address)
internal_transaction =
:internal_transaction_create
|> insert(
transaction: from_lincoln,
from_address: lincoln,
index: 1,
block_number: from_lincoln.block_number,
transaction_index: from_lincoln.index
)
|> with_contract_creation(contract_address)
session
|> AddressPage.visit_page(addresses.lincoln)
|> AddressPage.click_internal_transactions()
|> assert_has(AddressPage.contract_creation(internal_transaction))
end
describe "viewing token transfers" do describe "viewing token transfers" do
test "contributor can see all token transfers that he sent", %{ test "contributor can see all token transfers that he sent", %{
addresses: addresses, addresses: addresses,
@ -445,6 +364,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
end end
describe "viewing token transfers from a specific token" do describe "viewing token transfers from a specific token" do
@tag :skip
test "list token transfers related to the address", %{ test "list token transfers related to the address", %{
addresses: addresses, addresses: addresses,
block: block, block: block,

@ -9,8 +9,8 @@ config:
```elixir ```elixir
config :ethereum_jsonrpc, config :ethereum_jsonrpc,
url: "https://sokol.poa.network", url: "http://localhost:8545",
trace_url: "https://sokol-trace.poa.network", trace_url: "http://localhost:8545",
http: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]] http: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]]
``` ```
@ -44,8 +44,8 @@ mix test --exclude no_parity
| Protocol | URL | | Protocol | URL |
|:----------|:-----------------------------------| |:----------|:-----------------------------------|
| HTTP | `https://sokol-trace.poa.network` | | HTTP | `http://localhost:8545` |
| WebSocket | `wss://sokol-ws.poa.network/ws` | | WebSocket | `ws://localhost:8546` |
### Geth ### Geth

@ -7,8 +7,8 @@ defmodule EthereumJSONRPC do
Configuration for parity URLs can be provided with the following mix config: Configuration for parity URLs can be provided with the following mix config:
config :ethereum_jsonrpc, config :ethereum_jsonrpc,
url: "https://sokol.poa.network", url: "http://localhost:8545",
trace_url: "https://sokol-trace.poa.network", trace_url: "http://localhost:8545",
http: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]] http: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]]

@ -12,7 +12,7 @@ defmodule EthereumJSONRPC.Case.Parity.HTTPWebSocket do
transport_options: [ transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison, http: EthereumJSONRPC.HTTP.HTTPoison,
http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]], http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]],
url: "https://sokol-trace.poa.network" url: "http://18.207.247.30:8545"
], ],
variant: EthereumJSONRPC.Parity variant: EthereumJSONRPC.Parity
) )

@ -6,7 +6,7 @@ defmodule EthereumJSONRPC.WebSocket.Case.Parity do
import ExUnit.Callbacks, only: [start_supervised!: 1] import ExUnit.Callbacks, only: [start_supervised!: 1]
def setup do def setup do
url = "wss://sokol-ws.poa.network/ws" url = "ws://18.207.247.30:8546"
web_socket_module = EthereumJSONRPC.WebSocket.WebSocketClient web_socket_module = EthereumJSONRPC.WebSocket.WebSocketClient
web_socket = start_supervised!({web_socket_module, [url, []]}) web_socket = start_supervised!({web_socket_module, [url, []]})

@ -22,7 +22,7 @@ import_config "dev.secret.exs"
variant = variant =
if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do
"parity" "ganache"
else else
System.get_env("ETHEREUM_JSONRPC_VARIANT") System.get_env("ETHEREUM_JSONRPC_VARIANT")
|> String.split(".") |> String.split(".")

@ -5,11 +5,11 @@ config :explorer,
transport: EthereumJSONRPC.HTTP, transport: EthereumJSONRPC.HTTP,
transport_options: [ transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison, http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "https://sokol.poa.network", url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
method_to_url: [ method_to_url: [
eth_call: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network", eth_call: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network", eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network" trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545"
], ],
http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]] http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]]
], ],
@ -19,7 +19,7 @@ config :explorer,
transport: EthereumJSONRPC.WebSocket, transport: EthereumJSONRPC.WebSocket,
transport_options: [ transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL") || "wss://sokol-ws.poa.network/ws" url: System.get_env("ETHEREUM_JSONRPC_WS_URL") || "ws://localhost:8546"
], ],
variant: EthereumJSONRPC.Parity variant: EthereumJSONRPC.Parity
] ]

@ -5,11 +5,11 @@ config :explorer,
transport: EthereumJSONRPC.HTTP, transport: EthereumJSONRPC.HTTP,
transport_options: [ transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison, http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "https://sokol.poa.network", url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"),
method_to_url: [ method_to_url: [
eth_call: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network", eth_call: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network", eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network" trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL")
], ],
http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]] http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]]
], ],
@ -19,7 +19,7 @@ config :explorer,
transport: EthereumJSONRPC.WebSocket, transport: EthereumJSONRPC.WebSocket,
transport_options: [ transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL") || "wss://sokol-ws.poa.network/ws" url: System.get_env("ETHEREUM_JSONRPC_WS_URL")
], ],
variant: EthereumJSONRPC.Parity variant: EthereumJSONRPC.Parity
] ]

@ -899,12 +899,9 @@ defmodule Explorer.Chain do
def list_top_addresses do def list_top_addresses do
query = query =
from(a in Address, from(a in Address,
left_join: t in Transaction,
on: a.hash == t.from_address_hash,
where: a.fetched_coin_balance > ^0, where: a.fetched_coin_balance > ^0,
group_by: [a.hash, a.fetched_coin_balance],
order_by: [desc: a.fetched_coin_balance, asc: a.hash], order_by: [desc: a.fetched_coin_balance, asc: a.hash],
select: {a, fragment("coalesce(1 + max(?), 0)", t.nonce)}, select: {a, fragment("coalesce(1 + ?, 0)", a.nonce)},
limit: 250 limit: 250
) )

@ -8,7 +8,7 @@ defmodule Explorer.Chain.Address do
alias Ecto.Changeset alias Ecto.Changeset
alias Explorer.Chain.{Address, Block, Data, Hash, InternalTransaction, SmartContract, Token, Wei} alias Explorer.Chain.{Address, Block, Data, Hash, InternalTransaction, SmartContract, Token, Wei}
@optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number)a @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce)a
@required_attrs ~w(hash)a @required_attrs ~w(hash)a
@allowed_attrs @optional_attrs ++ @required_attrs @allowed_attrs @optional_attrs ++ @required_attrs
@ -34,7 +34,8 @@ defmodule Explorer.Chain.Address do
contract_code: Data.t() | nil, contract_code: Data.t() | nil,
names: %Ecto.Association.NotLoaded{} | [Address.Name.t()], names: %Ecto.Association.NotLoaded{} | [Address.Name.t()],
inserted_at: DateTime.t(), inserted_at: DateTime.t(),
updated_at: DateTime.t() updated_at: DateTime.t(),
nonce: non_neg_integer() | nil
} }
@primary_key {:hash, Hash.Address, autogenerate: false} @primary_key {:hash, Hash.Address, autogenerate: false}
@ -42,6 +43,7 @@ defmodule Explorer.Chain.Address do
field(:fetched_coin_balance, Wei) field(:fetched_coin_balance, Wei)
field(:fetched_coin_balance_block_number, :integer) field(:fetched_coin_balance_block_number, :integer)
field(:contract_code, Data) field(:contract_code, Data)
field(:nonce, :integer)
has_one(:smart_contract, SmartContract) has_one(:smart_contract, SmartContract)
has_one(:token, Token, foreign_key: :contract_address_hash) has_one(:token, Token, foreign_key: :contract_address_hash)

@ -95,18 +95,10 @@ defmodule Explorer.Chain.Import.Addresses do
# MAX on two columns # MAX on two columns
fetched_coin_balance_block_number: fetched_coin_balance_block_number:
fragment( fragment(
""" "GREATEST(EXCLUDED.fetched_coin_balance_block_number, ?)",
CASE WHEN EXCLUDED.fetched_coin_balance_block_number IS NOT NULL AND
(? IS NULL OR
EXCLUDED.fetched_coin_balance_block_number >= ?) THEN
EXCLUDED.fetched_coin_balance_block_number
ELSE ?
END
""",
address.fetched_coin_balance_block_number,
address.fetched_coin_balance_block_number,
address.fetched_coin_balance_block_number address.fetched_coin_balance_block_number
) ),
nonce: fragment("GREATEST(EXCLUDED.nonce, ?)", address.nonce)
] ]
] ]
) )

@ -24,15 +24,17 @@ defmodule Explorer.Chain.TokenTransfer do
use Ecto.Schema use Ecto.Schema
import Ecto.{Changeset, Query} import Ecto.Changeset
import Ecto.Query, only: [from: 2, dynamic: 2, limit: 2, where: 3]
alias Explorer.Chain.{Address, Block, Hash, Token, TokenTransfer, Transaction} alias Explorer.Chain.{Address, Hash, Token, TokenTransfer, Transaction}
alias Explorer.{PagingOptions, Repo} alias Explorer.{PagingOptions, Repo}
@default_paging_options %PagingOptions{page_size: 50} @default_paging_options %PagingOptions{page_size: 50}
@typedoc """ @typedoc """
* `:amount` - The token transferred amount * `:amount` - The token transferred amount
* `:block_number` - The block number that the transfer took place.
* `:from_address` - The `t:Explorer.Chain.Address.t/0` that sent the tokens * `:from_address` - The `t:Explorer.Chain.Address.t/0` that sent the tokens
* `:from_address_hash` - Address hash foreign key * `:from_address_hash` - Address hash foreign key
* `:to_address` - The `t:Explorer.Chain.Address.t/0` that received the tokens * `:to_address` - The `t:Explorer.Chain.Address.t/0` that received the tokens
@ -46,6 +48,7 @@ defmodule Explorer.Chain.TokenTransfer do
""" """
@type t :: %TokenTransfer{ @type t :: %TokenTransfer{
amount: Decimal.t(), amount: Decimal.t(),
block_number: non_neg_integer() | nil,
from_address: %Ecto.Association.NotLoaded{} | Address.t(), from_address: %Ecto.Association.NotLoaded{} | Address.t(),
from_address_hash: Hash.Address.t(), from_address_hash: Hash.Address.t(),
to_address: %Ecto.Association.NotLoaded{} | Address.t(), to_address: %Ecto.Association.NotLoaded{} | Address.t(),
@ -65,6 +68,7 @@ defmodule Explorer.Chain.TokenTransfer do
@primary_key false @primary_key false
schema "token_transfers" do schema "token_transfers" do
field(:amount, :decimal) field(:amount, :decimal)
field(:block_number, :integer)
field(:log_index, :integer, primary_key: true) field(:log_index, :integer, primary_key: true)
field(:token_id, :decimal) field(:token_id, :decimal)
@ -91,7 +95,7 @@ defmodule Explorer.Chain.TokenTransfer do
timestamps() timestamps()
end end
@required_attrs ~w(log_index from_address_hash to_address_hash token_contract_address_hash transaction_hash)a @required_attrs ~w(block_number log_index from_address_hash to_address_hash token_contract_address_hash transaction_hash)a
@optional_attrs ~w(amount token_id)a @optional_attrs ~w(amount token_id)a
@doc false @doc false
@ -118,13 +122,9 @@ defmodule Explorer.Chain.TokenTransfer do
query = query =
from( from(
tt in TokenTransfer, tt in TokenTransfer,
join: t in Transaction,
on: tt.transaction_hash == t.hash,
join: b in Block,
on: t.block_hash == b.hash,
where: tt.token_contract_address_hash == ^token_address_hash, where: tt.token_contract_address_hash == ^token_address_hash,
preload: [{:transaction, :block}, :token, :from_address, :to_address], preload: [{:transaction, :block}, :token, :from_address, :to_address],
order_by: [desc: b.timestamp] order_by: [desc: tt.block_number, desc: tt.log_index]
) )
query query
@ -135,19 +135,11 @@ defmodule Explorer.Chain.TokenTransfer do
def page_token_transfer(query, %PagingOptions{key: nil}), do: query def page_token_transfer(query, %PagingOptions{key: nil}), do: query
def page_token_transfer(query, %PagingOptions{key: {token_id}}) do def page_token_transfer(query, %PagingOptions{key: {block_number, log_index}}) do
where( where(
query, query,
[token_transfer], [tt],
token_transfer.token_id > ^token_id tt.block_number < ^block_number or (tt.block_number == ^block_number and tt.log_index < ^log_index)
)
end
def page_token_transfer(query, %PagingOptions{key: inserted_at}) do
where(
query,
[token_transfer],
token_transfer.inserted_at < ^inserted_at
) )
end end

@ -0,0 +1,26 @@
defmodule Explorer.Repo.Migrations.AddNonceToAddresses do
@moduledoc """
Use `priv/repo/migrations/scripts/20181126182700_migrate_address_nonce.sql` to migrate data.
```sh
mix ecto.migrate
psql -d $DATABASE -a -f priv/repo/migrations/scripts/20181126182700_migrate_address_nonce.sql
```
"""
use Ecto.Migration
def up do
# Add nonce
alter table(:addresses) do
add(:nonce, :integer)
end
end
def down do
# Remove nonce
alter table(:addresses) do
remove(:nonce)
end
end
end

@ -0,0 +1,18 @@
defmodule Explorer.Repo.Migrations.AddBlockNumberToTokenTransfers do
@moduledoc """
Use `priv/repo/migrations/scripts/20181121170616_token_transfers_update_block_number_in_batches.sql` to migrate data.
```sh
mix ecto.migrate
psql -d $DATABASE -a -f priv/repo/migrations/scripts/20181121170616_token_transfers_update_block_number_in_batches.sql
```
"""
use Ecto.Migration
def change do
alter table(:token_transfers) do
add(:block_number, :integer)
end
end
end

@ -0,0 +1,13 @@
defmodule Explorer.Repo.Migrations.AddIndexToAddresses do
use Ecto.Migration
def up do
execute(
"CREATE INDEX addresses_fetched_coin_balance_hash_index ON addresses (fetched_coin_balance DESC, hash ASC) WHERE fetched_coin_balance > 0"
)
end
def down do
execute("DROP INDEX addresses_fetched_coin_balance_hash_index")
end
end

@ -0,0 +1,39 @@
DO $$
DECLARE
row_count integer;
batch_size integer := 100000; -- HOW MANY ITEMS WILL BE UPDATED AT TIME
affected integer;
BEGIN
RAISE NOTICE 'Counting items to be updated';
row_count := (SELECT COUNT(*) FROM token_transfers WHERE block_number IS NULL);
RAISE NOTICE '% items', row_count;
WHILE row_count > 0 LOOP
WITH cte AS (
SELECT
t.hash,
t.block_number
FROM token_transfers AS tt
INNER JOIN transactions AS t ON t.hash = tt.transaction_hash
WHERE tt.block_number IS NULL
LIMIT batch_size
)
UPDATE token_transfers
SET
block_number = cte.block_number
FROM cte
WHERE token_transfers.transaction_hash = cte.hash;
GET DIAGNOSTICS affected = ROW_COUNT;
RAISE NOTICE '-> % token transfers updated!', affected;
-- UPDATES THE COUNTER SO IT DOESN'T TURN INTO AN INFINITE LOOP
row_count := row_count - batch_size;
RAISE NOTICE '-> % items missing to update', row_count;
CHECKPOINT; -- COMMITS THE BATCH UPDATES
END LOOP;
END $$;

@ -0,0 +1,55 @@
DO $$
DECLARE
row_count integer := 1;
batch_size integer := 50000; -- HOW MANY ITEMS WILL BE UPDATED AT TIME
iterator integer := batch_size;
affected integer;
BEGIN
DROP TABLE IF EXISTS addresses_nonce_temp;
-- CREATES TEMP TABLE TO STORE THE ADDRESS NONCE TO BE UPDATED
CREATE TEMP TABLE addresses_nonce_temp(
from_address_hash bytea,
nonce integer,
row_number integer
);
INSERT INTO addresses_nonce_temp
SELECT DISTINCT ON (from_address_hash)
from_address_hash,
nonce,
ROW_NUMBER () OVER ()
FROM transactions
ORDER BY from_address_hash, nonce DESC;
row_count := (SELECT count(*) FROM addresses_nonce_temp);
RAISE NOTICE '% items to be updated', row_count;
-- ITERATES THROUGH THE ITEMS UNTIL THE TEMP TABLE IS EMPTY
WHILE row_count > 0 LOOP
-- UPDATES THE ADDRESS NONCE AND RETURNS THE ADDRESS_HASH
WITH updated_addresses AS (
UPDATE addresses SET nonce = addresses_nonce_temp.nonce
FROM addresses_nonce_temp
WHERE addresses_nonce_temp.from_address_hash = addresses.hash
AND addresses_nonce_temp.row_number <= iterator
RETURNING addresses_nonce_temp.from_address_hash
)
DELETE FROM addresses_nonce_temp
WHERE (from_address_hash) IN (select from_address_hash from updated_addresses);
GET DIAGNOSTICS affected = ROW_COUNT;
RAISE NOTICE '-> % addresses updated!', affected;
-- COMMITS THE BATCH UPDATES
CHECKPOINT;
-- UPDATES THE COUNTER SO IT DOESN'T TURN INTO A INFINITE LOOP
row_count := (SELECT count(*) FROM addresses_nonce_temp);
iterator := iterator + batch_size;
RAISE NOTICE '-> % counter', row_count;
RAISE NOTICE '-> % next batch', iterator;
END LOOP;
END $$;

@ -1517,7 +1517,9 @@ defmodule Explorer.Chain.ImportTest do
}, },
token_transfers: %{ token_transfers: %{
params: [ params: [
params_for(:token_transfer, params_for(
:token_transfer,
block_number: 35,
from_address_hash: from_address_hash, from_address_hash: from_address_hash,
to_address_hash: to_address_hash, to_address_hash: to_address_hash,
token_contract_address_hash: token_contract_address_hash, token_contract_address_hash: token_contract_address_hash,

@ -8,7 +8,7 @@ defmodule Explorer.Chain.TokenTransferTest do
doctest Explorer.Chain.TokenTransfer doctest Explorer.Chain.TokenTransfer
describe "fetch_token_transfers/2" do describe "fetch_token_transfers_from_token_hash/2" do
test "returns token transfers for the given address" do test "returns token transfers for the given address" do
token_contract_address = insert(:contract_address) token_contract_address = insert(:contract_address)
@ -87,6 +87,7 @@ defmodule Explorer.Chain.TokenTransferTest do
second_page = second_page =
insert( insert(
:token_transfer, :token_transfer,
block_number: 999,
to_address: build(:address), to_address: build(:address),
transaction: transaction, transaction: transaction,
token_contract_address: token_contract_address, token_contract_address: token_contract_address,
@ -96,23 +97,53 @@ defmodule Explorer.Chain.TokenTransferTest do
first_page = first_page =
insert( insert(
:token_transfer, :token_transfer,
block_number: 1000,
to_address: build(:address), to_address: build(:address),
transaction: transaction, transaction: transaction,
token_contract_address: token_contract_address, token_contract_address: token_contract_address,
token: token token: token
) )
paging_options = %PagingOptions{key: first_page.inserted_at, page_size: 1} paging_options = %PagingOptions{key: {first_page.block_number, first_page.log_index}, page_size: 1}
token_transfers_primary_keys_paginated = token_transfers_primary_keys_paginated =
TokenTransfer.fetch_token_transfers_from_token_hash( token_contract_address.hash
token_contract_address.hash, |> TokenTransfer.fetch_token_transfers_from_token_hash(paging_options: paging_options)
paging_options: paging_options
)
|> Enum.map(&{&1.transaction_hash, &1.log_index}) |> Enum.map(&{&1.transaction_hash, &1.log_index})
assert token_transfers_primary_keys_paginated == [{second_page.transaction_hash, second_page.log_index}] assert token_transfers_primary_keys_paginated == [{second_page.transaction_hash, second_page.log_index}]
end end
test "paginates considering the log_index when there are repeated block numbers" do
token_contract_address = insert(:contract_address)
transaction =
:transaction
|> insert()
|> with_block()
token = insert(:token)
token_transfer =
insert(
:token_transfer,
block_number: 1000,
log_index: 0,
to_address: build(:address),
transaction: transaction,
token_contract_address: token_contract_address,
token: token
)
paging_options = %PagingOptions{key: {token_transfer.block_number, token_transfer.log_index + 1}, page_size: 1}
token_transfers_primary_keys_paginated =
token_contract_address.hash
|> TokenTransfer.fetch_token_transfers_from_token_hash(paging_options: paging_options)
|> Enum.map(&{&1.transaction_hash, &1.log_index})
assert token_transfers_primary_keys_paginated == [{token_transfer.transaction_hash, token_transfer.log_index}]
end
end end
describe "count_token_transfers/0" do describe "count_token_transfers/0" do

@ -3112,6 +3112,7 @@ defmodule Explorer.ChainTest do
first_page = first_page =
insert( insert(
:token_transfer, :token_transfer,
block_number: 1000,
to_address: build(:address), to_address: build(:address),
transaction: transaction, transaction: transaction,
token_contract_address: token_contract_address, token_contract_address: token_contract_address,
@ -3122,6 +3123,7 @@ defmodule Explorer.ChainTest do
second_page = second_page =
insert( insert(
:token_transfer, :token_transfer,
block_number: 999,
to_address: build(:address), to_address: build(:address),
transaction: transaction, transaction: transaction,
token_contract_address: token_contract_address, token_contract_address: token_contract_address,
@ -3129,13 +3131,11 @@ defmodule Explorer.ChainTest do
token_id: 29 token_id: 29
) )
paging_options = %PagingOptions{key: {first_page.token_id}, page_size: 1} paging_options = %PagingOptions{key: {first_page.block_number, first_page.log_index}, page_size: 1}
unique_tokens_ids_paginated = unique_tokens_ids_paginated =
Chain.address_to_unique_tokens( token_contract_address.hash
token_contract_address.hash, |> Chain.address_to_unique_tokens(paging_options: paging_options)
paging_options: paging_options
)
|> Enum.map(& &1.token_id) |> Enum.map(& &1.token_id)
assert unique_tokens_ids_paginated == [second_page.token_id] assert unique_tokens_ids_paginated == [second_page.token_id]

@ -387,6 +387,7 @@ defmodule Explorer.Factory do
%TokenTransfer{ %TokenTransfer{
amount: Decimal.new(1), amount: Decimal.new(1),
block_number: block_number(),
from_address: from_address, from_address: from_address,
to_address: to_address, to_address: to_address,
token_contract_address: token_address, token_contract_address: token_address,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -8,7 +8,7 @@
"method": "post", "method": "post",
"options": [], "options": [],
"request_body": "", "request_body": "",
"url": "https://sokol-trace.poa.network" "url": "http://18.207.247.30:8545"
}, },
"response": { "response": {
"binary": false, "binary": false,

@ -53,8 +53,8 @@ mix test --exclude no_parity
| Protocol | URL | | Protocol | URL |
|:----------|:-----------------------------------| |:----------|:-----------------------------------|
| HTTP | `https://sokol-trace.poa.network` | | HTTP | `http://localhost:8545` |
| WebSocket | `wss://sokol-ws.poa.network/ws` | | WebSocket | `ws://localhost:8546` |
### Geth ### Geth

@ -11,7 +11,7 @@ config :logger, :indexer_token_balances,
variant = variant =
if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do
"parity" "ganache"
else else
System.get_env("ETHEREUM_JSONRPC_VARIANT") System.get_env("ETHEREUM_JSONRPC_VARIANT")
|> String.split(".") |> String.split(".")

@ -6,11 +6,11 @@ config :indexer,
transport: EthereumJSONRPC.HTTP, transport: EthereumJSONRPC.HTTP,
transport_options: [ transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison, http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "https://sokol.poa.network", url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
method_to_url: [ method_to_url: [
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network", eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network", trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network" trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545"
], ],
http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]] http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]]
], ],
@ -20,6 +20,6 @@ config :indexer,
transport: EthereumJSONRPC.WebSocket, transport: EthereumJSONRPC.WebSocket,
transport_options: [ transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL") || "wss://sokol-ws.poa.network/ws" url: System.get_env("ETHEREUM_JSONRPC_WS_URL") || "ws://localhost:8546"
] ]
] ]

@ -6,11 +6,11 @@ config :indexer,
transport: EthereumJSONRPC.HTTP, transport: EthereumJSONRPC.HTTP,
transport_options: [ transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison, http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "https://sokol.poa.network", url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"),
method_to_url: [ method_to_url: [
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network", eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network", trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "https://sokol-trace.poa.network" trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL")
], ],
http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]] http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]]
], ],
@ -20,6 +20,6 @@ config :indexer,
transport: EthereumJSONRPC.WebSocket, transport: EthereumJSONRPC.WebSocket,
transport_options: [ transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL") || "wss://sokol-ws.poa.network/ws" url: System.get_env("ETHEREUM_JSONRPC_WS_URL")
] ]
] ]

@ -84,7 +84,8 @@ defmodule Indexer.AddressExtraction do
], ],
[ [
%{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :block_number, to: :fetched_coin_balance_block_number},
%{from: :from_address_hash, to: :hash} %{from: :from_address_hash, to: :hash},
%{from: :nonce, to: :nonce}
], ],
[ [
%{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :block_number, to: :fetched_coin_balance_block_number},
@ -136,6 +137,7 @@ defmodule Indexer.AddressExtraction do
required(:hash) => String.t(), required(:hash) => String.t(),
required(:fetched_coin_balance_block_number) => non_neg_integer(), required(:fetched_coin_balance_block_number) => non_neg_integer(),
optional(:fetched_coin_balance) => non_neg_integer(), optional(:fetched_coin_balance) => non_neg_integer(),
optional(:nonce) => non_neg_integer(),
optional(:contract_code) => String.t() optional(:contract_code) => String.t()
} }
@ -209,11 +211,13 @@ defmodule Indexer.AddressExtraction do
...> %{ ...> %{
...> block_number: 1, ...> block_number: 1,
...> from_address_hash: "0x0000000000000000000000000000000000000001", ...> from_address_hash: "0x0000000000000000000000000000000000000001",
...> to_address_hash: "0x0000000000000000000000000000000000000002" ...> to_address_hash: "0x0000000000000000000000000000000000000002",
...> nonce: 3
...> }, ...> },
...> %{ ...> %{
...> block_number: 2, ...> block_number: 2,
...> from_address_hash: "0x0000000000000000000000000000000000000003" ...> from_address_hash: "0x0000000000000000000000000000000000000003",
...> nonce: 4
...> } ...> }
...> ] ...> ]
...> } ...> }
@ -221,7 +225,8 @@ defmodule Indexer.AddressExtraction do
[ [
%{ %{
fetched_coin_balance_block_number: 1, fetched_coin_balance_block_number: 1,
hash: "0x0000000000000000000000000000000000000001" hash: "0x0000000000000000000000000000000000000001",
nonce: 3
}, },
%{ %{
fetched_coin_balance_block_number: 1, fetched_coin_balance_block_number: 1,
@ -229,7 +234,8 @@ defmodule Indexer.AddressExtraction do
}, },
%{ %{
fetched_coin_balance_block_number: 2, fetched_coin_balance_block_number: 2,
hash: "0x0000000000000000000000000000000000000003" hash: "0x0000000000000000000000000000000000000003",
nonce: 4
} }
] ]
@ -279,11 +285,13 @@ defmodule Indexer.AddressExtraction do
...> transactions: [ ...> transactions: [
...> %{ ...> %{
...> block_number: 3, ...> block_number: 3,
...> to_address_hash: "0x0000000000000000000000000000000000000001" ...> to_address_hash: "0x0000000000000000000000000000000000000001",
...> nonce: 5
...> }, ...> },
...> %{ ...> %{
...> block_number: 2, ...> block_number: 2,
...> from_address_hash: "0x0000000000000000000000000000000000000001" ...> from_address_hash: "0x0000000000000000000000000000000000000001",
...> nonce: 4
...> } ...> }
...> ], ...> ],
...> logs: [ ...> logs: [
@ -297,7 +305,8 @@ defmodule Indexer.AddressExtraction do
[ [
%{ %{
fetched_coin_balance_block_number: 7, fetched_coin_balance_block_number: 7,
hash: "0x0000000000000000000000000000000000000001" hash: "0x0000000000000000000000000000000000000001",
nonce: 4
} }
] ]
@ -316,11 +325,13 @@ defmodule Indexer.AddressExtraction do
...> transactions: [ ...> transactions: [
...> %{ ...> %{
...> block_number: 2, ...> block_number: 2,
...> from_address_hash: "0x0000000000000000000000000000000000000001" ...> from_address_hash: "0x0000000000000000000000000000000000000001",
...> nonce: 4
...> }, ...> },
...> %{ ...> %{
...> block_number: 3, ...> block_number: 3,
...> to_address_hash: "0x0000000000000000000000000000000000000001" ...> to_address_hash: "0x0000000000000000000000000000000000000001",
...> nonce: 5
...> } ...> }
...> ] ...> ]
...> } ...> }
@ -329,7 +340,8 @@ defmodule Indexer.AddressExtraction do
%{ %{
contract_code: "0x", contract_code: "0x",
fetched_coin_balance_block_number: 3, fetched_coin_balance_block_number: 3,
hash: "0x0000000000000000000000000000000000000001" hash: "0x0000000000000000000000000000000000000001",
nonce: 4
} }
] ]
@ -366,6 +378,7 @@ defmodule Indexer.AddressExtraction do
%{ %{
required(:block_number) => non_neg_integer(), required(:block_number) => non_neg_integer(),
required(:from_address_hash) => String.t(), required(:from_address_hash) => String.t(),
required(:nonce) => non_neg_integer(),
optional(:to_address_hash) => String.t(), optional(:to_address_hash) => String.t(),
optional(:created_contract_address_hash) => String.t() optional(:created_contract_address_hash) => String.t()
} }
@ -445,38 +458,42 @@ defmodule Indexer.AddressExtraction do
# Ensure that when `:addresses` or `:address_coin_balances` are present, their :fetched_coin_balance will win # Ensure that when `:addresses` or `:address_coin_balances` are present, their :fetched_coin_balance will win
defp merge_addresses(%{hash: hash} = first, %{hash: hash} = second) do defp merge_addresses(%{hash: hash} = first, %{hash: hash} = second) do
case {first[:fetched_coin_balance], second[:fetched_coin_balance]} do merged_addresses =
{nil, nil} -> case {first[:fetched_coin_balance], second[:fetched_coin_balance]} do
first {nil, nil} ->
|> Map.merge(second) first
|> Map.put( |> Map.merge(second)
:fetched_coin_balance_block_number, |> Map.put(
max_nil_last( :fetched_coin_balance_block_number,
Map.get(first, :fetched_coin_balance_block_number), max_nil_last(
Map.get(second, :fetched_coin_balance_block_number) Map.get(first, :fetched_coin_balance_block_number),
Map.get(second, :fetched_coin_balance_block_number)
)
) )
)
{nil, _} ->
{nil, _} -> # merge in `second` so its balance and block_number wins
# merge in `second` so its balance and block_number wins
Map.merge(first, second)
{_, nil} ->
# merge in `first` so its balance and block_number wins
Map.merge(second, first)
{_, _} ->
if greater_than_nil_last(
Map.get(first, :fetched_coin_balance_block_number),
Map.get(second, :fetched_coin_balance_block_number)
) do
# merge in `first` so its block number wins
Map.merge(second, first)
else
# merge in `second` so its block number wins
Map.merge(first, second) Map.merge(first, second)
end
end {_, nil} ->
# merge in `first` so its balance and block_number wins
Map.merge(second, first)
{_, _} ->
if greater_than_nil_last(
Map.get(first, :fetched_coin_balance_block_number),
Map.get(second, :fetched_coin_balance_block_number)
) do
# merge in `first` so its block number wins
Map.merge(second, first)
else
# merge in `second` so its block number wins
Map.merge(first, second)
end
end
merged_addresses
|> Map.put(:nonce, max_nil_last(first[:nonce], second[:nonce]))
end end
# `nil > 5 == true`, but we want numbers instead # `nil > 5 == true`, but we want numbers instead

@ -76,7 +76,8 @@ defmodule Indexer.AddressExtractionTest do
%{ %{
fetched_coin_balance_block_number: 2, fetched_coin_balance_block_number: 2,
contract_code: "0x1", contract_code: "0x1",
hash: "0x0000000000000000000000000000000000000001" hash: "0x0000000000000000000000000000000000000001",
nonce: nil
} }
] ]
end end
@ -95,7 +96,8 @@ defmodule Indexer.AddressExtractionTest do
transaction = %{ transaction = %{
block_number: 3, block_number: 3,
from_address_hash: gen_hash(), from_address_hash: gen_hash(),
to_address_hash: gen_hash() to_address_hash: gen_hash(),
nonce: 7
} }
log = %{address_hash: gen_hash(), block_number: 4} log = %{address_hash: gen_hash(), block_number: 4}
@ -136,7 +138,11 @@ defmodule Indexer.AddressExtractionTest do
contract_code: internal_transaction.created_contract_code, contract_code: internal_transaction.created_contract_code,
fetched_coin_balance_block_number: internal_transaction.block_number fetched_coin_balance_block_number: internal_transaction.block_number
}, },
%{hash: transaction.from_address_hash, fetched_coin_balance_block_number: transaction.block_number}, %{
hash: transaction.from_address_hash,
fetched_coin_balance_block_number: transaction.block_number,
nonce: 7
},
%{hash: transaction.to_address_hash, fetched_coin_balance_block_number: transaction.block_number}, %{hash: transaction.to_address_hash, fetched_coin_balance_block_number: transaction.block_number},
%{hash: log.address_hash, fetched_coin_balance_block_number: log.block_number}, %{hash: log.address_hash, fetched_coin_balance_block_number: log.block_number},
%{ %{
@ -176,7 +182,7 @@ defmodule Indexer.AddressExtractionTest do
blockchain_data = %{ blockchain_data = %{
blocks: [%{miner_hash: hash, number: 34}], blocks: [%{miner_hash: hash, number: 34}],
transactions: [%{block_number: 34, from_address_hash: hash}], transactions: [%{block_number: 34, from_address_hash: hash, nonce: 12}],
internal_transactions: [ internal_transactions: [
%{ %{
block_number: 34, block_number: 34,
@ -188,7 +194,7 @@ defmodule Indexer.AddressExtractionTest do
assert AddressExtraction.extract_addresses(blockchain_data) == assert AddressExtraction.extract_addresses(blockchain_data) ==
[ [
%{hash: hash, fetched_coin_balance_block_number: 34, contract_code: "code"} %{hash: hash, fetched_coin_balance_block_number: 34, contract_code: "code", nonce: 12}
] ]
end end

@ -20,12 +20,12 @@ defmodule Indexer.Block.Realtime.FetcherTest do
setup %{json_rpc_named_arguments: json_rpc_named_arguments} do setup %{json_rpc_named_arguments: json_rpc_named_arguments} do
core_json_rpc_named_arguments = core_json_rpc_named_arguments =
json_rpc_named_arguments json_rpc_named_arguments
|> put_in([:transport_options, :url], "https://core.poa.network") |> put_in([:transport_options, :url], "http://54.144.107.14:8545")
|> put_in( |> put_in(
[:transport_options, :method_to_url], [:transport_options, :method_to_url],
eth_getBalance: "https://core-trace.poa.network", eth_getBalance: "http://54.144.107.14:8545",
trace_replayTransaction: "https://core-trace.poa.network", trace_replayTransaction: "http://54.144.107.14:8545",
trace_block: "https://core-trace.poa.network" trace_block: "http://54.144.107.14:8545"
) )
block_fetcher = %Indexer.Block.Fetcher{ block_fetcher = %Indexer.Block.Fetcher{

@ -54,7 +54,7 @@ Available options are:
| `ETHEREUM_JSONRPC_VARIANT` | Variant of your JSON RPC service: `parity`, `geth` or `ganache` | `parity` | | `ETHEREUM_JSONRPC_VARIANT` | Variant of your JSON RPC service: `parity`, `geth` or `ganache` | `parity` |
| `ETHEREUM_JSONRPC_HTTP_URL` | HTTP JSON RPC URL Only for `geth` or `ganache` variant | Different per JSONRPC variant | | `ETHEREUM_JSONRPC_HTTP_URL` | HTTP JSON RPC URL Only for `geth` or `ganache` variant | Different per JSONRPC variant |
| `ETHEREUM_JSONRPC_WS_URL` | WS JSON RPC url | Different per JSONRPC variant | | `ETHEREUM_JSONRPC_WS_URL` | WS JSON RPC url | Different per JSONRPC variant |
| `ETHEREUM_JSONRPC_TRACE_URL` | Trace URL **Only for `parity` variant** | `https://sokol-trace.poa.network` | | `ETHEREUM_JSONRPC_TRACE_URL` | Trace URL **Only for `parity` variant** | `http://localhost:8545` |
| `COIN` | Default Coin | `POA` | | `COIN` | Default Coin | `POA` |
| `LOGO` | Coin logo | Empty | | `LOGO` | Coin logo | Empty |
| `NETWORK` | Network | Empty | | `NETWORK` | Network | Empty |
@ -65,13 +65,13 @@ Available options are:
`ETHEREUM_JSONRPC_HTTP_URL` default values: `ETHEREUM_JSONRPC_HTTP_URL` default values:
* For `parity` - `https://sokol.poa.network` * For `parity` - `http://localhost:8545`
* For `geth` - `https://mainnet.infura.io/8lTvJTKmHPCHazkneJsY` * For `geth` - `https://mainnet.infura.io/8lTvJTKmHPCHazkneJsY`
* For `ganache` - `http://localhost:7545` * For `ganache` - `http://localhost:7545`
`ETHEREUM_JSONRPC_WS_URL` default values: `ETHEREUM_JSONRPC_WS_URL` default values:
* For `parity` - `wss://sokol-ws.poa.network/ws` * For `parity` - `ws://localhost:8546`
* For `geth` - `wss://mainnet.infura.io/8lTvJTKmHPCHazkneJsY/ws` * For `geth` - `wss://mainnet.infura.io/8lTvJTKmHPCHazkneJsY/ws`
* For `ganache` - `ws://localhost:7545` * For `ganache` - `ws://localhost:7545`

Loading…
Cancel
Save