Fix ERC-1155 tokens fetching

pull/6013/head
Qwerty5Uiop 2 years ago
parent c870e653db
commit 1c8e590719
  1. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex
  2. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex
  3. 44
      apps/block_scout_web/test/block_scout_web/controllers/tokens/instance/transfer_controller_test.exs
  4. 23
      apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs
  5. 106
      apps/explorer/lib/explorer/chain.ex
  6. 6
      apps/explorer/lib/explorer/chain/token_transfer.ex

@ -59,7 +59,7 @@ defmodule BlockScoutWeb.Tokens.Instance.TransferController do
with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash),
{:ok, token} <- Chain.token_from_address_hash(hash, options),
{:ok, token_transfer} <-
Chain.erc721_token_instance_from_token_id_and_token_address(token_id, hash) do
Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, hash) do
render(
conn,
"index.html",

@ -7,7 +7,7 @@ defmodule BlockScoutWeb.Tokens.InstanceController do
def show(conn, %{"token_id" => token_address_hash, "id" => token_id}) do
with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash),
:ok <- Chain.check_token_exists(hash),
:ok <- Chain.check_erc721_token_instance_exists(token_id, hash) do
:ok <- Chain.check_erc721_or_erc1155_token_instance_exists(token_id, hash) do
token_instance_transfer_path =
conn
|> token_instance_transfer_path(:index, token_address_hash, token_id)

@ -0,0 +1,44 @@
defmodule BlockScoutWeb.Tokens.Instance.TransferControllerTest do
use BlockScoutWeb.ConnCase, async: false
describe "GET token-transfers/2" do
test "works for ERC-721 tokens", %{conn: conn} do
contract_address = insert(:address)
insert(:token, contract_address: contract_address)
token_id = 10
%{log_index: log_index} =
insert(:token_transfer,
from_address: contract_address,
token_contract_address: contract_address,
token_id: token_id
)
conn = get(conn, "/token/#{contract_address.hash}/instance/#{token_id}/token-transfers")
assert %{assigns: %{token_instance: %{log_index: ^log_index}}} = conn
end
test "works for ERC-1155 tokens", %{conn: conn} do
contract_address = insert(:address)
insert(:token, contract_address: contract_address)
token_id = 10
%{log_index: log_index} =
insert(:token_transfer,
from_address: contract_address,
token_contract_address: contract_address,
token_id: nil,
token_ids: [token_id]
)
conn = get(conn, "/token/#{contract_address.hash}/instance/#{token_id}/token-transfers")
assert %{assigns: %{token_instance: %{log_index: ^log_index}}} = conn
end
end
end

@ -19,5 +19,28 @@ defmodule BlockScoutWeb.Tokens.InstanceControllerTest do
assert conn.status == 302
end
test "works for ERC-1155 tokens", %{conn: conn} do
contract_address = insert(:address)
insert(:token, contract_address: contract_address)
token_id = 10
insert(:token_transfer,
from_address: contract_address,
token_contract_address: contract_address,
token_id: nil,
token_ids: [token_id]
)
conn = get(conn, token_instance_path(BlockScoutWeb.Endpoint, :show, to_string(contract_address.hash), token_id))
assert conn.status == 302
assert get_resp_header(conn, "location") == [
"/token/#{contract_address.hash}/instance/#{token_id}/token-transfers"
]
end
end
end

