Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add pagination support for querying wallet addresses #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ config :ethcule_poirot, Adapters.Api.Blockscout,
config :ethcule_poirot, Adapters.Api.DissrupTheGraph,
api_url: System.get_env("DISSRUP_THE_GRAPH_API_URL"),
api_timeout: 120_000

config :logger, :console,
level: :info
33 changes: 25 additions & 8 deletions lib/adapters/api/blockscout.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ defmodule Adapters.Api.Blockscout do
end

@impl true
def transactions_for_address(eth_address) do
def transactions_for_address(eth_address, search_after) do
search_first = 15
Logger.debug("Query with: {\"eth_address\": \"#{eth_address}\", \"search_first\": #{search_first}, \"search_after\": \"#{search_after}\"}")
Neuron.query(
"""
query($eth_address: AddressHash!) {
query($eth_address: AddressHash!, $search_first: Int, $search_after: String) {
address(hash: $eth_address) {
contractCode
transactions(last: 23, count: 23) {
transactions(first: $search_first, after: $search_after) {
edges {
node {
hash
Expand All @@ -51,11 +53,15 @@ defmodule Adapters.Api.Blockscout do
status
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
""",
%{eth_address: eth_address}
%{eth_address: eth_address, search_first: search_first, search_after: search_after}
)
|> parse_transactions(eth_address)
end
Expand Down Expand Up @@ -84,6 +90,8 @@ defmodule Adapters.Api.Blockscout do
defp parse_transactions({:ok, %Neuron.Response{body: body}}, eth_address) do
transactions = body["data"]["address"]["transactions"]["edges"] || []
contract_code = !!body["data"]["address"]["contractCode"]
end_cursor = body["data"]["address"]["transactions"]["pageInfo"]["endCursor"]
has_next_page = body["data"]["address"]["transactions"]["pageInfo"]["hasNextPage"]

transactions_struct =
Enum.map(
Expand All @@ -100,18 +108,27 @@ defmodule Adapters.Api.Blockscout do
%Address{
eth_address: eth_address,
contract: contract_code,
transactions: transactions_struct
transactions: transactions_struct,
end_cursor: end_cursor,
has_next_page: has_next_page
}
end

@spec parse_transactions({:error, any()}, String.t()) :: Address.t()
defp parse_transactions(_request_result, eth_address) do
Logger.warn("Failed ETH transactions for #{eth_address}")
defp parse_transactions(request_result, eth_address) do
case request_result do
{:error, error} ->
Logger.warn("Failed ETH transactions for #{eth_address}: #{inspect(error)}")

_ ->
Logger.debug("Successful ETH transactions for #{eth_address}")
end

%Address{
eth_address: eth_address,
contract: false,
transactions: []
transactions: [],
has_next_page: false
}
end
end
6 changes: 4 additions & 2 deletions lib/address.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
defmodule Address do
@moduledoc false

defstruct [:eth_address, :contract, :transactions]
defstruct [:eth_address, :contract, :transactions, :end_cursor, :has_next_page]

@type t :: %__MODULE__{
eth_address: String.t() | nil,
contract: boolean() | nil,
transactions: list(Transaction.t()) | nil
transactions: list(Transaction.t()) | nil,
end_cursor: String.t() | nil,
has_next_page: boolean() | nil,
}
end
28 changes: 22 additions & 6 deletions lib/ethcule_poirot/address_explorer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,34 @@ defmodule EthculePoirot.AddressExplorer do
end

@impl true
def handle_info(:start, %{depth: depth} = state) do
def handle_info(:start, %{depth: depth} = state) do
search_after = nil
handle_info_loop(state, depth, search_after)
NetworkExplorer.node_visited(state.eth_address)
{:stop, :normal, state}
end

defp handle_info_loop(state, depth, search_after) do
state.eth_address
|> state.api_handler.transactions_for_address()
|> state.api_handler.transactions_for_address(search_after)
|> handle_transactions(depth)
|> handle_next_page(state, depth, search_after)
end

NetworkExplorer.node_visited(state.eth_address)

{:stop, :normal, state}
@spec handle_next_page(Address.t(), any(), pos_integer(), String.t()) :: any()
defp handle_next_page(address_info, state, depth, search_after) do
if address_info.has_next_page do
state
|> handle_info_loop(depth, address_info.end_cursor)
else
Logger.debug("Last page for #{address_info.eth_address} reached (after #{search_after})")
end
end

@spec handle_transactions(Address.t(), pos_integer()) :: any()
@spec handle_transactions(Address.t(), pos_integer()) :: Address.t()
defp handle_transactions(%{transactions: []} = address_info, _depth) do
update_node_label(address_info.contract, address_info.eth_address)
address_info
end

defp handle_transactions(address_info, depth) do
Expand All @@ -61,6 +76,7 @@ defmodule EthculePoirot.AddressExplorer do

NetworkExplorer.visit_node(next_address, depth - 1)
end)
address_info
end

@spec update_node_label(true, String.t()) :: any()
Expand Down
1 change: 1 addition & 0 deletions lib/ethcule_poirot/network_explorer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ defmodule EthculePoirot.NetworkExplorer do
Logger.info("Already explored #{eth_address}")
{:noreply, state}
else
Logger.debug("Queued #{eth_address}")
new_known = MapSet.put(state.known, eth_address)

{processes_count, new_remaining, new_exploring} =
Expand Down