From c685cd00fdf53e9f0d28ef6a82a0b9de218c8e7d Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 21 Apr 2021 18:22:16 +0300 Subject: [PATCH] ERC-1155 transfers support in UI (cherry picked from commit d8b6f073c1132bf389adafb440643ea09a30d4fa) --- .../_token_balances.html.eex | 9 ++++ .../address_token_balance/_tokens.html.eex | 7 +++- ..._token_transfer_type_display_name.html.eex | 12 ++++++ .../tokens/holder/_token_balances.html.eex | 2 +- .../tokens/transfer/_token_transfer.html.eex | 9 +--- .../transaction/_total_transfers.html.eex | 9 +++- .../_token_transfer.html.eex | 9 +--- .../views/api/rpc/address_view.ex | 6 +++ .../block_scout_web/views/tokens/helpers.ex | 8 ++-- .../views/tokens/holder_view.ex | 12 ++++-- .../views/tokens/overview_view.ex | 1 + .../views/tokens/transfer_view.ex | 1 + .../views/transaction_token_transfer_view.ex | 2 + .../block_scout_web/views/transaction_view.ex | 37 ++++++++--------- .../views/tokens/holder_view_test.exs | 6 +-- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 6 ++- apps/explorer/lib/explorer/chain.ex | 32 +++++++++++++-- .../chain/address/current_token_balance.ex | 8 +++- apps/explorer/lib/explorer/chain/token.ex | 1 + .../lib/explorer/chain/token_transfer.ex | 1 + .../lib/explorer/token/balance_reader.ex | 41 ++++++++++++------- ...add_token_id_to_current_token_balances.exs | 12 ++++++ .../test/explorer/chain/import_test.exs | 9 ++-- .../indexer/fetcher/token_balance_test.exs | 16 -------- 24 files changed, 166 insertions(+), 90 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex create mode 100644 apps/explorer/priv/repo/migrations/20210422115740_add_token_id_to_current_token_balances.exs diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex index 502df98049..6f388fbdc4 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex @@ -59,6 +59,15 @@ ) %> <% end %> + <%= if Enum.any?(@token_balances, fn {token_balance, _, _} -> token_balance.token.type == "ERC-1155" end) do %> + <%= render( + "_tokens.html", + conn: @conn, + token_balances: filter_by_type(@token_balances, "ERC-1155"), + type: "ERC-721" + ) %> + <% end %> + <%= if Enum.any?(@token_balances, fn {token_balance, _, _} -> token_balance.token.type == "ERC-20" end) do %> <%= render( "_tokens.html", diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_tokens.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_tokens.html.eex index 3d3ea373b6..96dfcc584f 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_tokens.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_tokens.html.eex @@ -35,8 +35,11 @@
<% col_md = if token_balance.token.usd_value, do: "col-md-6", else: "col-md-12" %> -

- <%= format_according_to_decimals(token_balance.value, token.decimals) %> <%= token_symbol(token) %> +

+ <%= format_according_to_decimals(token_balance.value, token_balance.token.decimals) %> <%= token_balance.token.symbol %> + <%= if token_balance.token_type == "ERC-1155" do %> + <%= " TokenID " <> to_string(token_balance.token_id) %> + <% end %>

<%= if token_balance.token.usd_value do %>

diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex new file mode 100644 index 0000000000..d79aff67ed --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex @@ -0,0 +1,12 @@ +<%= case @type do %> + <% :token_burning -> %> + <%= gettext("Token Burning") %> + <% :token_minting -> %> + <%= gettext("Token Minting") %> + <% :token_spawning -> %> + <%= gettext("Token Creation") %> + <% :token_transfer -> %> + <%= gettext("Token Transfer") %> + <% _ -> %> + <%= gettext("Token Transfer") %> +<% end %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex index 105207a70c..f8fecf59a6 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex @@ -7,7 +7,7 @@ - <%= format_token_balance_value(@token_balance.value, @token) %> <%= @token.symbol %> + <%= format_token_balance_value(@token_balance.value, @token_balance.token_id, @token) %> <%= @token.symbol %> <%= if show_total_supply_percentage?(@token.total_supply) do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex index dc2e48c014..d1d151ffe2 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex @@ -3,14 +3,7 @@

- <%= cond do %> - <% @token_transfer.to_address.hash == @burn_address_hash -> %> - <%= gettext("Token Burning") %> - <% @token_transfer.from_address.hash == @burn_address_hash -> %> - <%= gettext("Token Minting") %> - <% true -> %> - <%= gettext("Token Transfer") %> - <% end %> + <%= render(BlockScoutWeb.CommonComponentsView, "_token_transfer_type_display_name.html", type: Chain.get_token_transfer_type(@token_transfer)) %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex index c0b0d09119..d056ea0536 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex @@ -1,8 +1,15 @@ <%= case token_transfer_amount(@transfer) do %> <% {:ok, :erc721_instance} -> %> <%= "TokenID ["%><%= link(short_token_id(@transfer.token_id, 30), to: token_instance_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash, to_string(@transfer.token_id)), "data-test": "token_link") %><%= "]" %> + <% {:ok, :erc1155_instance, value} -> %> + <% transfer_type = Chain.get_token_transfer_type(@transfer) %> + <%= if transfer_type == :token_spawning do %> + <%= "TokenID ["%><%= link(short_token_id(@transfer.token_id, 30), to: token_instance_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash, to_string(@transfer.token_id)), "data-test": "token_link") %><%= "]" %> + <% else %> + <%= "#{value} TokenID ["%><%= link(short_token_id(@transfer.token_id, 30), to: token_instance_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash, to_string(@transfer.token_id)), "data-test": "token_link") %><%= "]" %> + <% end %> <% {:ok, value} -> %> <%= value %> <% end %> -<%= " "%> +<%= " " %> <%= link(token_symbol(@transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash), "data-test": "token_link") %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex index 3bbbe6a419..885990a377 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex @@ -1,14 +1,7 @@
- <%= cond do %> - <% @token_transfer.to_address.hash == @burn_address_hash -> %> - <%= gettext("Token Burning") %> - <% @token_transfer.from_address.hash == @burn_address_hash -> %> - <%= gettext("Token Minting") %> - <% true -> %> - <%= gettext("Token Transfer") %> - <% end %> + <%= render(BlockScoutWeb.CommonComponentsView, "_token_transfer_type_display_name.html", type: Chain.get_token_transfer_type(@token_transfer)) %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex index 30f9803259..cdc8b8375a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex @@ -172,6 +172,12 @@ defmodule BlockScoutWeb.API.RPC.AddressView do |> Map.put_new(:tokenID, token_transfer.token_id) end + defp prepare_token_transfer(%{token_type: "ERC-1155"} = token_transfer) do + token_transfer + |> prepare_common_token_transfer() + |> Map.put_new(:tokenID, token_transfer.token_id) + end + defp prepare_token_transfer(%{token_type: "ERC-20"} = token_transfer) do token_transfer |> prepare_common_token_transfer() diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex index 7c5bf2916c..f585fbd6ea 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex @@ -32,14 +32,14 @@ defmodule BlockScoutWeb.Tokens.Helpers do {:ok, CurrencyHelpers.format_according_to_decimals(amount, decimals)} end - defp do_token_transfer_amount(%Token{type: "ERC-1155", decimals: decimals}, amount, _token_id) do - {:ok, CurrencyHelpers.format_according_to_decimals(amount, decimals)} - end - defp do_token_transfer_amount(%Token{type: "ERC-721"}, _amount, _token_id) do {:ok, :erc721_instance} end + defp do_token_transfer_amount(%Token{type: "ERC-1155", decimals: decimals}, amount, _token_id) do + {:ok, :erc1155_instance, CurrencyHelpers.format_according_to_decimals(amount, decimals)} + end + defp do_token_transfer_amount(_token, _amount, _token_id) do nil end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex index e185e3f9e8..2edfd93981 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex @@ -54,19 +54,23 @@ defmodule BlockScoutWeb.Tokens.HolderView do ## Examples iex> token = build(:token, type: "ERC-20", decimals: Decimal.new(2)) - iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(100000, token) + iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(100000, nil, token) "1,000" iex> token = build(:token, type: "ERC-721") - iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(1, token) + iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(1, nil, token) 1 """ - def format_token_balance_value(value, %Token{type: "ERC-20", decimals: decimals}) do + def format_token_balance_value(value, _id, %Token{type: "ERC-20", decimals: decimals}) do format_according_to_decimals(value, decimals) end - def format_token_balance_value(value, _token) do + def format_token_balance_value(value, id, %Token{type: "ERC-1155", decimals: decimals}) do + to_string(format_according_to_decimals(value, decimals)) <> " TokenID " <> to_string(id) + end + + def format_token_balance_value(value, _id, _token) do value end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex index f18b68ee74..b2ac3a6832 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex @@ -44,6 +44,7 @@ defmodule BlockScoutWeb.Tokens.OverviewView do defp tab_name(["inventory"]), do: gettext("Inventory") def display_inventory?(%Token{type: "ERC-721"}), do: true + def display_inventory?(%Token{type: "ERC-1155"}), do: true def display_inventory?(_), do: false def smart_contract_with_read_only_functions?( diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex index 96f29c5fe0..3ea5d84840 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex @@ -2,5 +2,6 @@ defmodule BlockScoutWeb.Tokens.TransferView do use BlockScoutWeb, :view alias BlockScoutWeb.Tokens.OverviewView + alias Explorer.Chain alias Explorer.Chain.Address end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex index eb8fe81053..66999ad804 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.TransactionTokenTransferView do use BlockScoutWeb, :view + + alias Explorer.Chain end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index 84856e8af9..da4c5eea10 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -123,32 +123,21 @@ defmodule BlockScoutWeb.TransactionView do end ) - transfers = ft_transfers ++ nft_transfers + final_ft_transfers = Map.values(ft_transfers) + transfers = final_ft_transfers ++ nft_transfers - mintings = ft_mintings ++ nft_mintings + final_ft_mintings = Map.values(ft_mintings) + mintings = final_ft_mintings ++ nft_mintings - burnings = ft_burnings ++ nft_burnings + final_ft_burnings = Map.values(ft_burnings) + burnings = final_ft_burnings ++ nft_burnings - creations = ft_creations ++ nft_creations + final_ft_creations = Map.values(ft_creations) + creations = final_ft_creations ++ nft_creations %{transfers: transfers, mintings: mintings, burnings: burnings, creations: creations} end - defp aggregate_reducer(%{amount: amount, amounts: amounts} = token_transfer, {acc1, acc2}) - when is_nil(amount) and is_nil(amounts) do - new_entry = %{ - token: token_transfer.token, - amount: nil, - amounts: [], - token_id: token_transfer.token_id, - token_ids: [], - to_address_hash: token_transfer.to_address_hash, - from_address_hash: token_transfer.from_address_hash - } - - {acc1, [new_entry | acc2]} - end - defp aggregate_reducer(%{amount: amount, amounts: amounts} = token_transfer, {acc1, acc2}) when is_nil(amount) and not is_nil(amounts) do new_entry = %{ @@ -220,6 +209,7 @@ defmodule BlockScoutWeb.TransactionView do case type do :erc20 -> gettext("ERC-20 ") :erc721 -> gettext("ERC-721 ") + :erc1155 -> gettext("ERC-1155 ") _ -> "" end end @@ -531,6 +521,7 @@ defmodule BlockScoutWeb.TransactionView do creations_count = Enum.count(token_transfers_types, fn token_transfers_type -> token_transfers_type == @token_creation_type end) +<<<<<<< HEAD cond do Enum.count(token_transfers_types) == burnings_count -> @token_burning_type @@ -554,6 +545,14 @@ defmodule BlockScoutWeb.TransactionView do case Integer.parse(string_value) do {integer, ""} -> integer _ -> 0 +======= + + cond do + Enum.count(token_transfers_types) == burnings_count -> @token_burning_type + Enum.count(token_transfers_types) == mintings_count -> @token_minting_type + Enum.count(token_transfers_types) == creations_count -> @token_creation_type + true -> @token_transfer_type +>>>>>>> d8b6f073c (ERC-1155 transfers support in UI) end end diff --git a/apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs index 20bd85e7ef..6b5c6c853d 100644 --- a/apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs @@ -56,19 +56,19 @@ defmodule BlockScoutWeb.Tokens.HolderViewTest do end end - describe "format_token_balance_value/1" do + describe "format_token_balance_value/3" do test "formats according to token decimals when it's a ERC-20" do token = build(:token, type: "ERC-20", decimals: Decimal.new(2)) token_balance = build(:token_balance, value: 2_000_000) - assert HolderView.format_token_balance_value(token_balance.value, token) == "20,000" + assert HolderView.format_token_balance_value(token_balance.value, nil, token) == "20,000" end test "returns the value when it's ERC-721" do token = build(:token, type: "ERC-721") token_balance = build(:token_balance, value: 1) - assert HolderView.format_token_balance_value(token_balance.value, token) == 1 + assert HolderView.format_token_balance_value(token_balance.value, nil, token) == 1 end end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index a772f615c5..15e9c843c0 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -168,7 +168,11 @@ defmodule EthereumJSONRPC do """ @spec execute_contract_functions([Contract.call()], [map()], json_rpc_named_arguments) :: [Contract.call_result()] def execute_contract_functions(functions, abi, json_rpc_named_arguments) do - Contract.execute_contract_functions(functions, abi, json_rpc_named_arguments) + if Enum.count(functions) > 0 do + Contract.execute_contract_functions(functions, abi, json_rpc_named_arguments) + else + [] + end end @doc """ diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index fb6cb6414d..a3443730ba 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -5885,7 +5885,7 @@ defmodule Explorer.Chain do end @spec transaction_token_transfer_type(Transaction.t()) :: - :erc20 | :erc721 | :token_transfer | nil + :erc20 | :erc721 | :erc1155 | :token_transfer | nil def transaction_token_transfer_type( %Transaction{ status: :ok, @@ -5932,10 +5932,24 @@ defmodule Explorer.Chain do find_erc721_token_transfer(transaction.token_transfers, {from_address, to_address}) + # safeTransferFrom(address,address,uint256,uint256,bytes) + {"0xf242432a" <> params, ^zero_wei} -> + types = [:address, :address, {:uint, 256}, {:uint, 256}, :bytes] + [from_address, to_address, _id, _value, _data] = decode_params(params, types) + + find_erc1155_token_transfer(transaction.token_transfers, {from_address, to_address}) + + # safeBatchTransferFrom(address,address,uint256[],uint256[],bytes) + {"0x2eb2c2d6" <> params, ^zero_wei} -> + types = [:address, :address, [{:uint, 256}], [{:uint, 256}], :bytes] + [from_address, to_address, _ids, _values, _data] = decode_params(params, types) + + find_erc1155_token_transfer(transaction.token_transfers, {from_address, to_address}) + {"0xf907fc5b" <> _params, ^zero_wei} -> :erc20 - # check for ERC 20 or for old ERC 721 token versions + # check for ERC-20 or for old ERC-721, ERC-1155 token versions {unquote(TokenTransfer.transfer_function_signature()) <> params, ^zero_wei} -> types = [:address, {:uint, 256}] @@ -5943,7 +5957,7 @@ defmodule Explorer.Chain do decimal_value = Decimal.new(value) - find_erc721_or_erc20_token_transfer(transaction.token_transfers, {address, decimal_value}) + find_erc721_or_erc20_or_erc1155_token_transfer(transaction.token_transfers, {address, decimal_value}) _ -> nil @@ -5959,7 +5973,16 @@ defmodule Explorer.Chain do if token_transfer, do: :erc721 end - defp find_erc721_or_erc20_token_transfer(token_transfers, {address, decimal_value}) do + defp find_erc1155_token_transfer(token_transfers, {from_address, to_address}) do + token_transfer = + Enum.find(token_transfers, fn token_transfer -> + token_transfer.from_address_hash.bytes == from_address && token_transfer.to_address_hash.bytes == to_address + end) + + if token_transfer, do: :erc1155 + end + + defp find_erc721_or_erc20_or_erc1155_token_transfer(token_transfers, {address, decimal_value}) do token_transfer = Enum.find(token_transfers, fn token_transfer -> token_transfer.to_address_hash.bytes == address && token_transfer.amount == decimal_value @@ -5969,6 +5992,7 @@ defmodule Explorer.Chain do case token_transfer.token do %Token{type: "ERC-20"} -> :erc20 %Token{type: "ERC-721"} -> :erc721 + %Token{type: "ERC-1155"} -> :erc1155 _ -> nil end else diff --git a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex index cb09f0391c..bfa3971bbe 100644 --- a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex @@ -23,6 +23,8 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do * `token_contract_address_hash` - The contract address hash foreign key. * `block_number` - The block's number that the transfer took place. * `value` - The value that's represents the balance. + * `token_id` - The token_id of the transferred token (applicable for ERC-1155 and ERC-721 tokens) + * `token_type` - The type of the token """ @type t :: %__MODULE__{ address: %Ecto.Association.NotLoaded{} | Address.t(), @@ -39,6 +41,8 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do field(:value, :decimal) field(:block_number, :integer) field(:value_fetched_at, :utc_datetime_usec) + field(:token_id, :decimal) + field(:token_type, :string) # A transient field for deriving token holder count deltas during address_current_token_balances upserts field(:old_value, :decimal) @@ -56,8 +60,8 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do timestamps() end - @optional_fields ~w(value value_fetched_at)a - @required_fields ~w(address_hash block_number token_contract_address_hash)a + @optional_fields ~w(value value_fetched_at token_id)a + @required_fields ~w(address_hash block_number token_contract_address_hash token_type)a @allowed_fields @optional_fields ++ @required_fields @doc false diff --git a/apps/explorer/lib/explorer/chain/token.ex b/apps/explorer/lib/explorer/chain/token.ex index 990ac1fa34..686ee7c70f 100644 --- a/apps/explorer/lib/explorer/chain/token.ex +++ b/apps/explorer/lib/explorer/chain/token.ex @@ -8,6 +8,7 @@ defmodule Explorer.Chain.Token do * ERC-20 * ERC-721 + * ERC-1155 ## Token Specifications diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 91ead3b238..38c7318192 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -300,6 +300,7 @@ defmodule Explorer.Chain.TokenTransfer do 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], diff --git a/apps/explorer/lib/explorer/token/balance_reader.ex b/apps/explorer/lib/explorer/token/balance_reader.ex index a18bd053b3..837cc94c06 100644 --- a/apps/explorer/lib/explorer/token/balance_reader.ex +++ b/apps/explorer/lib/explorer/token/balance_reader.ex @@ -45,6 +45,13 @@ defmodule Explorer.Token.BalanceReader do def get_balances_of(token_balance_requests) do regular_balances = token_balance_requests + |> Enum.filter(fn request -> + if Map.has_key?(request, :token_type) do + request.token_type !== "ERC-1155" + else + true + end + end) |> Enum.map(&format_balance_request/1) |> Reader.query_contracts(@balance_function_abi) |> Enum.map(&format_balance_result/1) @@ -52,21 +59,13 @@ defmodule Explorer.Token.BalanceReader do erc1155_balances = token_balance_requests |> Enum.filter(fn request -> - request.token_type == "ERC-1155" - end) - |> Enum.map(fn %{ - address_hash: address_hash, - block_number: block_number, - token_contract_address_hash: token_contract_address_hash, - token_id: token_id - } -> - %{ - contract_address: token_contract_address_hash, - function_name: "balanceOf", - args: [address_hash, token_id], - block_number: block_number - } + if Map.has_key?(request, :token_type) do + request.token_type == "ERC-1155" + else + false + end end) + |> Enum.map(&format_erc_1155_balance_request/1) |> Reader.query_contracts(@erc1155_balance_function_abi) |> Enum.map(&format_balance_result/1) @@ -86,6 +85,20 @@ defmodule Explorer.Token.BalanceReader do } end + defp format_erc_1155_balance_request(%{ + address_hash: address_hash, + block_number: block_number, + token_contract_address_hash: token_contract_address_hash, + token_id: token_id + }) do + %{ + contract_address: token_contract_address_hash, + method_id: "00fdd58e", + args: [address_hash, token_id], + block_number: block_number + } + end + defp format_balance_result({:ok, [balance]}) do {:ok, balance} end diff --git a/apps/explorer/priv/repo/migrations/20210422115740_add_token_id_to_current_token_balances.exs b/apps/explorer/priv/repo/migrations/20210422115740_add_token_id_to_current_token_balances.exs new file mode 100644 index 0000000000..4d8d870529 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20210422115740_add_token_id_to_current_token_balances.exs @@ -0,0 +1,12 @@ +defmodule Explorer.Repo.Migrations.AddTokenIdToCurrentTokenBalances do + use Ecto.Migration + + def change do + alter table(:address_current_token_balances) do + add(:token_id, :numeric, precision: 78, scale: 0, null: true) + add(:token_type, :string, null: true) + end + + create(index(:address_current_token_balances, [:token_id])) + end +end diff --git a/apps/explorer/test/explorer/chain/import_test.exs b/apps/explorer/test/explorer/chain/import_test.exs index 9c7a47b94e..9cde0dbb81 100644 --- a/apps/explorer/test/explorer/chain/import_test.exs +++ b/apps/explorer/test/explorer/chain/import_test.exs @@ -428,13 +428,15 @@ defmodule Explorer.Chain.ImportTest do address_hash: "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", token_contract_address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b", block_number: "37", - value: 200 + value: 200, + token_type: "ERC-20" }, %{ address_hash: "0x515c09c5bba1ed566b02a5b0599ec5d5d0aee73d", token_contract_address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b", block_number: "37", - value: 100 + value: 100, + token_type: "ERC-20" } ], timeout: 5 @@ -2264,7 +2266,8 @@ defmodule Explorer.Chain.ImportTest do address_hash: address_hash, token_contract_address_hash: token_contract_address_hash, block_number: block_number, - value: value_after + value: value_after, + token_type: "ERC-20" } ] }, diff --git a/apps/indexer/test/indexer/fetcher/token_balance_test.exs b/apps/indexer/test/indexer/fetcher/token_balance_test.exs index a9c3c62848..bddeb2c32b 100644 --- a/apps/indexer/test/indexer/fetcher/token_balance_test.exs +++ b/apps/indexer/test/indexer/fetcher/token_balance_test.exs @@ -76,22 +76,6 @@ defmodule Indexer.Fetcher.TokenBalanceTest do token_balance_a = insert(:token_balance, value_fetched_at: nil, value: nil) token_balance_b = insert(:token_balance, value_fetched_at: nil, value: nil) - expect( - EthereumJSONRPC.Mox, - :json_rpc, - 1, - fn [%{id: id, method: "eth_call", params: [%{data: _, to: _}, _]}], _options -> - {:ok, - [ - %{ - error: %{code: -32015, data: "Reverted 0x", message: "VM execution error."}, - id: id, - jsonrpc: "2.0" - } - ]} - end - ) - token_balances = [ { token_balance_a.address_hash.bytes,