@ -4881,6 +4881,28 @@ defmodule Explorer.Chain do
end
end
@spec erc721_or_erc1155_token_instance_from_token_id_and_token_address(binary(), Hash.Address.t()) ::
{:ok, TokenTransfer.t()} | {:error, :not_found}
def erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, token_contract_address) do
query =
from(tt in TokenTransfer,
left_join: instance in Instance,
on:
tt.token_contract_address_hash == instance.token_contract_address_hash and
(tt.token_id == instance.token_id or instance.token_id in tt.token_ids),
where:
tt.token_contract_address_hash == ^token_contract_address and
(tt.token_id == ^token_id or ^token_id in tt.token_ids),
limit: 1,
select: %{tt | instance: instance}
)
case Repo.one(query) do
nil -> {:error, :not_found}
token_instance -> {:ok, token_instance}
end
end
defp fetch_coin_balances(address_hash, paging_options) do
address = Repo.get_by(Address, hash: address_hash)
@ -5505,7 +5527,7 @@ defmodule Explorer.Chain do
end
@doc """
Checks if a `t:Explorer.Chain.TokenTransfer.t/0` with the given `hash` and `token_id` exists.
Checks if a `t:Explorer.Chain.TokenTransfer.t/0` of type ERC-721 with the given `hash` and `token_id` exists.
Returns `:ok` if found
@ -5533,7 +5555,46 @@ defmodule Explorer.Chain do
end
@doc """
Checks if a `t:Explorer.Chain.TokenTransfer.t/0` with the given `hash` and `token_id` exists.
Checks if a `t:Explorer.Chain.TokenTransfer.t/0` of type ERC-721 or ERC-1155 with the given `hash` and `token_id` exists.
Returns `:ok` if found
iex> contract_address = insert(:address)
iex> token_id = 10
iex> insert(:token_transfer,
...> from_address: contract_address,
...> token_contract_address: contract_address,
...> token_id: token_id
...> )
iex> Explorer.Chain.check_erc721_or_erc1155_token_instance_exists(token_id, contract_address.hash)
:ok
iex> contract_address = insert(:address)
iex> token_id = 10
iex> insert(:token_transfer,
...> from_address: contract_address,
...> token_contract_address: contract_address,
...> token_ids: [token_id]
...> )
iex> Explorer.Chain.check_erc721_or_erc1155_token_instance_exists(token_id, contract_address.hash)
:ok
Returns `:not_found` if not found
iex> {:ok, hash} = Explorer.Chain.string_to_address_hash("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
iex> Explorer.Chain.check_erc721_or_erc1155_token_instance_exists(10, hash)
:not_found
"""
@spec check_erc721_or_erc1155_token_instance_exists(binary() | non_neg_integer(), Hash.Address.t()) ::
:ok | :not_found
def check_erc721_or_erc1155_token_instance_exists(token_id, hash) do
token_id
|> erc721_or_erc1155_token_instance_exist?(hash)
|> boolean_to_check_result()
end
@doc """
Checks if a `t:Explorer.Chain.TokenTransfer.t/0` of type ERC-721 with the given `hash` and `token_id` exists.
Returns `true` if found
@ -5563,6 +5624,47 @@ defmodule Explorer.Chain do
Repo.exists?(query)
end
@doc """
Checks if a `t:Explorer.Chain.TokenTransfer.t/0` of type ERC-721 or ERC-1155 with the given `hash` and `token_id` exists.
Returns `true` if found
iex> contract_address = insert(:address)
iex> token_id = 10
iex> insert(:token_transfer,
...> from_address: contract_address,
...> token_contract_address: contract_address,
...> token_id: token_id
...> )
iex> Explorer.Chain.erc721_or_erc1155_token_instance_exist?(token_id, contract_address.hash)
true
iex> contract_address = insert(:address)
iex> token_id = 10
iex> insert(:token_transfer,
...> from_address: contract_address,
...> token_contract_address: contract_address,
...> token_ids: [token_id]
...> )
iex> Explorer.Chain.erc721_or_erc1155_token_instance_exist?(token_id, contract_address.hash)
true
Returns `false` if not found
iex> {:ok, hash} = Explorer.Chain.string_to_address_hash("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
iex> Explorer.Chain.erc721_or_erc1155_token_instance_exist?(10, hash)
false
"""
@spec erc721_or_erc1155_token_instance_exist?(binary() | non_neg_integer(), Hash.Address.t()) :: boolean()
def erc721_or_erc1155_token_instance_exist?(token_id, hash) do
query =
from(tt in TokenTransfer,
where: tt.token_contract_address_hash == ^hash and (tt.token_id == ^token_id or ^token_id in tt.token_ids)
)
Repo.exists?(query)
end
defp boolean_to_check_result(true), do: :ok
defp boolean_to_check_result(false), do: :not_found

@ -177,7 +177,7 @@ defmodule Explorer.Chain.TokenTransfer do
from(
tt in TokenTransfer,
where: tt.token_contract_address_hash == ^token_address_hash,
where: tt.token_id == ^token_id,
where: tt.token_id == ^token_id or ^token_id in tt.token_ids,
where: not is_nil(tt.block_number),
preload: [{:transaction, :block}, :token, :from_address, :to_address],
order_by: [desc: tt.block_number]
@ -206,7 +206,9 @@ defmodule Explorer.Chain.TokenTransfer do
query =
from(
tt in TokenTransfer,
where: tt.token_contract_address_hash == ^token_address_hash and tt.token_id == ^token_id,
where:
tt.token_contract_address_hash == ^token_address_hash and
(tt.token_id == ^token_id or ^token_id in tt.token_ids),
select: fragment("COUNT(*)")
)

Loading…
Cancel
Save