Merge pull request #7097 from blockscout/vb-force-display-token-instance-page

Force display token instance page
pull/7119/head
Victor Baranov 2 years ago committed by GitHub
commit d3758c0d7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 33
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex
  3. 21
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/helper.ex
  4. 24
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/holder_controller.ex
  5. 15
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/metadata_controller.ex
  6. 24
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex
  7. 4
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex
  8. 23
      apps/block_scout_web/test/block_scout_web/controllers/address_token_controller_test.exs
  9. 6
      apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs
  10. 2
      apps/block_scout_web/test/block_scout_web/controllers/tokens/instance/transfer_controller_test.exs
  11. 2
      apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs
  12. 8
      apps/block_scout_web/test/block_scout_web/views/nft_helpers_test.exs
  13. 19
      apps/explorer/lib/explorer/chain.ex
  14. 4
      apps/explorer/lib/explorer/chain/token_transfer.ex

@ -26,6 +26,7 @@
- [#7144](https://github.com/blockscout/blockscout/pull/7144) - Update Blockscout logo - [#7144](https://github.com/blockscout/blockscout/pull/7144) - Update Blockscout logo
- [#7136](https://github.com/blockscout/blockscout/pull/7136) - Add release link or commit hash to docker images - [#7136](https://github.com/blockscout/blockscout/pull/7136) - Add release link or commit hash to docker images
- [#7097](https://github.com/blockscout/blockscout/pull/7097) - Force display token instance page
- [#7072](https://github.com/blockscout/blockscout/pull/7072) - Add a separate docker compose for geth with clique consensus - [#7072](https://github.com/blockscout/blockscout/pull/7072) - Add a separate docker compose for geth with clique consensus
- [#7056](https://github.com/blockscout/blockscout/pull/7056) - Add path_helper in interact.js - [#7056](https://github.com/blockscout/blockscout/pull/7056) - Add path_helper in interact.js
- [#7040](https://github.com/blockscout/blockscout/pull/7040) - Use alias BlockScoutWeb.Cldr.Number - [#7040](https://github.com/blockscout/blockscout/pull/7040) - Use alias BlockScoutWeb.Cldr.Number

@ -110,29 +110,33 @@ defmodule BlockScoutWeb.API.V2.TokenController do
end end
end end
def instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id} = params) do def instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id_str} = params) do
with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
{:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)},
{:not_found, {:ok, token_instance}} <- {:not_found, false} <- {:not_found, Chain.is_erc_20_token?(token)},
{:not_found, {:format, {token_id, ""}} <- {:format, Integer.parse(token_id_str)} do
Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, address_hash, @api_true)} do token_instance =
case Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, address_hash, @api_true) do
{:ok, token_instance} -> token_instance |> Chain.put_owner_to_token_instance(@api_true)
{:error, :not_found} -> %{token_id: token_id, metadata: nil, owner: nil}
end
conn conn
|> put_status(200) |> put_status(200)
|> render(:token_instance, %{ |> render(:token_instance, %{
token_instance: token_instance |> Chain.put_owner_to_token_instance(@api_true), token_instance: token_instance,
token: token token: token
}) })
end end
end end
def transfers_by_instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id} = params) do def transfers_by_instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id_str} = params) do
with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
{:not_found, {:ok, _token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)},
{:not_found, {:ok, _token_instance}} <- {:not_found, false} <- {:not_found, Chain.is_erc_20_token?(token)},
{:not_found, {:format, {token_id, ""}} <- {:format, Integer.parse(token_id_str)} do
Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, address_hash, @api_true)} do
paging_options = paging_options(params) paging_options = paging_options(params)
results = results =
@ -155,13 +159,12 @@ defmodule BlockScoutWeb.API.V2.TokenController do
end end
end end
def transfers_count_by_instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id} = params) do def transfers_count_by_instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id_str} = params) do
with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
{:not_found, {:ok, _token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)},
{:not_found, {:ok, _token_instance}} <- {:not_found, false} <- {:not_found, Chain.is_erc_20_token?(token)},
{:not_found, {:format, {token_id, ""}} <- {:format, Integer.parse(token_id_str)} do
Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, address_hash, @api_true)} do
conn conn
|> put_status(200) |> put_status(200)
|> json(%{ |> json(%{

@ -0,0 +1,21 @@
defmodule BlockScoutWeb.Tokens.Instance.Helper do
@moduledoc """
Token instance controllers common helper
"""
use BlockScoutWeb, :controller
alias BlockScoutWeb.Controller
alias Explorer.Chain
def render(conn, token_instance, hash, token_id, token) do
render(
conn,
"index.html",
token_instance: %{instance: token_instance, token_id: Decimal.new(token_id)},
current_path: Controller.current_full_path(conn),
token: token,
total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id)
)
end
end

@ -1,17 +1,19 @@
defmodule BlockScoutWeb.Tokens.Instance.HolderController do defmodule BlockScoutWeb.Tokens.Instance.HolderController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
alias BlockScoutWeb.Controller
alias BlockScoutWeb.Tokens.HolderView alias BlockScoutWeb.Tokens.HolderView
alias BlockScoutWeb.Tokens.Instance.Helper
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Address alias Explorer.Chain.Address
alias Phoenix.View alias Phoenix.View
import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3]
def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id, "type" => "JSON"} = params) do def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id_str, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(token_address_hash), with {:ok, address_hash} <- Chain.string_to_address_hash(token_address_hash),
{:ok, token} <- Chain.token_from_address_hash(address_hash), {:ok, token} <- Chain.token_from_address_hash(address_hash),
false <- Chain.is_erc_20_token?(token),
{token_id, ""} <- Integer.parse(token_id_str),
token_holders <- token_holders <-
Chain.fetch_token_holders_from_token_hash_and_token_id(address_hash, token_id, paging_options(params)) do Chain.fetch_token_holders_from_token_hash_and_token_id(address_hash, token_id, paging_options(params)) do
{token_holders_paginated, next_page} = split_list_by_page(token_holders) {token_holders_paginated, next_page} = split_list_by_page(token_holders)
@ -51,21 +53,17 @@ defmodule BlockScoutWeb.Tokens.Instance.HolderController do
end end
end end
def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id}) do def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id_str}) do
options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}]
with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash),
{:ok, token} <- Chain.token_from_address_hash(hash, options), {:ok, token} <- Chain.token_from_address_hash(hash, options),
{:ok, token_instance} <- false <- Chain.is_erc_20_token?(token),
Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, hash) do {token_id, ""} <- Integer.parse(token_id_str) do
render( case Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, hash) do
conn, {:ok, token_instance} -> Helper.render(conn, token_instance, hash, token_id, token)
"index.html", {:error, :not_found} -> Helper.render(conn, nil, hash, token_id, token)
token_instance: %{instance: token_instance, token_id: Decimal.new(token_id)}, end
current_path: Controller.current_full_path(conn),
token: token,
total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id)
)
else else
_ -> _ ->
not_found(conn) not_found(conn)

