diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs index 1a1b7fa302..2f3ef1038c 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs @@ -2221,7 +2221,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do token_transfer = insert(:token_transfer, %{ token_contract_address: token_address, - token_id: 666, + token_ids: [666], transaction: transaction, block: transaction.block, block_number: transaction.block_number @@ -2241,7 +2241,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> get("/api", params) |> json_response(200) - assert result["tokenID"] == to_string(token_transfer.token_id) + assert result["tokenID"] == to_string(List.first(token_transfer.token_ids)) assert response["status"] == "1" assert response["message"] == "OK" assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) diff --git a/apps/explorer/lib/explorer/account/notifier/summary.ex b/apps/explorer/lib/explorer/account/notifier/summary.ex index 8ba3f2e9fc..2c795ce252 100644 --- a/apps/explorer/lib/explorer/account/notifier/summary.ex +++ b/apps/explorer/lib/explorer/account/notifier/summary.ex @@ -132,7 +132,7 @@ defmodule Explorer.Account.Notifier.Summary do from_address_hash: transfer.from_address_hash, to_address_hash: transfer.to_address_hash, block_number: transfer.block_number, - subject: to_string(transfer.token_id), + subject: to_string(List.first(transfer.token_ids)), tx_fee: fee(transaction), name: transfer.token.name, type: transfer.token.type @@ -193,14 +193,8 @@ defmodule Explorer.Account.Notifier.Summary do ) end - def token_ids(%Chain.TokenTransfer{token_id: token_id, token_ids: token_ids}) do - case token_id do - nil -> - Enum.map_join(token_ids, ", ", fn id -> to_string(id) end) - - _ -> - to_string(token_id) - end + def token_ids(%Chain.TokenTransfer{token_ids: token_ids}) do + Enum.map_join(token_ids, ", ", fn id -> to_string(id) end) end def token_decimals(%Chain.TokenTransfer{} = transfer) do diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 09123d9513..ab2be303c9 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -4714,29 +4714,35 @@ defmodule Explorer.Chain do select: token.contract_address_hash ) - query = + token_ids_query = from( token_transfer in TokenTransfer, + select: %{ + token_contract_address_hash: token_transfer.token_contract_address_hash, + token_id: fragment("unnest(?)", token_transfer.token_ids) + } + ) + + query = + from( + transfer in subquery(token_ids_query), inner_join: token in subquery(nft_tokens), - on: token.contract_address_hash == token_transfer.token_contract_address_hash, + on: token.contract_address_hash == transfer.token_contract_address_hash, left_join: instance in Instance, on: - token_transfer.token_contract_address_hash == instance.token_contract_address_hash and - (token_transfer.token_id == instance.token_id or - fragment("? @> ARRAY[?::decimal]", token_transfer.token_ids, instance.token_id)), - where: - is_nil(instance.token_id) and (not is_nil(token_transfer.token_id) or not is_nil(token_transfer.token_ids)), + transfer.token_contract_address_hash == instance.token_contract_address_hash and + transfer.token_id == instance.token_id, + where: is_nil(instance.token_id), select: %{ - contract_address_hash: token_transfer.token_contract_address_hash, - token_id: token_transfer.token_id, - token_ids: token_transfer.token_ids + contract_address_hash: transfer.token_contract_address_hash, + token_id: transfer.token_id } ) distinct_query = from( q in subquery(query), - distinct: [q.contract_address_hash, q.token_id, q.token_ids] + distinct: [q.contract_address_hash, q.token_id] ) Repo.stream_reduce(distinct_query, initial, reducer) @@ -5749,10 +5755,8 @@ defmodule Explorer.Chain do @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 fragment("? @> ARRAY[?::decimal]", tt.token_ids, ^Decimal.new(token_id))) + from(i in Instance, + where: i.token_contract_address_hash == ^hash and i.token_id == ^Decimal.new(token_id) ) Repo.exists?(query) diff --git a/apps/explorer/lib/explorer/chain/import/runner/token_transfers.ex b/apps/explorer/lib/explorer/chain/import/runner/token_transfers.ex index 337c6f5208..f50e98691e 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/token_transfers.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/token_transfers.ex @@ -89,19 +89,19 @@ defmodule Explorer.Chain.Import.Runner.TokenTransfers do from_address_hash: fragment("EXCLUDED.from_address_hash"), to_address_hash: fragment("EXCLUDED.to_address_hash"), token_contract_address_hash: fragment("EXCLUDED.token_contract_address_hash"), - token_id: fragment("EXCLUDED.token_id"), + token_ids: fragment("EXCLUDED.token_ids"), inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", token_transfer.inserted_at), updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", token_transfer.updated_at) ] ], where: fragment( - "(EXCLUDED.amount, EXCLUDED.from_address_hash, EXCLUDED.to_address_hash, EXCLUDED.token_contract_address_hash, EXCLUDED.token_id) IS DISTINCT FROM (?, ? ,? , ?, ?)", + "(EXCLUDED.amount, EXCLUDED.from_address_hash, EXCLUDED.to_address_hash, EXCLUDED.token_contract_address_hash, EXCLUDED.token_ids) IS DISTINCT FROM (?, ? ,? , ?, ?)", token_transfer.amount, token_transfer.from_address_hash, token_transfer.to_address_hash, token_transfer.token_contract_address_hash, - token_transfer.token_id + token_transfer.token_ids ) ) end diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 4c23267682..152614b4da 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -110,8 +110,8 @@ defmodule Explorer.Chain.TokenTransfer do type: Hash.Full ) - has_one( - :instance, + has_many( + :instances, Instance, foreign_key: :token_contract_address_hash, references: :token_contract_address_hash @@ -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 or fragment("? @> ARRAY[?::decimal]", tt.token_ids, ^Decimal.new(token_id)), + where: fragment("? @> ARRAY[?::decimal]", tt.token_ids, ^Decimal.new(token_id)), where: not is_nil(tt.block_number), preload: [{:transaction, :block}, :token, :from_address, :to_address], order_by: [desc: tt.block_number] @@ -208,7 +208,7 @@ defmodule Explorer.Chain.TokenTransfer do tt in TokenTransfer, where: tt.token_contract_address_hash == ^token_address_hash and - (tt.token_id == ^token_id or fragment("? @> ARRAY[?::decimal]", tt.token_ids, ^Decimal.new(token_id))), + fragment("? @> ARRAY[?::decimal]", tt.token_ids, ^Decimal.new(token_id)), select: fragment("COUNT(*)") ) @@ -218,11 +218,11 @@ defmodule Explorer.Chain.TokenTransfer do def page_token_transfer(query, %PagingOptions{key: nil}), do: query def page_token_transfer(query, %PagingOptions{key: {token_id}, asc_order: true}) do - where(query, [tt], tt.token_id > ^token_id) + where(query, [tt], fragment("?[1] > ?", tt.token_ids, ^token_id)) end def page_token_transfer(query, %PagingOptions{key: {token_id}}) do - where(query, [tt], tt.token_id < ^token_id) + where(query, [tt], fragment("?[1] < ?", tt.token_ids, ^token_id)) end def page_token_transfer(query, %PagingOptions{key: {block_number, log_index}, asc_order: true}) do @@ -304,27 +304,4 @@ defmodule Explorer.Chain.TokenTransfer do tt.block_number < ^block_number ) end - - @doc """ - Innventory tab query. - A token ERC-721 is considered unique because it corresponds to the possession - of a specific asset. - - To find out its current owner, it is necessary to look at the token last - transfer. - """ - @spec address_to_unique_tokens(Hash.Address.t()) :: Ecto.Query.t() - def address_to_unique_tokens(contract_address_hash) do - 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, - where: tt.token_contract_address_hash == ^contract_address_hash, - where: tt.to_address_hash != ^"0x0000000000000000000000000000000000000000", - order_by: [desc: tt.block_number], - distinct: [desc: tt.token_id], - preload: [:to_address], - select: %{tt | instance: instance} - ) - end end diff --git a/apps/explorer/test/explorer/account/notify/summary_test.exs b/apps/explorer/test/explorer/account/notify/summary_test.exs index 6402064b7e..9fcfb9230d 100644 --- a/apps/explorer/test/explorer/account/notify/summary_test.exs +++ b/apps/explorer/test/explorer/account/notify/summary_test.exs @@ -152,7 +152,7 @@ defmodule Explorer.Account.Notify.SummaryTest do :token_transfer |> insert( transaction: tx, - token_id: 42, + token_ids: [42], token_contract_address: token.contract_address ) |> Repo.preload([ @@ -198,7 +198,7 @@ defmodule Explorer.Account.Notify.SummaryTest do :token_transfer |> insert( transaction: tx, - token_id: 42, + token_ids: [42], token_contract_address: token.contract_address ) |> Repo.preload([ @@ -244,7 +244,6 @@ defmodule Explorer.Account.Notify.SummaryTest do :token_transfer |> insert( transaction: tx, - token_id: nil, token_ids: [23, 42], token_contract_address: token.contract_address ) diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 5e55b9ac1f..90dc2b3b7b 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -137,7 +137,7 @@ defmodule Explorer.ChainTest do end end - describe "ERC721_token_instance_from_token_id_and_token_address/2" do + describe "ERC721_or_ERC1155_token_instance_from_token_id_and_token_address/2" do test "return ERC721 token instance" do contract_address = insert(:address) @@ -146,11 +146,11 @@ defmodule Explorer.ChainTest do insert(:token_transfer, from_address: contract_address, token_contract_address: contract_address, - token_id: token_id + token_ids: [token_id] ) assert {:ok, result} = - Chain.erc721_token_instance_from_token_id_and_token_address(token_id, contract_address.hash) + Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, contract_address.hash) assert result.token_id == Decimal.new(token_id) end @@ -4837,41 +4837,15 @@ defmodule Explorer.ChainTest do transaction: transaction, token_contract_address: token_contract_address, token: token, - token_id: 11 - ) - - assert {:ok, [result]} = Chain.stream_unfetched_token_instances([], &[&1 | &2]) - assert result.token_id == token_transfer.token_id - assert result.contract_address_hash == token_transfer.token_contract_address_hash - end - - test "reduces with given reducer and accumulator for ERC-1155 token" do - token_contract_address = insert(:contract_address) - token = insert(:token, contract_address: token_contract_address, type: "ERC-1155") - - transaction = - :transaction - |> insert() - |> with_block(insert(:block, number: 1)) - - token_transfer = - insert( - :token_transfer, - block_number: 1000, - to_address: build(:address), - transaction: transaction, - token_contract_address: token_contract_address, - token: token, - token_id: nil, token_ids: [11] ) assert {:ok, [result]} = Chain.stream_unfetched_token_instances([], &[&1 | &2]) - assert result.token_ids == token_transfer.token_ids + assert result.token_id == List.first(token_transfer.token_ids) assert result.contract_address_hash == token_transfer.token_contract_address_hash end - test "does not fetch token transfers without token id or token_ids" do + test "does not fetch token transfers without token_ids" do token_contract_address = insert(:contract_address) token = insert(:token, contract_address: token_contract_address, type: "ERC-721") @@ -4887,7 +4861,6 @@ defmodule Explorer.ChainTest do transaction: transaction, token_contract_address: token_contract_address, token: token, - token_id: nil, token_ids: nil ) @@ -4911,11 +4884,11 @@ defmodule Explorer.ChainTest do transaction: transaction, token_contract_address: token_contract_address, token: token, - token_id: 11 + token_ids: [11] ) insert(:token_instance, - token_id: token_transfer.token_id, + token_id: List.first(token_transfer.token_ids), token_contract_address_hash: token_transfer.token_contract_address_hash ) diff --git a/apps/indexer/test/indexer/transform/token_transfers_test.exs b/apps/indexer/test/indexer/transform/token_transfers_test.exs index dbea962c32..c21947bf89 100644 --- a/apps/indexer/test/indexer/transform/token_transfers_test.exs +++ b/apps/indexer/test/indexer/transform/token_transfers_test.exs @@ -68,13 +68,13 @@ defmodule Indexer.Transform.TokenTransfersTest do from_address_hash: truncated_hash(log_3.second_topic), to_address_hash: truncated_hash(log_3.third_topic), token_contract_address_hash: log_3.address_hash, - token_id: 183, + token_ids: [183], transaction_hash: log_3.transaction_hash, token_type: "ERC-721", block_hash: log_3.block_hash }, %{ - token_id: nil, + token_ids: nil, amount: Decimal.new(17_000_000_000_000_000_000), block_number: log_1.block_number, log_index: log_1.index, @@ -122,7 +122,7 @@ defmodule Indexer.Transform.TokenTransfersTest do to_address_hash: "0xbe8cdfc13ffda20c844ac3da2b53a23ac5787f1e", block_hash: "0x79594150677f083756a37eee7b97ed99ab071f502104332cb3835bac345711ca", token_contract_address_hash: log.address_hash, - token_id: 14_939, + token_ids: [14_939], transaction_hash: log.transaction_hash, token_type: "ERC-721" } @@ -158,8 +158,9 @@ defmodule Indexer.Transform.TokenTransfersTest do log_index: 2, to_address_hash: "0x9c978f4cfa1fe13406bcc05baf26a35716f881dd", token_contract_address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb", - token_id: - 7_237_005_577_332_282_011_952_059_972_634_123_378_909_214_838_582_411_639_295_170_840_059_424_276_480, + token_ids: [ + 7_237_005_577_332_282_011_952_059_972_634_123_378_909_214_838_582_411_639_295_170_840_059_424_276_480 + ], token_type: "ERC-1155", transaction_hash: "0x6d2dd62c178e55a13b65601f227c4ffdd8aa4e3bcb1f24731363b4f7619e92c8" } @@ -198,7 +199,6 @@ defmodule Indexer.Transform.TokenTransfersTest do log_index: 2, to_address_hash: "0x6c943470780461b00783ad530a53913bd2c104d3", token_contract_address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb", - token_id: nil, token_ids: [680_564_733_841_876_926_926_749_214_863_536_422_912], token_type: "ERC-1155", transaction_hash: "0x6d2dd62c178e55a13b65601f227c4ffdd8aa4e3bcb1f24731363b4f7619e92c8",