Merge pull request #1009 from poanetwork/ln-optmize-address-transactions-page

Optmize address transactions page
pull/1045/head
Amanda 6 years ago committed by GitHub
commit 13b9f8ccef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  2. 5
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex
  3. 4
      apps/block_scout_web/priv/gettext/default.pot
  4. 4
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  5. 38
      apps/explorer/lib/explorer/chain.ex
  6. 59
      apps/explorer/lib/explorer/chain/token_transfer.ex
  7. 28
      apps/explorer/lib/explorer/chain/transaction.ex
  8. 84
      apps/explorer/test/explorer/chain_test.exs

@ -19,7 +19,10 @@ defmodule BlockScoutWeb.AddressTransactionController do
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional,
:token_transfers => :optional
[token_transfers: :token] => :optional,
[token_transfers: :to_address] => :optional,
[token_transfers: :from_address] => :optional,
[token_transfers: :token_contract_address] => :optional
}
]
@ -81,7 +84,7 @@ defmodule BlockScoutWeb.AddressTransactionController do
{:ok, address} <- Chain.hash_to_address(address_hash) do
pending_options =
@transaction_necessity_by_association
|> Keyword.merge(paging_options(%{}))
|> Keyword.merge(paging_options(params))
|> Keyword.merge(current_filter(params))
full_options = put_in(pending_options, [:necessity_by_association, :block], :required)

@ -52,14 +52,17 @@
<%= if involves_token_transfers?(@transaction) do %>
<div class="offset-md-2 col-md-10 col-lg-8 d-flex flex-column mt-2 mb-2">
<% [first_token_transfer | remaining_token_transfers]= @transaction.token_transfers %>
<% [first_token_transfer | remaining_token_transfers] = @transaction.token_transfers %>
<%= render "_token_transfer.html", address: assigns[:current_address], token_transfer: first_token_transfer %>
<div class="collapse token-transfer-toggle" id="transaction-<%= @transaction.hash %>">
<%= for token_transfer <- remaining_token_transfers do %>
<%= render "_token_transfer.html", address: assigns[:current_address], token_transfer: token_transfer %>
<% end %>
</div>
</div>
<%= if Enum.any?(remaining_token_transfers) do %>
<div class="col-md-12 d-flex flex-column mt-1 mb-2 text-center token-tile-view-more">
<span class="token-tile-more-lines">

@ -1080,12 +1080,12 @@ msgid "View Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_tile.html.eex:67
#: lib/block_scout_web/templates/transaction/_tile.html.eex:70
msgid "View Less Transfers"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_tile.html.eex:66
#: lib/block_scout_web/templates/transaction/_tile.html.eex:69
msgid "View More Transfers"
msgstr ""

@ -1080,12 +1080,12 @@ msgid "View Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_tile.html.eex:67
#: lib/block_scout_web/templates/transaction/_tile.html.eex:70
msgid "View Less Transfers"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_tile.html.eex:66
#: lib/block_scout_web/templates/transaction/_tile.html.eex:69
msgid "View More Transfers"
msgstr ""

@ -219,38 +219,22 @@ defmodule Explorer.Chain do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
transaction_matches =
direction
|> case do
:from -> [:from_address_hash]
:to -> [:to_address_hash, :created_contract_address_hash]
_ -> [:from_address_hash, :to_address_hash, :created_contract_address_hash]
end
|> Enum.map(fn address_field ->
paging_options
|> fetch_transactions()
|> Transaction.where_address_fields_match(address_hash, address_field)
|> join_associations(necessity_by_association)
|> Transaction.preload_token_transfers(address_hash)
|> Repo.all()
|> MapSet.new()
end)
token_transfer_matches =
{:ok, address_bytes} = Explorer.Chain.Hash.Address.dump(address_hash)
token_transfers_dynamic = TokenTransfer.dynamic_any_address_fields_match(direction, address_bytes)
transaction_dynamic =
Transaction.dynamic_where_address_hash_matches(address_hash, direction, token_transfers_dynamic)
base_query =
paging_options
|> fetch_transactions()
|> TokenTransfer.where_address_fields_match(address_hash, direction)
|> join_associations(necessity_by_association)
|> Transaction.preload_token_transfers(address_hash)
|> Repo.all()
|> MapSet.new()
transaction_matches
|> Enum.reduce(token_transfer_matches, &MapSet.union/2)
|> MapSet.to_list()
|> Enum.sort_by(& &1.index, &>=/2)
|> Enum.sort_by(& &1.block_number, &>=/2)
|> Enum.slice(0..paging_options.page_size)
base_query
|> from(where: ^transaction_dynamic)
|> Repo.all()
end
@doc """

@ -151,22 +151,57 @@ defmodule Explorer.Chain.TokenTransfer do
)
end
def where_address_fields_match(query, address_hash, :from) do
query
|> join(:left, [transaction], tt in assoc(transaction, :token_transfers))
|> where([_transaction, tt], tt.from_address_hash == ^address_hash)
@doc """
Builds a dynamic query expression to identify if there is a token transfer
related to the hash.
"""
def dynamic_any_address_fields_match(:to, address_bytes) do
dynamic(
[t],
t.hash ==
fragment(
~s"""
(SELECT tt.transaction_hash
FROM "token_transfers" AS tt
WHERE (tt."to_address_hash" = ?)
LIMIT 1)
""",
^address_bytes
)
)
end
def where_address_fields_match(query, address_hash, :to) do
query
|> join(:left, [transaction], tt in assoc(transaction, :token_transfers))
|> where([_transaction, tt], tt.to_address_hash == ^address_hash)
def dynamic_any_address_fields_match(:from, address_bytes) do
dynamic(
[t],
t.hash ==
fragment(
~s"""
(SELECT tt.transaction_hash
FROM "token_transfers" AS tt
WHERE (tt."from_address_hash" = ?)
LIMIT 1)
""",
^address_bytes
)
)
end
def where_address_fields_match(query, address_hash, _) do
query
|> join(:left, [transaction], tt in assoc(transaction, :token_transfers))
|> where([_transaction, tt], tt.to_address_hash == ^address_hash or tt.from_address_hash == ^address_hash)
def dynamic_any_address_fields_match(_, address_bytes) do
dynamic(
[t],
t.hash ==
fragment(
~s"""
(SELECT tt.transaction_hash
FROM "token_transfers" AS tt
WHERE ((tt."to_address_hash" = ?) OR (tt."from_address_hash" = ?))
LIMIT 1)
""",
^address_bytes,
^address_bytes
)
)
end
@doc """