@ -1,25 +1,20 @@
defmodule BlockScoutWeb.Tokens.Instance.MetadataController do defmodule BlockScoutWeb.Tokens.Instance.MetadataController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
alias BlockScoutWeb.Controller alias BlockScoutWeb.Tokens.Instance.Helper
alias Explorer.Chain alias Explorer.Chain
def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id}) do def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id_str}) do
options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}]
with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash),
{:ok, token} <- Chain.token_from_address_hash(hash, options), {:ok, token} <- Chain.token_from_address_hash(hash, options),
false <- Chain.is_erc_20_token?(token),
{token_id, ""} <- Integer.parse(token_id_str),
{:ok, token_instance} <- {:ok, token_instance} <-
Chain.erc721_or_erc1155_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
if token_instance.metadata do if token_instance.metadata do
render( Helper.render(conn, token_instance, hash, token_id, token)
conn,
"index.html",
token_instance: %{instance: token_instance, token_id: Decimal.new(token_id)},
current_path: Controller.current_full_path(conn),
token: token,
total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id)
)
else else
not_found(conn) not_found(conn)
end end

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.Tokens.Instance.TransferController do defmodule BlockScoutWeb.Tokens.Instance.TransferController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
alias BlockScoutWeb.Controller alias BlockScoutWeb.Tokens.Instance.Helper
alias BlockScoutWeb.Tokens.TransferView alias BlockScoutWeb.Tokens.TransferView
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Address alias Explorer.Chain.Address
@ -12,9 +12,11 @@ defmodule BlockScoutWeb.Tokens.Instance.TransferController do
{:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000")
@burn_address_hash burn_address_hash @burn_address_hash burn_address_hash
def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id, "type" => "JSON"} = params) do def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id_str, "type" => "JSON"} = params) do
with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash),
{:ok, token} <- Chain.token_from_address_hash(hash), {:ok, token} <- Chain.token_from_address_hash(hash),
false <- Chain.is_erc_20_token?(token),
{token_id, ""} <- Integer.parse(token_id_str),
token_transfers <- token_transfers <-
Chain.fetch_token_transfers_from_token_hash_and_token_id(hash, token_id, paging_options(params)) do Chain.fetch_token_transfers_from_token_hash_and_token_id(hash, token_id, paging_options(params)) do
{token_transfers_paginated, next_page} = split_list_by_page(token_transfers) {token_transfers_paginated, next_page} = split_list_by_page(token_transfers)
@ -53,21 +55,17 @@ defmodule BlockScoutWeb.Tokens.Instance.TransferController do
end end
end end
def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id}) do def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id_str}) do
options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}]
with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash),
{:ok, token} <- Chain.token_from_address_hash(hash, options), {:ok, token} <- Chain.token_from_address_hash(hash, options),
{:ok, token_instance} <- false <- Chain.is_erc_20_token?(token),
Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, hash) do {token_id, ""} <- Integer.parse(token_id_str) do
render( case Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, hash) do
conn, {:ok, token_instance} -> Helper.render(conn, token_instance, hash, token_id, token)
"index.html", {:error, :not_found} -> Helper.render(conn, nil, hash, token_id, token)
token_instance: %{instance: token_instance, token_id: Decimal.new(token_id)}, end
current_path: Controller.current_full_path(conn),
token: token,
total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id)
)
else else
_ -> _ ->
not_found(conn) not_found(conn)

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

