Build query to fetch tokens from an address

pull/534/head
Felipe Renan 6 years ago
parent 3b226d83eb
commit 3970233b3a
  1. 7
      apps/explorer/lib/explorer/chain.ex
  2. 21
      apps/explorer/lib/explorer/chain/token.ex
  3. 97
      apps/explorer/test/explorer/chain_test.exs

@ -1564,4 +1564,11 @@ defmodule Explorer.Chain do
Repo.one(query) != nil Repo.one(query) != nil
end end
@spec fetch_tokens_from_address_hash(Hash.Address.t()) :: []
def fetch_tokens_from_address_hash(address_hash) do
address_hash
|> Token.with_transfers_by_address()
|> Repo.all()
end
end end

@ -19,8 +19,8 @@ defmodule Explorer.Chain.Token do
use Ecto.Schema use Ecto.Schema
import Ecto.{Changeset} import Ecto.{Changeset, Query}
alias Explorer.Chain.{Address, Hash, Token} alias Explorer.Chain.{Address, Hash, Token, TokenTransfer}
@typedoc """ @typedoc """
* `:name` - Name of the token * `:name` - Name of the token
@ -74,4 +74,21 @@ defmodule Explorer.Chain.Token do
|> foreign_key_constraint(:contract_address) |> foreign_key_constraint(:contract_address)
|> unique_constraint(:contract_address_hash) |> unique_constraint(:contract_address_hash)
end end
@doc """
Builds an `Ecto.Query` to fetch tokens that the given address has interacted with.
In order to fetch a token, the given address must have transfered tokens to or received tokens
from an another address.
"""
def with_transfers_by_address(address_hash) do
from(
token in Token,
join: tt in TokenTransfer,
on: tt.token_contract_address_hash == token.contract_address_hash,
where: tt.to_address_hash == ^address_hash or tt.from_address_hash == ^address_hash,
distinct: tt.token_contract_address_hash,
select: token
)
end
end end

@ -1604,4 +1604,101 @@ defmodule Explorer.ChainTest do
assert Chain.transaction_has_token_transfers?(transaction.hash) == false assert Chain.transaction_has_token_transfers?(transaction.hash) == false
end end
end end
describe "fetch_tokens_from_address_hash/1" do
test "only returns tokens that a given address has interacted with" do
alice = insert(:address)
token_a =
:token
|> insert(name: "token-1")
|> Repo.preload(:contract_address)
token_b =
:token
|> insert(name: "token-2")
|> Repo.preload(:contract_address)
token_c =
:token
|> insert(name: "token-3")
|> Repo.preload(:contract_address)
insert(
:token_transfer,
token_contract_address: token_a.contract_address,
from_address: alice,
to_address: build(:address)
)
insert(
:token_transfer,
token_contract_address: token_b.contract_address,
from_address: build(:address),
to_address: alice
)
insert(
:token_transfer,
token_contract_address: token_c.contract_address,
from_address: build(:address),
to_address: build(:address)
)
expected_tokens =
alice.hash
|> Chain.fetch_tokens_from_address_hash()
|> Enum.map(& &1.name)
assert expected_tokens == [token_a.name, token_b.name]
end
test "returns a empty list when the given address hasn't interacted with one" do
alice = insert(:address)
token =
:token
|> insert(name: "token-1")
|> Repo.preload(:contract_address)
insert(
:token_transfer,
token_contract_address: token.contract_address,
from_address: build(:address),
to_address: build(:address)
)
assert Chain.fetch_tokens_from_address_hash(alice.hash) == []
end
test "distinct tokens by contract_address_hash" do
alice = insert(:address)
token =
:token
|> insert(name: "token-1")
|> Repo.preload(:contract_address)
insert(
:token_transfer,
token_contract_address: token.contract_address,
from_address: alice,
to_address: build(:address)
)
insert(
:token_transfer,
token_contract_address: token.contract_address,
from_address: build(:address),
to_address: alice
)
expected_tokens =
alice.hash
|> Chain.fetch_tokens_from_address_hash()
|> Enum.map(& &1.name)
assert expected_tokens == [token.name]
end
end
end end

Loading…
Cancel
Save