Merge pull request #5032 from blockscout/np-fix-csv-token-transfer-export

Fix token transfer csv export
pull/5045/head
Victor Baranov 3 years ago committed by GitHub
commit 569b07e0aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 4
      apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs
  3. 67
      apps/explorer/lib/explorer/chain.ex
  4. 4
      apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex
  5. 8
      apps/explorer/lib/explorer/chain/token_transfer.ex
  6. 6
      apps/explorer/lib/explorer/paging_options.ex
  7. 3
      apps/explorer/test/explorer/chain/address_token_transfer_csv_exporter_test.exs

@ -8,6 +8,7 @@
- [#4931](https://github.com/blockscout/blockscout/pull/4931) - Web3 modal with Wallet Connect for Write contract page and Staking Dapp - [#4931](https://github.com/blockscout/blockscout/pull/4931) - Web3 modal with Wallet Connect for Write contract page and Staking Dapp
### Fixes ### Fixes
- [#5032](https://github.com/blockscout/blockscout/pull/5032) - Fix token transfer csv export
- [#5020](https://github.com/blockscout/blockscout/pull/5020) - Token instance image display imrovement - [#5020](https://github.com/blockscout/blockscout/pull/5020) - Token instance image display imrovement
- [#5019](https://github.com/blockscout/blockscout/pull/5019) - Fix fetch_last_token_balance function termination - [#5019](https://github.com/blockscout/blockscout/pull/5019) - Fix fetch_last_token_balance function termination
- [#5011](https://github.com/blockscout/blockscout/pull/5011) - Fix `0x0` implementation address - [#5011](https://github.com/blockscout/blockscout/pull/5011) - Fix `0x0` implementation address

@ -167,8 +167,8 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do
|> insert(from_address: address) |> insert(from_address: address)
|> with_block() |> with_block()
insert(:token_transfer, transaction: transaction, from_address: address) insert(:token_transfer, transaction: transaction, from_address: address, block_number: transaction.block_number)
insert(:token_transfer, transaction: transaction, to_address: address) insert(:token_transfer, transaction: transaction, to_address: address, block_number: transaction.block_number)
from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime) from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime)
to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime)

@ -559,22 +559,67 @@ defmodule Explorer.Chain do
@spec address_hash_to_token_transfers_including_contract(Hash.Address.t(), Keyword.t()) :: [TokenTransfer.t()] @spec address_hash_to_token_transfers_including_contract(Hash.Address.t(), Keyword.t()) :: [TokenTransfer.t()]
def address_hash_to_token_transfers_including_contract(address_hash, options \\ []) do def address_hash_to_token_transfers_including_contract(address_hash, options \\ []) do
paging_options = Keyword.get(options, :paging_options, @default_paging_options) paging_options = Keyword.get(options, :paging_options, @default_paging_options)
from_block = Keyword.get(options, :from_block)
to_block = Keyword.get(options, :to_block)
query = query =
from( from_block
token_transfer in TokenTransfer, |> query_address_hash_to_token_transfers_including_contract(to_block, address_hash)
where: token_transfer.to_address_hash == ^address_hash, |> order_by([token_transfer], asc: token_transfer.block_number, asc: token_transfer.log_index)
or_where: token_transfer.from_address_hash == ^address_hash,
or_where: token_transfer.token_contract_address_hash == ^address_hash
)
query query
|> handle_paging_options(paging_options) |> handle_token_transfer_paging_options(paging_options)
|> preload(transaction: :block) |> preload(transaction: :block)
|> preload(:token) |> preload(:token)
|> Repo.all() |> Repo.all()
end end
defp query_address_hash_to_token_transfers_including_contract(nil, to_block, address_hash)
when not is_nil(to_block) do
from(
token_transfer in TokenTransfer,
where:
(token_transfer.to_address_hash == ^address_hash or
token_transfer.from_address_hash == ^address_hash or
token_transfer.token_contract_address_hash == ^address_hash) and
token_transfer.block_number <= ^to_block
)
end
defp query_address_hash_to_token_transfers_including_contract(from_block, nil, address_hash)
when not is_nil(from_block) do
from(
token_transfer in TokenTransfer,
where:
(token_transfer.to_address_hash == ^address_hash or
token_transfer.from_address_hash == ^address_hash or
token_transfer.token_contract_address_hash == ^address_hash) and
token_transfer.block_number >= ^from_block
)
end
defp query_address_hash_to_token_transfers_including_contract(from_block, to_block, address_hash)
when not is_nil(from_block) and not is_nil(to_block) do
from(
token_transfer in TokenTransfer,
where:
(token_transfer.to_address_hash == ^address_hash or
token_transfer.from_address_hash == ^address_hash or
token_transfer.token_contract_address_hash == ^address_hash) and
(token_transfer.block_number >= ^from_block and token_transfer.block_number <= ^to_block)
)
end
defp query_address_hash_to_token_transfers_including_contract(_, _, address_hash) do
from(
token_transfer in TokenTransfer,
where:
token_transfer.to_address_hash == ^address_hash or
token_transfer.from_address_hash == ^address_hash or
token_transfer.token_contract_address_hash == ^address_hash
)
end
@spec address_to_logs(Hash.Address.t(), Keyword.t()) :: [Log.t()] @spec address_to_logs(Hash.Address.t(), Keyword.t()) :: [Log.t()]
def address_to_logs(address_hash, options \\ []) when is_list(options) do def address_to_logs(address_hash, options \\ []) when is_list(options) do
paging_options = Keyword.get(options, :paging_options) || %PagingOptions{page_size: 50} paging_options = Keyword.get(options, :paging_options) || %PagingOptions{page_size: 50}
@ -4258,6 +4303,14 @@ defmodule Explorer.Chain do
|> limit(^paging_options.page_size) |> limit(^paging_options.page_size)
end end
defp handle_token_transfer_paging_options(query, nil), do: query
defp handle_token_transfer_paging_options(query, paging_options) do
query
|> TokenTransfer.page_token_transfer(paging_options)
|> limit(^paging_options.page_size)
end
defp join_association(query, [{association, nested_preload}], necessity) defp join_association(query, [{association, nested_preload}], necessity)
when is_atom(association) and is_atom(nested_preload) do when is_atom(association) and is_atom(nested_preload) do
case necessity do case necessity do

@ -8,7 +8,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
alias NimbleCSV.RFC4180 alias NimbleCSV.RFC4180
@page_size 150 @page_size 150
@paging_options %PagingOptions{page_size: @page_size + 1} @paging_options %PagingOptions{page_size: @page_size + 1, asc_order: true}
@spec export(Address.t(), String.t(), String.t()) :: Enumerable.t() @spec export(Address.t(), String.t(), String.t()) :: Enumerable.t()
def export(address, from_period, to_period) do def export(address, from_period, to_period) do
@ -30,7 +30,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
token_transfers = Chain.address_hash_to_token_transfers_including_contract(address_hash, options) token_transfers = Chain.address_hash_to_token_transfers_including_contract(address_hash, options)
new_acc = token_transfers ++ acc new_acc = acc ++ token_transfers
case Enum.split(token_transfers, @page_size) do case Enum.split(token_transfers, @page_size) do
{_token_transfers, [%TokenTransfer{block_number: block_number, log_index: log_index}]} -> {_token_transfers, [%TokenTransfer{block_number: block_number, log_index: log_index}]} ->

@ -219,6 +219,14 @@ defmodule Explorer.Chain.TokenTransfer do
where(query, [tt], tt.token_id < ^token_id) where(query, [tt], tt.token_id < ^token_id)
end end
def page_token_transfer(query, %PagingOptions{key: {block_number, log_index}, asc_order: true}) do
where(
query,
[tt],
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: {block_number, log_index}}) do def page_token_transfer(query, %PagingOptions{key: {block_number, log_index}}) do
where( where(
query, query,

@ -9,7 +9,8 @@ defmodule Explorer.PagingOptions do
page_size: page_size, page_size: page_size,
page_number: page_number, page_number: page_number,
is_pending_tx: is_pending_tx, is_pending_tx: is_pending_tx,
is_index_in_asc_order: is_index_in_asc_order is_index_in_asc_order: is_index_in_asc_order,
asc_order: asc_order
} }
@typep key :: any() @typep key :: any()
@ -17,6 +18,7 @@ defmodule Explorer.PagingOptions do
@typep page_number :: pos_integer() @typep page_number :: pos_integer()
@typep is_pending_tx :: atom() @typep is_pending_tx :: atom()
@typep is_index_in_asc_order :: atom() @typep is_index_in_asc_order :: atom()
@typep asc_order :: atom()
defstruct [:key, :page_size, page_number: 1, is_pending_tx: false, is_index_in_asc_order: false] defstruct [:key, :page_size, page_number: 1, is_pending_tx: false, is_index_in_asc_order: false, asc_order: false]
end end

@ -12,7 +12,8 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do
|> insert(from_address: address) |> insert(from_address: address)
|> with_block() |> with_block()
token_transfer = insert(:token_transfer, transaction: transaction, from_address: address) token_transfer =
insert(:token_transfer, transaction: transaction, from_address: address, block_number: transaction.block_number)
from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime) from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime)
to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime)

Loading…
Cancel
Save