@ -133,20 +133,19 @@ defmodule BlockScoutWeb.AddressTokenControllerTest do
test "returns next page of results based on last seen token for erc-1155", %{conn: conn} do test "returns next page of results based on last seen token for erc-1155", %{conn: conn} do
address = insert(:address) address = insert(:address)
tokens = 1..51
1..51 |> Enum.reduce([], fn _i, acc ->
|> Enum.reduce([], fn i, acc -> token = insert(:token, name: "FN2 Token", type: "ERC-1155")
token = insert(:token, name: "FN2 Token", type: "ERC-1155")
insert( insert(
:address_current_token_balance, :address_current_token_balance,
token_contract_address_hash: token.contract_address_hash, token_contract_address_hash: token.contract_address_hash,
address: address, address: address,
value: 3 value: 3
) )
acc ++ [token.name] acc ++ [token.name]
end) end)
conn = conn =
get(conn, address_token_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash)), %{ get(conn, address_token_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash)), %{

@ -489,7 +489,7 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do
end end
test "get token instance by token id", %{conn: conn} do test "get token instance by token id", %{conn: conn} do
token = insert(:token) token = insert(:token, type: "ERC-721")
for _ <- 0..50 do for _ <- 0..50 do
insert(:token_instance, token_id: 0) insert(:token_instance, token_id: 0)
@ -713,7 +713,7 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do
end end
test "receive 0 count", %{conn: conn} do test "receive 0 count", %{conn: conn} do
token = insert(:token) token = insert(:token, type: "ERC-721")
insert(:token_instance, token_id: 0, token_contract_address_hash: token.contract_address_hash) insert(:token_instance, token_id: 0, token_contract_address_hash: token.contract_address_hash)
@ -723,7 +723,7 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do
end end
test "get count > 0", %{conn: conn} do test "get count > 0", %{conn: conn} do
token = insert(:token) token = insert(:token, type: "ERC-721")
for _ <- 0..50 do for _ <- 0..50 do
insert(:token_instance, token_id: 0) insert(:token_instance, token_id: 0)

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.Tokens.Instance.TransferControllerTest do
describe "GET token-transfers/2" do describe "GET token-transfers/2" do
test "fetches the instance", %{conn: conn} do test "fetches the instance", %{conn: conn} do
token = insert(:token) token = insert(:token, type: "ERC-721")
contract_address_hash = token.contract_address_hash contract_address_hash = token.contract_address_hash

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.Tokens.InstanceControllerTest do
describe "GET show/2" do describe "GET show/2" do
test "redirects with valid params", %{conn: conn} do test "redirects with valid params", %{conn: conn} do
token = insert(:token) token = insert(:token, type: "ERC-721")
contract_address_hash = token.contract_address_hash contract_address_hash = token.contract_address_hash

@ -1,28 +1,28 @@
defmodule BlockScoutWeb.NFTHelpersTest do defmodule BlockScoutWeb.NFTHelpersTest do
use BlockScoutWeb.ConnCase, async: true use BlockScoutWeb.ConnCase, async: true
alias BlockScoutWeb.{NFTHelpers} alias BlockScoutWeb.NFTHelpers
describe "compose_ipfs_url/1" do describe "compose_ipfs_url/1" do
test "transforms ipfs link like ipfs://${id}" do test "transforms ipfs link like ipfs://${id}" do
url = "ipfs://QmYFf7D2UtqnNz8Lu57Gnk3dxgdAiuboPWMEaNNjhr29tS/hidden.png" url = "ipfs://QmYFf7D2UtqnNz8Lu57Gnk3dxgdAiuboPWMEaNNjhr29tS/hidden.png"
assert "https://ipfs.io/ipfs/QmYFf7D2UtqnNz8Lu57Gnk3dxgdAiuboPWMEaNNjhr29tS/hidden.png" == assert "https://ipfs.io/ipfs/QmYFf7D2UtqnNz8Lu57Gnk3dxgdAiuboPWMEaNNjhr29tS/hidden.png" ==
BlockScoutWeb.NFTHelpers.compose_ipfs_url(url) NFTHelpers.compose_ipfs_url(url)
end end
test "transforms ipfs link like ipfs://ipfs" do test "transforms ipfs link like ipfs://ipfs" do
url = "ipfs://ipfs/Qmbgk4Ps5kiVdeYCHufMFgqzWLFuovFRtenY5P8m9vr9XW/animation.mp4" url = "ipfs://ipfs/Qmbgk4Ps5kiVdeYCHufMFgqzWLFuovFRtenY5P8m9vr9XW/animation.mp4"
assert "https://ipfs.io/ipfs/Qmbgk4Ps5kiVdeYCHufMFgqzWLFuovFRtenY5P8m9vr9XW/animation.mp4" == assert "https://ipfs.io/ipfs/Qmbgk4Ps5kiVdeYCHufMFgqzWLFuovFRtenY5P8m9vr9XW/animation.mp4" ==
BlockScoutWeb.NFTHelpers.compose_ipfs_url(url) NFTHelpers.compose_ipfs_url(url)
end end
test "transforms ipfs link in different case" do test "transforms ipfs link in different case" do
url = "IpFs://baFybeid4ed2ua7fwupv4nx2ziczr3edhygl7ws3yx6y2juon7xakgj6cfm/51.json" url = "IpFs://baFybeid4ed2ua7fwupv4nx2ziczr3edhygl7ws3yx6y2juon7xakgj6cfm/51.json"
assert "https://ipfs.io/ipfs/baFybeid4ed2ua7fwupv4nx2ziczr3edhygl7ws3yx6y2juon7xakgj6cfm/51.json" == assert "https://ipfs.io/ipfs/baFybeid4ed2ua7fwupv4nx2ziczr3edhygl7ws3yx6y2juon7xakgj6cfm/51.json" ==
BlockScoutWeb.NFTHelpers.compose_ipfs_url(url) NFTHelpers.compose_ipfs_url(url)
end end
end end
end end

@ -5042,7 +5042,7 @@ defmodule Explorer.Chain do
TokenTransfer.fetch_token_transfers_from_token_hash(token_address_hash, options) TokenTransfer.fetch_token_transfers_from_token_hash(token_address_hash, options)
end end
@spec fetch_token_transfers_from_token_hash_and_token_id(Hash.t(), binary(), [paging_options]) :: [] @spec fetch_token_transfers_from_token_hash_and_token_id(Hash.t(), non_neg_integer(), [paging_options]) :: []
def fetch_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options \\ []) do def fetch_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options \\ []) do
TokenTransfer.fetch_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options) TokenTransfer.fetch_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options)
end end
@ -5052,7 +5052,7 @@ defmodule Explorer.Chain do
TokenTransfer.count_token_transfers_from_token_hash(token_address_hash) TokenTransfer.count_token_transfers_from_token_hash(token_address_hash)
end end
@spec count_token_transfers_from_token_hash_and_token_id(Hash.t(), binary(), [api?]) :: non_neg_integer() @spec count_token_transfers_from_token_hash_and_token_id(Hash.t(), non_neg_integer(), [api?]) :: non_neg_integer()
def count_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options \\ []) do def count_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options \\ []) do
TokenTransfer.count_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options) TokenTransfer.count_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options)
end end
@ -5233,7 +5233,7 @@ defmodule Explorer.Chain do
|> select_repo(options).all() |> select_repo(options).all()
end end
@spec erc721_or_erc1155_token_instance_from_token_id_and_token_address(binary(), Hash.Address.t(), [api?]) :: @spec erc721_or_erc1155_token_instance_from_token_id_and_token_address(non_neg_integer(), Hash.Address.t(), [api?]) ::
{:ok, Instance.t()} | {:error, :not_found} {:ok, Instance.t()} | {:error, :not_found}
def erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, token_contract_address, options \\ []) do def erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, token_contract_address, options \\ []) do
query = query =
@ -5649,6 +5649,7 @@ defmodule Explorer.Chain do
|> TypeDecoder.decode_raw(types) |> TypeDecoder.decode_raw(types)
end end
@spec get_token_type(Hash.Address.t()) :: String.t() | nil
def get_token_type(hash) do def get_token_type(hash) do
query = query =
from( from(
@ -5660,6 +5661,18 @@ defmodule Explorer.Chain do
Repo.one(query) Repo.one(query)
end end
@spec is_erc_20_token?(Token.t()) :: bool
def is_erc_20_token?(token) do
is_erc_20_token_type?(token.type)
end
defp is_erc_20_token_type?(type) do
case type do
"ERC-20" -> true
_ -> false
end
end
@doc """ @doc """
Checks if an `t:Explorer.Chain.Address.t/0` with the given `hash` exists. Checks if an `t:Explorer.Chain.Address.t/0` with the given `hash` exists.

@ -173,7 +173,7 @@ defmodule Explorer.Chain.TokenTransfer do
|> Chain.select_repo(options).all() |> Chain.select_repo(options).all()
end end
@spec fetch_token_transfers_from_token_hash_and_token_id(Hash.t(), binary(), [paging_options | api?]) :: [] @spec fetch_token_transfers_from_token_hash_and_token_id(Hash.t(), non_neg_integer(), [paging_options | api?]) :: []
def fetch_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options) do def fetch_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options) do
paging_options = Keyword.get(options, :paging_options, @default_paging_options) paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@ -205,7 +205,7 @@ defmodule Explorer.Chain.TokenTransfer do
Repo.one(query, timeout: :infinity) Repo.one(query, timeout: :infinity)
end end
@spec count_token_transfers_from_token_hash_and_token_id(Hash.t(), binary(), [api?]) :: non_neg_integer() @spec count_token_transfers_from_token_hash_and_token_id(Hash.t(), non_neg_integer(), [api?]) :: non_neg_integer()
def count_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options) do def count_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id, options) do
query = query =
from( from(

Loading…
Cancel
Save