Merge pull request #4977 from blockscout/vb-export-token-transfers-include-transfers-on-contract

Export token transfers on address: include transfers on contract itself
pull/4976/head
Victor Baranov 3 years ago committed by GitHub
commit d154b2b91b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 23
      apps/explorer/lib/explorer/chain.ex
  3. 36
      apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex

@ -4,6 +4,7 @@
- [#4931](https://github.com/blockscout/blockscout/pull/4931) - Web3 modal with Wallet Connect for Write contract page and Staking Dapp
### Fixes
- [#4977](https://github.com/blockscout/blockscout/pull/4977) - Export token transfers on address: include transfers on contract itself
- [#4965](https://github.com/blockscout/blockscout/pull/4965) - Fix search field appearance on medium size screens
- [#4945](https://github.com/blockscout/blockscout/pull/4945) - Fix `Verify & Publish` button link
- [#4888](https://github.com/blockscout/blockscout/pull/4888) - Fix fetch_top_tokens method: add nulls last for token holders desc order

@ -549,6 +549,29 @@ defmodule Explorer.Chain do
|> Repo.all()
end
@doc """
address_hash_to_token_transfers_including_contract/2 function returns token transfers on address (to/from/contract).
It is used by CSV export of token transfers button.
"""
@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
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
query =
from(
token_transfer in TokenTransfer,
where: token_transfer.to_address_hash == ^address_hash,
or_where: token_transfer.from_address_hash == ^address_hash,
or_where: token_transfer.token_contract_address_hash == ^address_hash
)
query
|> handle_paging_options(paging_options)
|> preload(transaction: :block)
|> preload(:token)
|> Repo.all()
end
@spec address_to_logs(Hash.Address.t(), Keyword.t()) :: [Log.t()]
def address_to_logs(address_hash, options \\ []) when is_list(options) do
paging_options = Keyword.get(options, :paging_options) || %PagingOptions{page_size: 50}

@ -4,7 +4,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
"""
alias Explorer.{Chain, PagingOptions}
alias Explorer.Chain.{Address, AddressTransactionCsvExporter, TokenTransfer}
alias Explorer.Chain.{Address, TokenTransfer}
alias NimbleCSV.RFC4180
@page_size 150
@ -16,18 +16,30 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
to_block = Chain.convert_date_to_max_block(to_period)
address.hash
|> AddressTransactionCsvExporter.fetch_all_transactions(from_block, to_block, @paging_options)
|> to_token_transfers()
|> fetch_all_token_transfers(from_block, to_block, @paging_options)
|> to_csv_format(address)
|> dump_to_stream()
end
defp to_token_transfers(transactions) do
transactions
|> Enum.flat_map(fn transaction ->
transaction.token_transfers
|> Enum.map(fn transfer -> %{transfer | transaction: transaction} end)
end)
def fetch_all_token_transfers(address_hash, from_block, to_block, paging_options, acc \\ []) do
options =
[]
|> Keyword.put(:paging_options, paging_options)
|> Keyword.put(:from_block, from_block)
|> Keyword.put(:to_block, to_block)
token_transfers = Chain.address_hash_to_token_transfers_including_contract(address_hash, options)
new_acc = token_transfers ++ acc
case Enum.split(token_transfers, @page_size) do
{_token_transfers, [%TokenTransfer{block_number: block_number, log_index: log_index}]} ->
new_paging_options = %{@paging_options | key: {block_number, log_index}}
fetch_all_token_transfers(address_hash, from_block, to_block, new_paging_options, new_acc)
{_, []} ->
new_acc
end
end
defp dump_to_stream(transactions) do
@ -58,9 +70,9 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
to_string(token_transfer.transaction_hash),
token_transfer.transaction.block_number,
token_transfer.transaction.block.timestamp,
token_transfer.from_address |> to_string() |> String.downcase(),
token_transfer.to_address |> to_string() |> String.downcase(),
token_transfer.token_contract_address |> to_string() |> String.downcase(),
token_transfer.from_address_hash |> to_string() |> String.downcase(),
token_transfer.to_address_hash |> to_string() |> String.downcase(),
token_transfer.token_contract_address_hash |> to_string() |> String.downcase(),
type(token_transfer, address.hash),
token_transfer.token.symbol,
token_transfer.amount,

Loading…
Cancel
Save