@ -3,7 +3,7 @@ defmodule Explorer.Chain.Transaction do
use Explorer.Schema
import Ecto.Query, only: [from: 2, preload: 3, where: 3, subquery: 1]
import Ecto.Query, only: [dynamic: 2, from: 2, preload: 3, subquery: 1, where: 3]
alias Ecto.Changeset
@ -427,6 +427,32 @@ defmodule Explorer.Chain.Transaction do
where(query, [t], field(t, ^address_field) == ^address_hash)
end
@doc """
Builds a dynamic query expression to identify if there is a transaction
related to the hash.
"""
def dynamic_where_address_hash_matches(address_hash, :to, dynamic) do
dynamic(
[t],
t.to_address_hash == ^address_hash or t.created_contract_address_hash == ^address_hash or ^dynamic
)
end
def dynamic_where_address_hash_matches(address_hash, :from, dynamic) do
dynamic(
[t],
t.from_address_hash == ^address_hash or ^dynamic
)
end
def dynamic_where_address_hash_matches(address_hash, _, dynamic) do
dynamic(
[t],
t.to_address_hash == ^address_hash or t.from_address_hash == ^address_hash or
t.created_contract_address_hash == ^address_hash or ^dynamic
)
end
@collated_fields ~w(block_number cumulative_gas_used gas_used index)a
@collated_message "can't be blank when the transaction is collated into a block"

@ -208,15 +208,14 @@ defmodule Explorer.ChainTest do
transaction =
:transaction
|> insert()
|> insert(to_address: address, to_address_hash: address.hash)
|> with_block()
insert(:token_transfer, to_address: address, transaction: transaction)
transaction =
Transaction
|> Repo.get!(transaction.hash)
|> Repo.preload([:block, :to_address, :from_address, token_transfers: :token])
insert(
:token_transfer,
to_address: address,
transaction: transaction
)
assert [transaction.hash] ==
Chain.address_to_transactions(address)
@ -228,33 +227,75 @@ defmodule Explorer.ChainTest do
transaction =
:transaction
|> insert()
|> insert(to_address: address)
|> with_block()
token_transfer = insert(:token_transfer, to_address: address, transaction: transaction)
insert(:token_transfer, to_address: build(:address), transaction: transaction)
token_transfer =
insert(
:token_transfer,
to_address: address,
transaction: transaction
)
insert(
:token_transfer,
to_address: build(:address),
transaction: transaction
)
transaction =
address
|> Chain.address_to_transactions()
|> List.first()
transaction = Chain.address_to_transactions(address) |> List.first()
token_transfers_related =
Enum.map(
transaction.token_transfers,
&{&1.transaction_hash, &1.log_index}
)
assert transaction.token_transfers |> Enum.map(&{&1.transaction_hash, &1.log_index}) == [
assert token_transfers_related == [
{token_transfer.transaction_hash, token_transfer.log_index}
]
end
test "returns just the token transfers related to the given contract address" do
contract_address = insert(:address, contract_code: Factory.data("contract_code"))
contract_address =
insert(
:address,
contract_code: Factory.data("contract_code")
)
transaction =
:transaction
|> insert()
|> insert(to_address: contract_address)
|> with_block()
token_transfer = insert(:token_transfer, to_address: contract_address, transaction: transaction)
insert(:token_transfer, to_address: build(:address), transaction: transaction)
token_transfer =
insert(
:token_transfer,
to_address: contract_address,
transaction: transaction
)
transaction = Chain.address_to_transactions(contract_address) |> List.first()
insert(
:token_transfer,
to_address: build(:address),
transaction: transaction
)
transaction =
contract_address
|> Chain.address_to_transactions()
|> List.first()
token_transfers_contract_address =
Enum.map(
transaction.token_transfers,
&{&1.transaction_hash, &1.log_index}
)
assert Enum.map(transaction.token_transfers, &{&1.transaction_hash, &1.log_index}) == [
assert token_transfers_contract_address == [
{token_transfer.transaction_hash, token_transfer.log_index}
]
end
@ -289,7 +330,7 @@ defmodule Explorer.ChainTest do
address = insert(:address)
second_page_hashes =
50
2
|> insert_list(:transaction, from_address: address)
|> with_block()
|> Enum.map(& &1.hash)
@ -302,7 +343,10 @@ defmodule Explorer.ChainTest do
assert second_page_hashes ==
address
|> Chain.address_to_transactions(
paging_options: %PagingOptions{key: {block_number, index}, page_size: 50}
paging_options: %PagingOptions{
key: {block_number, index},
page_size: 2
}
)
|> Enum.map(& &1.hash)
|> Enum.reverse()

Loading…
Cancel
Save