From efdd82e8d8ff279bda9fb822feea0d94b4b9d088 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 28 Aug 2019 15:06:05 +0300 Subject: [PATCH 01/92] fetch ERC721 token instance by token_id --- .../controllers/tokens/instance_controller.ex | 16 ++++++++++++++++ .../lib/block_scout_web/web_router.ex | 7 +++++++ apps/explorer/lib/explorer/chain.ex | 15 +++++++++++++++ apps/explorer/test/explorer/chain_test.exs | 19 +++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex new file mode 100644 index 0000000000..2c2d1f6f2e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex @@ -0,0 +1,16 @@ +defmodule BlockScoutWeb.Tokens.InstanceController do + use BlockScoutWeb, :controller + + alias Explorer.Chain + + def show(conn, %{"id" => token_id, "token_address_hash" => token_address_hash}) do + with {:ok, token_address} <- Chain.hash_to_address(token_address_hash, []), + {:ok, token_transfer} <- + Chain.erc721_token_instance_from_token_id_and_token_address(token_id, token_address.hash) do + json(conn, token_transfer) + else + _ -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/web_router.ex b/apps/block_scout_web/lib/block_scout_web/web_router.ex index 1bbd57d041..18589ca0e1 100644 --- a/apps/block_scout_web/lib/block_scout_web/web_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/web_router.ex @@ -178,6 +178,13 @@ defmodule BlockScoutWeb.WebRouter do only: [:index], as: :inventory ) + + resources( + "/token_instance", + Tokens.InstanceController, + only: [:show], + as: :instance + ) end resources( diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 58253c0f27..6c59dc2847 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2959,6 +2959,21 @@ defmodule Explorer.Chain do |> Repo.all() end + @spec erc721_token_instance_from_token_id_and_token_address(binary(), Hash.Address.t()) :: TokenTransfer.t() | nil + def erc721_token_instance_from_token_id_and_token_address(token_id, token_contract_address) do + query = + from(tt in TokenTransfer, + where: tt.token_contract_address_hash == ^token_contract_address and tt.token_id == ^token_id, + order_by: [desc: tt.block_number], + limit: 1 + ) + + case Repo.one(query) do + nil -> {:error, :not_found} + token_instance -> {:ok, token_instance} + end + end + @spec address_to_coin_balances(Hash.Address.t(), [paging_options]) :: [] def address_to_coin_balances(address_hash, options) do paging_options = Keyword.get(options, :paging_options, @default_paging_options) diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index f49be6da4e..5fa5b58f7d 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -82,6 +82,25 @@ defmodule Explorer.ChainTest do end end + describe "ERC721_token_instance_from_token_id_and_token_address/2" do + test "return ERC721 token instance" do + contract_address = insert(:address) + + token_id = 10 + + insert(:token_transfer, + from_address: contract_address, + token_contract_address: contract_address, + token_id: token_id + ) + + assert {:ok, result} = + Chain.erc721_token_instance_from_token_id_and_token_address(token_id, contract_address.hash) + + assert result.token_id == Decimal.new(token_id) + end + end + describe "address_to_logs/2" do test "fetches logs" do %Address{hash: address_hash} = address = insert(:address) From aa70b0b6b2163b7226845dd66802d88702e2b6f8 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 28 Aug 2019 15:39:43 +0300 Subject: [PATCH 02/92] add token.instance controller test --- .../tokens/instance_controller_test.exs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs new file mode 100644 index 0000000000..c96ab9f84d --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs @@ -0,0 +1,22 @@ +defmodule BlockScoutWeb.Tokens.InstanceControllerTest do + use BlockScoutWeb.ConnCase, async: false + + describe "GET show/2" do + test "returns erc721 token with valid params", %{conn: conn} do + contract_address = insert(:address) + + token_id = 10 + + insert(:token_transfer, + from_address: contract_address, + token_contract_address: contract_address, + token_id: token_id + ) + + conn = + get(conn, token_instance_path(conn, :show, token_id, %{token_address_hash: to_string(contract_address.hash)})) + + assert conn.status == 200 + end + end +end From 04edabcb82e2afe2004993532e51e33d1ccec6ce Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 29 Aug 2019 13:11:12 +0300 Subject: [PATCH 03/92] fix dialyzer --- apps/explorer/lib/explorer/chain.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 9a38b7dae3..8e7894dba2 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2959,7 +2959,8 @@ defmodule Explorer.Chain do |> Repo.all() end - @spec erc721_token_instance_from_token_id_and_token_address(binary(), Hash.Address.t()) :: TokenTransfer.t() | nil + @spec erc721_token_instance_from_token_id_and_token_address(binary(), Hash.Address.t()) :: + {:ok, TokenTransfer.t()} | {:error, :not_found} def erc721_token_instance_from_token_id_and_token_address(token_id, token_contract_address) do query = from(tt in TokenTransfer, From 06d2fb0a4777fff09a4af68980707a06c36d463d Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 29 Aug 2019 14:03:24 +0300 Subject: [PATCH 04/92] fix token instance router and controller --- .../controllers/tokens/instance_controller.ex | 17 +++++++++++++++-- .../lib/block_scout_web/web_router.ex | 2 +- .../tokens/instance_controller_test.exs | 3 +-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex index 2c2d1f6f2e..c1c9dd2ddd 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex @@ -2,9 +2,11 @@ defmodule BlockScoutWeb.Tokens.InstanceController do use BlockScoutWeb, :controller alias Explorer.Chain + alias Explorer.Chain.Hash.Address - def show(conn, %{"id" => token_id, "token_address_hash" => token_address_hash}) do - with {:ok, token_address} <- Chain.hash_to_address(token_address_hash, []), + def show(conn, %{"token_id" => token_id, "id" => token_address_hash}) do + with {:ok, hash} <- validate_address_hash(token_address_hash), + {:ok, token_address} <- Chain.hash_to_address(hash, []), {:ok, token_transfer} <- Chain.erc721_token_instance_from_token_id_and_token_address(token_id, token_address.hash) do json(conn, token_transfer) @@ -13,4 +15,15 @@ defmodule BlockScoutWeb.Tokens.InstanceController do not_found(conn) end end + + def show(conn, _) do + not_found(conn) + end + + defp validate_address_hash(address_hash) do + case Address.cast(address_hash) do + {:ok, hash} -> {:ok, hash} + :error -> :invalid_address + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/web_router.ex b/apps/block_scout_web/lib/block_scout_web/web_router.ex index 18589ca0e1..3b197cc45a 100644 --- a/apps/block_scout_web/lib/block_scout_web/web_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/web_router.ex @@ -180,7 +180,7 @@ defmodule BlockScoutWeb.WebRouter do ) resources( - "/token_instance", + "/instance", Tokens.InstanceController, only: [:show], as: :instance diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs index c96ab9f84d..5c3fbbf59d 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs @@ -13,8 +13,7 @@ defmodule BlockScoutWeb.Tokens.InstanceControllerTest do token_id: token_id ) - conn = - get(conn, token_instance_path(conn, :show, token_id, %{token_address_hash: to_string(contract_address.hash)})) + conn = get(conn, token_instance_path(BlockScoutWeb.Endpoint, :show, token_id, to_string(contract_address.hash))) assert conn.status == 200 end From de4117b35457bb29c8ed5f1b703bbf5390e743dd Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 29 Aug 2019 14:47:32 +0300 Subject: [PATCH 05/92] add token instance overview view --- .../instance/overview/_details.html.eex | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex new file mode 100644 index 0000000000..10bf138709 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex @@ -0,0 +1,117 @@ +
+
+
+
+
+

+ <%= if token_name?(@token) do %> + <%= @token.name %> + <% else %> + <%= gettext("Token Details") %> + <% end %> + + + + + + + + + + + + + + + + + +

+ +

<%= to_string(@token_id) %>

+ +
+ + <%= link to: + address_path(@conn, :show, @token.contract_address_hash), + "data-test": "token_contract_address" + do %> + <%= gettext "View Contract" %> + <% end %> + +
+ <%= @token.type %> + <%= @total_token_transfers %> <%= gettext "Transfers" %> + <%= if decimals?(@token) do %> + <%= @token.decimals %> <%= gettext "Decimals" %> + <% end %> +
+
+
+
+
+ + <%= if total_supply?(@token) do %> +
+
+
+

<%= gettext "Total Supply" %>

+
+

+ <%= if decimals?(@token) do %> + <%= format_according_to_decimals(@token.total_supply, @token.decimals) %> + <% else %> + <%= format_integer_to_currency(@token.total_supply) %> + <% end %> <%= @token.symbol %> +

+ <%= if @token.usd_value do %> +
+ + | + +
+ <% else %> +
+ <% end %> +
+
+
+
+
+ <% end %> +
+ + From 6fa0afbcde9957a41f5eb9a5d5a52e758af3f9e0 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 29 Aug 2019 15:58:40 +0300 Subject: [PATCH 06/92] add token instance transfers controller --- .../tokens/instance/transfer_controller.ex | 28 +++++++++++++++++++ .../controllers/tokens/instance_controller.ex | 10 +------ .../views/tokens/instance/transfer_view.ex | 3 ++ .../views/tokens/instance_view.ex | 3 ++ .../lib/block_scout_web/web_router.ex | 9 +++++- 5 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/instance_view.ex diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex new file mode 100644 index 0000000000..e0190af2a1 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex @@ -0,0 +1,28 @@ +defmodule BlockScoutWeb.Tokens.Instance.TransferController do + use BlockScoutWeb, :controller + + alias Explorer.{Chain, Market} + + def show(conn, %{"token_id" => token_id, "id" => token_address_hash}) do + with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), + {:ok, token} <- Chain.token_from_address_hash(hash), + {:ok, token_transfer} <- + Chain.erc721_token_instance_from_token_id_and_token_address(token_id, hash) do + render( + conn, + "index.html", + token_instance: token_transfer, + current_path: current_path(conn), + token: Market.add_price(token), + total_token_transfers: Chain.count_token_transfers_from_token_hash(hash) + ) + else + _ -> + not_found(conn) + end + end + + def show(conn, _) do + not_found(conn) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex index c1c9dd2ddd..24dc551347 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex @@ -2,10 +2,9 @@ defmodule BlockScoutWeb.Tokens.InstanceController do use BlockScoutWeb, :controller alias Explorer.Chain - alias Explorer.Chain.Hash.Address def show(conn, %{"token_id" => token_id, "id" => token_address_hash}) do - with {:ok, hash} <- validate_address_hash(token_address_hash), + with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), {:ok, token_address} <- Chain.hash_to_address(hash, []), {:ok, token_transfer} <- Chain.erc721_token_instance_from_token_id_and_token_address(token_id, token_address.hash) do @@ -19,11 +18,4 @@ defmodule BlockScoutWeb.Tokens.InstanceController do def show(conn, _) do not_found(conn) end - - defp validate_address_hash(address_hash) do - case Address.cast(address_hash) do - {:ok, hash} -> {:ok, hash} - :error -> :invalid_address - end - end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex new file mode 100644 index 0000000000..0be8cb0531 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex @@ -0,0 +1,3 @@ +defmodule BlockScoutWeb.Tokens.Instance.TransferView do + use BlockScoutWeb, :view +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance_view.ex new file mode 100644 index 0000000000..c18c5b5e1f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance_view.ex @@ -0,0 +1,3 @@ +defmodule BlockScoutWeb.Tokens.InstanceView do + use BlockScoutWeb, :view +end diff --git a/apps/block_scout_web/lib/block_scout_web/web_router.ex b/apps/block_scout_web/lib/block_scout_web/web_router.ex index 3b197cc45a..12ab36ec10 100644 --- a/apps/block_scout_web/lib/block_scout_web/web_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/web_router.ex @@ -184,7 +184,14 @@ defmodule BlockScoutWeb.WebRouter do Tokens.InstanceController, only: [:show], as: :instance - ) + ) do + resources( + "/token_transfers", + Tokens.Instance.TransferController, + only: [:index], + as: :transfer + ) + end end resources( From 97dc5adbadedb467813cf1af442c281d4b12fa0b Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Fri, 30 Aug 2019 11:56:25 +0300 Subject: [PATCH 07/92] redirect to token instance transfers page --- .../tokens/instance/transfer_controller.ex | 6 ++- .../controllers/tokens/instance_controller.ex | 8 ++-- .../tokens/inventory_controller.ex | 4 +- .../tokens/instance/transfer/index.html.eex | 38 +++++++++++++++++++ .../tokens/inventory/_token.html.eex | 2 +- .../views/tokens/instance/overview_view.ex | 21 ++++++++++ .../views/tokens/instance/transfer_view.ex | 2 + .../tokens/instance_controller_test.exs | 6 ++- 8 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex index e0190af2a1..52be59dca7 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex @@ -3,9 +3,11 @@ defmodule BlockScoutWeb.Tokens.Instance.TransferController do alias Explorer.{Chain, Market} - def show(conn, %{"token_id" => token_id, "id" => token_address_hash}) do + def index(conn, %{"token_id" => token_id, "instance_id" => token_address_hash}) do + options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] + 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, options), {:ok, token_transfer} <- Chain.erc721_token_instance_from_token_id_and_token_address(token_id, hash) do render( diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex index 24dc551347..cbe27009cf 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex @@ -5,10 +5,10 @@ defmodule BlockScoutWeb.Tokens.InstanceController do def show(conn, %{"token_id" => token_id, "id" => token_address_hash}) do with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), - {:ok, token_address} <- Chain.hash_to_address(hash, []), - {:ok, token_transfer} <- - Chain.erc721_token_instance_from_token_id_and_token_address(token_id, token_address.hash) do - json(conn, token_transfer) + {:ok, _token_address} <- Chain.token_from_address_hash(hash, []), + {:ok, _token_transfer} <- + Chain.erc721_token_instance_from_token_id_and_token_address(token_id, hash) |> IO.inspect() do + redirect(conn, to: token_instance_transfer_path(conn, :index, token_id, token_address_hash)) else _ -> not_found(conn) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex index b9783b2c63..d695277765 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex @@ -39,7 +39,9 @@ defmodule BlockScoutWeb.Tokens.InventoryController do View.render_to_string( InventoryView, "_token.html", - token_transfer: token_transfer + token_transfer: token_transfer, + token: token, + conn: conn ) end) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex new file mode 100644 index 0000000000..3de4ae1273 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex @@ -0,0 +1,38 @@ +
+ <%= render( + OverviewView, + "_details.html", + token: @token, + total_token_transfers: @total_token_transfers, + token_id: @token_instance.token_id, + conn: @conn + ) %> + +
+
+ <%= render OverviewView, "_tabs.html", assigns %> +
+

<%= gettext "Token Transfers" %>

+ + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + + + + + +
+ <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
+ + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
+
+
+
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex index 88c36e5158..11c52c364f 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex @@ -10,7 +10,7 @@ <%= gettext "Token ID" %>: - <%= @token_transfer.token_id %> + <%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, to_string(@token_transfer.token_id), @token.contract_address_hash)) %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex new file mode 100644 index 0000000000..bb5eca56f9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex @@ -0,0 +1,21 @@ +defmodule BlockScoutWeb.Tokens.Instance.OverviewView do + use BlockScoutWeb, :view + + alias Explorer.Chain.Token + alias BlockScoutWeb.CurrencyHelpers + + def token_name?(%Token{name: nil}), do: false + def token_name?(%Token{name: _}), do: true + + def decimals?(%Token{decimals: nil}), do: false + def decimals?(%Token{decimals: _}), do: true + + def total_supply?(%Token{total_supply: nil}), do: false + def total_supply?(%Token{total_supply: _}), do: true + + def total_supply_usd(token) do + tokens = CurrencyHelpers.divide_decimals(token.total_supply, token.decimals) + price = token.usd_value + Decimal.mult(tokens, price) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex index 0be8cb0531..2cf314efa1 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.Tokens.Instance.TransferView do use BlockScoutWeb, :view + + alias BlockScoutWeb.Tokens.Instance.OverviewView end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs index 5c3fbbf59d..03d3010b2d 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs @@ -2,9 +2,11 @@ defmodule BlockScoutWeb.Tokens.InstanceControllerTest do use BlockScoutWeb.ConnCase, async: false describe "GET show/2" do - test "returns erc721 token with valid params", %{conn: conn} do + test "redirects with valid params", %{conn: conn} do contract_address = insert(:address) + insert(:token, contract_address: contract_address) + token_id = 10 insert(:token_transfer, @@ -15,7 +17,7 @@ defmodule BlockScoutWeb.Tokens.InstanceControllerTest do conn = get(conn, token_instance_path(BlockScoutWeb.Endpoint, :show, token_id, to_string(contract_address.hash))) - assert conn.status == 200 + assert conn.status == 302 end end end From 471b905930fc6a42b19d7832be827bb5c8df260f Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Fri, 30 Aug 2019 12:43:46 +0300 Subject: [PATCH 08/92] add tabs --- .../tokens/instance/overview/_tabs.html.eex | 15 +++++++++++++++ .../views/tokens/instance/overview_view.ex | 10 +++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex new file mode 100644 index 0000000000..c026783b08 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex @@ -0,0 +1,15 @@ +
+ <%= link( + gettext("Token Transfers"), + class: "card-tab #{tab_status("token_transfers", @conn.request_path)}", + to: token_path(@conn, :show, @token.contract_address_hash) + ) + %> + <%= if smart_contract_with_read_only_functions?(@token) do %> + <%= link( + gettext("Read Contract"), + to: token_read_contract_path(@conn, :index, @token.contract_address_hash), + class: "card-tab #{tab_status("read_contract", @conn.request_path)}") + %> + <% end %> +
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex index bb5eca56f9..5574d7d727 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex @@ -1,8 +1,8 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do use BlockScoutWeb, :view - alias Explorer.Chain.Token alias BlockScoutWeb.CurrencyHelpers + alias Explorer.Chain.{Address, SmartContract, Token} def token_name?(%Token{name: nil}), do: false def token_name?(%Token{name: _}), do: true @@ -18,4 +18,12 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do price = token.usd_value Decimal.mult(tokens, price) end + + def smart_contract_with_read_only_functions?( + %Token{contract_address: %Address{smart_contract: %SmartContract{}}} = token + ) do + Enum.any?(token.contract_address.smart_contract.abi, & &1["constant"]) + end + + def smart_contract_with_read_only_functions?(%Token{contract_address: %Address{smart_contract: nil}}), do: false end From ab8262f193a1b171f770f1a1904d1d3ced2d3ee2 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Fri, 30 Aug 2019 13:18:22 +0300 Subject: [PATCH 09/92] show token transfers for the specified token instance --- .../tokens/instance/transfer_controller.ex | 44 +++++++++++++++++++ apps/explorer/lib/explorer/chain.ex | 5 +++ .../lib/explorer/chain/token_transfer.ex | 20 +++++++++ 3 files changed, 69 insertions(+) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex index 52be59dca7..42dbfab5f0 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex @@ -1,7 +1,51 @@ defmodule BlockScoutWeb.Tokens.Instance.TransferController do use BlockScoutWeb, :controller + alias BlockScoutWeb.Tokens.TransferView alias Explorer.{Chain, Market} + alias Phoenix.View + + import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] + + def index(conn, %{"token_id" => token_id, "instance_id" => token_address_hash, "type" => "JSON"} = params) do + with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), + {:ok, token} <- Chain.token_from_address_hash(hash), + token_transfers <- + 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) + + next_page_path = + case next_page_params(next_page, token_transfers_paginated, params) do + nil -> + nil + + next_page_params -> + token_instance_transfer_path( + conn, + :index, + token_id, + token.contract_address_hash, + Map.delete(next_page_params, "type") + ) + end + + transfers_json = + Enum.map(token_transfers_paginated, fn transfer -> + View.render_to_string( + TransferView, + "_token_transfer.html", + conn: conn, + token: token, + transfer: transfer + ) + end) + + json(conn, %{items: transfers_json, next_page_path: next_page_path}) + else + _ -> + not_found(conn) + end + end def index(conn, %{"token_id" => token_id, "instance_id" => token_address_hash}) do options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 8e7894dba2..c44078f2c3 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2818,6 +2818,11 @@ defmodule Explorer.Chain do TokenTransfer.fetch_token_transfers_from_token_hash(token_address_hash, options) end + @spec fetch_token_transfers_from_token_hash_and_token_id(Hash.t(), binary(), [paging_options]) :: [] + 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) + end + @spec count_token_transfers_from_token_hash(Hash.t()) :: non_neg_integer() def count_token_transfers_from_token_hash(token_address_hash) do TokenTransfer.count_token_transfers_from_token_hash(token_address_hash) diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 619de87db2..7c6570f561 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -140,6 +140,26 @@ defmodule Explorer.Chain.TokenTransfer do |> Repo.all() end + @spec fetch_token_transfers_from_token_hash_and_token_id(Hash.t(), binary(), [paging_options]) :: [] + 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) + + query = + from( + tt in TokenTransfer, + where: + tt.token_contract_address_hash == ^token_address_hash and tt.token_id == ^token_id and + not is_nil(tt.block_number), + preload: [{:transaction, :block}, :token, :from_address, :to_address], + order_by: [desc: tt.block_number, desc: tt.log_index] + ) + + query + |> page_token_transfer(paging_options) + |> limit(^paging_options.page_size) + |> Repo.all() + end + @spec count_token_transfers_from_token_hash(Hash.t()) :: non_neg_integer() def count_token_transfers_from_token_hash(token_address_hash) do query = From 3b54cedc72173c5b82ff79d1a9007d44370b6af5 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Fri, 30 Aug 2019 14:27:11 +0300 Subject: [PATCH 10/92] count token transfers for token id --- .../tokens/instance/transfer_controller.ex | 2 +- apps/explorer/lib/explorer/chain.ex | 5 +++++ apps/explorer/lib/explorer/chain/token_transfer.ex | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex index 42dbfab5f0..912e580224 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex @@ -60,7 +60,7 @@ defmodule BlockScoutWeb.Tokens.Instance.TransferController do token_instance: token_transfer, current_path: current_path(conn), token: Market.add_price(token), - total_token_transfers: Chain.count_token_transfers_from_token_hash(hash) + total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id) ) else _ -> diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index c44078f2c3..2fb788c1f4 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2828,6 +2828,11 @@ defmodule Explorer.Chain do TokenTransfer.count_token_transfers_from_token_hash(token_address_hash) end + @spec count_token_transfers_from_token_hash_and_token_id(Hash.t(), binary()) :: non_neg_integer() + def count_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id) do + TokenTransfer.count_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id) + end + @spec transaction_has_token_transfers?(Hash.t()) :: boolean() def transaction_has_token_transfers?(transaction_hash) do query = from(tt in TokenTransfer, where: tt.transaction_hash == ^transaction_hash) diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 7c6570f561..207a8ab524 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -172,6 +172,18 @@ defmodule Explorer.Chain.TokenTransfer do Repo.one(query) end + @spec count_token_transfers_from_token_hash_and_token_id(Hash.t(), binary()) :: non_neg_integer() + def count_token_transfers_from_token_hash_and_token_id(token_address_hash, token_id) do + query = + from( + tt in TokenTransfer, + where: tt.token_contract_address_hash == ^token_address_hash and tt.token_id == ^token_id, + select: fragment("COUNT(*)") + ) + + Repo.one(query) + end + def page_token_transfer(query, %PagingOptions{key: nil}), do: query def page_token_transfer(query, %PagingOptions{key: {token_id}}) do From d85b6663e71db171a45276daa3cef55c5e3a0b7e Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Fri, 30 Aug 2019 14:49:43 +0300 Subject: [PATCH 11/92] fix QR Code --- .../templates/tokens/instance/overview/_details.html.eex | 8 ++++---- .../views/tokens/instance/overview_view.ex | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex index 10bf138709..25a3c4f7ab 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex @@ -11,13 +11,13 @@ <% end %> - + @@ -107,7 +107,7 @@
- <%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @transfer.transaction_hash %> + <%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @token_transfer.transaction_hash %> - <%= link to: address_token_transfers_path(@conn, :index, @transfer.from_address, @token.contract_address_hash), "data-test": "address_hash_link" do %> + <%= link to: address_token_transfers_path(@conn, :index, @token_transfer.from_address, @token.contract_address_hash), "data-test": "address_hash_link" do %> <%= render( BlockScoutWeb.AddressView, "_responsive_hash.html", - address: @transfer.from_address, - contract: BlockScoutWeb.AddressView.contract?(@transfer.from_address) + address: @token_transfer.from_address, + contract: BlockScoutWeb.AddressView.contract?(@token_transfer.from_address) ) %> <% end %> → - <%= link to: address_token_transfers_path(@conn, :index, @transfer.to_address, @token.contract_address_hash), "data-test": "address_hash_link" do %> + <%= link to: address_token_transfers_path(@conn, :index, @token_transfer.to_address, @token.contract_address_hash), "data-test": "address_hash_link" do %> <%= render( BlockScoutWeb.AddressView, "_responsive_hash.html", - address: @transfer.to_address, - contract: BlockScoutWeb.AddressView.contract?(@transfer.to_address) + address: @token_transfer.to_address, + contract: BlockScoutWeb.AddressView.contract?(@token_transfer.to_address) ) %> <% end %> - <%= token_transfer_amount(@transfer) %> <%= @transfer.token.symbol %> + <%= case token_transfer_amount(@token_transfer) do %> + <% {:ok, :erc721_instance} -> %> + <%= "TokenID ["%><%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, to_string(@token_transfer.token_id), @token_transfer.token.contract_address_hash)) %><%= "]" %> + <% {:ok, value} -> %> + <%= value %> + <% end %> + <%= @token_transfer.token.symbol %>
@@ -36,11 +42,11 @@
<%= link( - gettext("Block #%{number}", number: @transfer.block_number), - to: block_path(BlockScoutWeb.Endpoint, :show, @transfer.block_number) + gettext("Block #%{number}", number: @token_transfer.block_number), + to: block_path(BlockScoutWeb.Endpoint, :show, @token_transfer.block_number) ) %> - +
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex index 91a7d9973a..85711ca136 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex @@ -38,11 +38,11 @@
<% [first_token_transfer | remaining_token_transfers] = @transaction.token_transfers %> - <%= render "_token_transfer.html", address: assigns[:current_address], token_transfer: first_token_transfer %> + <%= render "_token_transfer.html", address: assigns[:current_address], token_transfer: first_token_transfer, conn: @conn %>
<%= for token_transfer <- remaining_token_transfers do %> - <%= render "_token_transfer.html", address: assigns[:current_address], token_transfer: token_transfer %> + <%= render "_token_transfer.html", address: assigns[:current_address], token_transfer: token_transfer, conn: @conn %> <% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex index 8c88fdf87b..4143e66a9b 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex @@ -20,6 +20,12 @@
- <%= token_transfer_amount(@token_transfer) %> <%= link(token_symbol(@token_transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, @token_transfer.token.contract_address_hash)) %> + <%= case token_transfer_amount(@token_transfer) do %> + <% {:ok, :erc721_instance} -> %> + <%= "TokenID ["%><%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, to_string(@token_transfer.token_id), @token_transfer.token.contract_address_hash)) %><%= "]" %> + <% {:ok, value} -> %> + <%= value %> + <% end %> + <%= link(token_symbol(@token_transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, @token_transfer.token.contract_address_hash)) %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index 00266d92b3..cbba23ba90 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -193,7 +193,12 @@
<%= for transfer <- aggregate_token_transfers(transaction_with_transfers.token_transfers) do %>

- <%= token_transfer_amount(transfer) %> + <%= case token_transfer_amount(@token_transfer) do %> + <% {:ok, :erc721_instance} -> %> + <%= "TokenID ["%><%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, to_string(@token_transfer.token_id), @token_transfer.token.contract_address_hash)) %><%= "]" %> + <% {:ok, value} -> %> + <%= value %> + <% end %> <%= " "%> <%= link(token_symbol(transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, transfer.token.contract_address_hash)) %>

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 7c3c202f80..4396c7942c 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 @@ -13,7 +13,13 @@ - <%= token_transfer_amount(@token_transfer) %> <%= link(token_symbol(@token_transfer.token), to: token_path(@conn, :show, @token_transfer.token.contract_address_hash)) %> + <%= case token_transfer_amount(@token_transfer) do%> + <% {:ok, :erc721_instance} -> %> + <%= "TokenID ["%><%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, to_string(@token_transfer.token_id), @token_transfer.token.contract_address_hash)) %><%= "]" %> + <% {:ok, value} -> %> + <%= value %> + <% end %> + <%= link(token_symbol(@token_transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, @token_transfer.token.contract_address_hash)) %>
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 33569f3a97..0cc90aad69 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 @@ -21,19 +21,19 @@ defmodule BlockScoutWeb.Tokens.Helpers do end defp do_token_transfer_amount(%Token{type: "ERC-20"}, nil, _token_id) do - "--" + {:ok, "--"} end defp do_token_transfer_amount(%Token{type: "ERC-20", decimals: nil}, amount, _token_id) do - CurrencyHelpers.format_according_to_decimals(amount, Decimal.new(0)) + {:ok, CurrencyHelpers.format_according_to_decimals(amount, Decimal.new(0))} end defp do_token_transfer_amount(%Token{type: "ERC-20", decimals: decimals}, amount, _token_id) do - CurrencyHelpers.format_according_to_decimals(amount, decimals) + {:ok, CurrencyHelpers.format_according_to_decimals(amount, decimals)} end - defp do_token_transfer_amount(%Token{type: "ERC-721"}, _amount, token_id) do - "TokenID [#{token_id}]" + defp do_token_transfer_amount(%Token{type: "ERC-721"}, _amount, _token_id) do + {:ok, :erc721_instance} end defp do_token_transfer_amount(_token, _amount, _token_id) do From a5794ad2daed13e749bc8355ede0ca5f85a7a9d4 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 4 Sep 2019 14:05:17 +0300 Subject: [PATCH 18/92] fix gettext --- apps/block_scout_web/priv/gettext/default.pot | 12 ++++++------ .../priv/gettext/en/LC_MESSAGES/default.po | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 9a0b057b04..91a94d5fdd 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -187,7 +187,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/block/_link.html.eex:2 #: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:28 -#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:39 +#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:45 msgid "Block #%{number}" msgstr "" @@ -697,7 +697,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20 #: lib/block_scout_web/templates/transaction/_tile.html.eex:29 #: lib/block_scout_web/templates/transaction/overview.html.eex:179 -#: lib/block_scout_web/templates/transaction/overview.html.eex:209 +#: lib/block_scout_web/templates/transaction/overview.html.eex:214 #: lib/block_scout_web/views/wei_helpers.ex:78 msgid "Ether" msgstr "" @@ -1060,7 +1060,7 @@ msgid "License ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:237 +#: lib/block_scout_web/templates/transaction/overview.html.eex:242 msgid "Limit" msgstr "" @@ -1641,7 +1641,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:231 +#: lib/block_scout_web/templates/transaction/overview.html.eex:236 msgid "Used" msgstr "" @@ -1677,7 +1677,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/overview.html.eex:179 -#: lib/block_scout_web/templates/transaction/overview.html.eex:209 +#: lib/block_scout_web/templates/transaction/overview.html.eex:214 msgid "Value" msgstr "" @@ -1829,7 +1829,7 @@ msgid "Copy Token ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:227 +#: lib/block_scout_web/templates/transaction/overview.html.eex:232 msgid "Gas" msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 0f87dc54c1..91a94d5fdd 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -187,7 +187,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/block/_link.html.eex:2 #: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:28 -#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:39 +#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:45 msgid "Block #%{number}" msgstr "" @@ -697,7 +697,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20 #: lib/block_scout_web/templates/transaction/_tile.html.eex:29 #: lib/block_scout_web/templates/transaction/overview.html.eex:179 -#: lib/block_scout_web/templates/transaction/overview.html.eex:209 +#: lib/block_scout_web/templates/transaction/overview.html.eex:214 #: lib/block_scout_web/views/wei_helpers.ex:78 msgid "Ether" msgstr "" @@ -1060,7 +1060,7 @@ msgid "License ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:237 +#: lib/block_scout_web/templates/transaction/overview.html.eex:242 msgid "Limit" msgstr "" @@ -1641,7 +1641,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:231 +#: lib/block_scout_web/templates/transaction/overview.html.eex:236 msgid "Used" msgstr "" @@ -1677,7 +1677,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/overview.html.eex:179 -#: lib/block_scout_web/templates/transaction/overview.html.eex:209 +#: lib/block_scout_web/templates/transaction/overview.html.eex:214 msgid "Value" msgstr "" @@ -1829,7 +1829,7 @@ msgid "Copy Token ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:227 +#: lib/block_scout_web/templates/transaction/overview.html.eex:232 msgid "Gas" msgstr "" @@ -1854,12 +1854,12 @@ msgstr "" msgid "Loading chart" msgstr "" -#, elixir-format, fuzzy +#, elixir-format #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:58 msgid "Module" msgstr "" -#, elixir-format, fuzzy +#, elixir-format #: lib/block_scout_web/templates/chain/show.html.eex:56 msgid "Total transactions" msgstr "" From 1f902e1af38797565bea5aaee6a0e467779a8fea Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 4 Sep 2019 14:19:19 +0300 Subject: [PATCH 19/92] fix token helpers test --- .../test/block_scout_web/views/tokens/helpers_test.exs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/views/tokens/helpers_test.exs b/apps/block_scout_web/test/block_scout_web/views/tokens/helpers_test.exs index 2cbfd2c0a5..817a3137d6 100644 --- a/apps/block_scout_web/test/block_scout_web/views/tokens/helpers_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/tokens/helpers_test.exs @@ -8,28 +8,28 @@ defmodule BlockScoutWeb.Tokens.HelpersTest do token = build(:token, type: "ERC-20") token_transfer = build(:token_transfer, token: token, amount: nil) - assert Helpers.token_transfer_amount(token_transfer) == "--" + assert Helpers.token_transfer_amount(token_transfer) == {:ok, "--"} end test "returns the formatted amount according to token decimals with ERC-20 token" do token = build(:token, type: "ERC-20", decimals: Decimal.new(6)) token_transfer = build(:token_transfer, token: token, amount: Decimal.new(1_000_000)) - assert Helpers.token_transfer_amount(token_transfer) == "1" + assert Helpers.token_transfer_amount(token_transfer) == {:ok, "1"} end test "returns the formatted amount when the decimals is nil with ERC-20 token" do token = build(:token, type: "ERC-20", decimals: nil) token_transfer = build(:token_transfer, token: token, amount: Decimal.new(1_000_000)) - assert Helpers.token_transfer_amount(token_transfer) == "1,000,000" + assert Helpers.token_transfer_amount(token_transfer) == {:ok, "1,000,000"} end test "returns a string with the token_id with ERC-721 token" do token = build(:token, type: "ERC-721", decimals: nil) token_transfer = build(:token_transfer, token: token, amount: nil, token_id: 1) - assert Helpers.token_transfer_amount(token_transfer) == "TokenID [1]" + assert Helpers.token_transfer_amount(token_transfer) == {:ok, :erc721_instance} end test "returns nothing for unknown token's type" do From f4b0c21c2e17b5d7516d480e306a33486bb810e3 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 4 Sep 2019 14:24:13 +0300 Subject: [PATCH 20/92] fix remaining tests --- .../controllers/recent_transactions_controller.ex | 2 +- .../block_scout_web/templates/transaction/overview.html.eex | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex index a98e380e67..3b9830476c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex @@ -23,7 +23,7 @@ defmodule BlockScoutWeb.RecentTransactionsController do %{ transaction_hash: Hash.to_string(transaction.hash), transaction_html: - View.render_to_string(BlockScoutWeb.TransactionView, "_tile.html", transaction: transaction) + View.render_to_string(BlockScoutWeb.TransactionView, "_tile.html", transaction: transaction, conn: conn) } end) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index cbba23ba90..85db58c441 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -193,9 +193,9 @@
<%= for transfer <- aggregate_token_transfers(transaction_with_transfers.token_transfers) do %>

- <%= case token_transfer_amount(@token_transfer) do %> + <%= case token_transfer_amount(transfer) do %> <% {:ok, :erc721_instance} -> %> - <%= "TokenID ["%><%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, to_string(@token_transfer.token_id), @token_transfer.token.contract_address_hash)) %><%= "]" %> + <%= "TokenID ["%><%= link(transfer.token_id, to: token_instance_path(@conn, :show, to_string(transfer.token_id), transfer.token.contract_address_hash)) %><%= "]" %> <% {:ok, value} -> %> <%= value %> <% end %> From 834688d894bfb9574f12055056b232f46eee820f Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 4 Sep 2019 14:32:19 +0300 Subject: [PATCH 21/92] fix block transactions test --- .../controllers/block_transaction_controller.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex index dd362cd065..9351f9f886 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex @@ -50,7 +50,8 @@ defmodule BlockScoutWeb.BlockTransactionController do View.render_to_string( TransactionView, "_tile.html", - transaction: transaction + transaction: transaction, + conn: conn ) end) From 8873774028097992195eaa710611db088e0157dc Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 4 Sep 2019 14:39:11 +0300 Subject: [PATCH 22/92] fix qr code --- .../lib/block_scout_web/views/api_docs_view.ex | 2 +- .../views/tokens/instance/overview_view.ex | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex index 363a4a0179..a75bd2dcbf 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex @@ -34,7 +34,7 @@ defmodule BlockScoutWeb.APIDocsView do end) end - defp blockscout_url do + def blockscout_url do url_params = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url] host = url_params[:host] path = url_params[:path] diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex index 41db88febe..e4a6ef6039 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex @@ -4,6 +4,8 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do alias BlockScoutWeb.CurrencyHelpers alias Explorer.Chain.{Address, SmartContract, Token} + import BlockScoutWeb.APIDocsView, only: [blockscout_url: 0] + def token_name?(%Token{name: nil}), do: false def token_name?(%Token{name: _}), do: true @@ -28,8 +30,11 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do def smart_contract_with_read_only_functions?(%Token{contract_address: %Address{smart_contract: nil}}), do: false def qr_code(conn, token_id, hash) do - conn - |> token_instance_url(:show, to_string(token_id), to_string(hash)) + token_instance_path = token_instance_path(conn, :show, to_string(token_id), to_string(hash)) + + url = blockscout_url() <> token_instance_path + + url |> QRCode.to_png() |> Base.encode64() end From 0ca1daf2ba2c77e9b26ff2734caaa3261752d45d Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 5 Sep 2019 12:21:04 +0300 Subject: [PATCH 23/92] pass conn assign --- .../controllers/pending_transaction_controller.ex | 3 ++- .../lib/block_scout_web/controllers/transaction_controller.ex | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex index 1128f04616..9385ee5616 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex @@ -43,7 +43,8 @@ defmodule BlockScoutWeb.PendingTransactionController do View.render_to_string( TransactionView, "_tile.html", - transaction: transaction + transaction: transaction, + conn: conn ) end), next_page_path: next_page_url diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex index 3f849c0e7c..db0cae65d8 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex @@ -41,7 +41,8 @@ defmodule BlockScoutWeb.TransactionController do View.render_to_string( TransactionView, "_tile.html", - transaction: transaction + transaction: transaction, + conn: conn ) end), next_page_path: next_page_path From 9f3dc8d92b38784b29fe07af04217edcaec009bf Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 5 Sep 2019 13:29:26 +0300 Subject: [PATCH 24/92] add instance model --- .../lib/explorer/chain/token/instance.ex | 43 +++++++++++++++++++ .../20190905083522_create_token_instances.exs | 18 ++++++++ 2 files changed, 61 insertions(+) create mode 100644 apps/explorer/lib/explorer/chain/token/instance.ex create mode 100644 apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs diff --git a/apps/explorer/lib/explorer/chain/token/instance.ex b/apps/explorer/lib/explorer/chain/token/instance.ex new file mode 100644 index 0000000000..90a6d671dc --- /dev/null +++ b/apps/explorer/lib/explorer/chain/token/instance.ex @@ -0,0 +1,43 @@ +defmodule Explorer.Chain.Token.Instance do + @moduledoc """ + Represents an ERC 721 token instance and stores metadata defined in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md. + """ + + use Explorer.Schema + + alias Explorer.Chain.{Hash, Token} + alias Explorer.Chain.Token.Instance + + @typedoc """ + * `token_id` - ID of the token + * `token_contract_address_hash` - Address hash foreign key + * `metadata` - Token instance metadata + """ + + @type t :: %Instance{ + token_id: non_neg_integer(), + token_contract_address_hash: Hash.Address.t(), + metadata: Map.t() + } + + schema "token_instances" do + field(:token_id, :decimal, primary_key: true) + field(:metadata, :map) + + belongs_to( + :token, + Token, + foreign_key: :token_contract_address_hash, + references: :contract_address_hash, + type: Hash.Address, + primary_key: true + ) + end + + # def changeset(%Instance{} = instance, params \\ %{}) do + # instance + # |> cast([:token_id, :metadata, :token_contract_address_hash]) + # |> validate_required([:token_id, :token_contract_address_hash]) + # |> foreign_key_constraint(:token_contract_address_hash) + # end +end diff --git a/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs b/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs new file mode 100644 index 0000000000..ef20e47e46 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs @@ -0,0 +1,18 @@ +defmodule Explorer.Repo.Migrations.CreateTokenInstances do + use Ecto.Migration + + def change do + create table(:token_instances, primary_key: false) do + # ERC-721 tokens have IDs + # 10^x = 2^256, x ~ 77.064, so 78 decimal digits will store the full 256-bits of a native EVM type + add(:token_id, :numeric, precision: 78, scale: 0, null: false, primary_key: true) + + add(:token_contract_address_hash, references(:tokens, column: :contract_address_hash, type: :bytea), + null: false, + primary_key: true + ) + + add(:metadata, :jsonb) + end + end +end From bcffd6f705978767958e3983a941101021b79582 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 5 Sep 2019 16:31:34 +0300 Subject: [PATCH 25/92] upsert token instance --- apps/explorer/lib/explorer/chain.ex | 11 ++++ .../lib/explorer/chain/token/instance.ex | 15 +++-- .../20190905083522_create_token_instances.exs | 2 + apps/explorer/test/explorer/chain_test.exs | 57 +++++++++++++++++++ 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index e830709500..5974406e6c 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -40,6 +40,7 @@ defmodule Explorer.Chain do SmartContract, StakingPool, Token, + Token.Instance, TokenTransfer, Transaction, Wei @@ -2973,6 +2974,16 @@ defmodule Explorer.Chain do end end + @spec upsert_token_instance(map()) :: {:ok, Instance.t()} | {:error, Ecto.Changeset.t()} + def upsert_token_instance(params) do + changeset = Instance.changeset(%Instance{}, params) + + Repo.insert(changeset, + on_conflict: :replace_all, + conflict_target: [:token_id, :token_contract_address_hash] + ) + end + @doc """ Update a new `t:Token.t/0` record. diff --git a/apps/explorer/lib/explorer/chain/token/instance.ex b/apps/explorer/lib/explorer/chain/token/instance.ex index 90a6d671dc..0e704650a0 100644 --- a/apps/explorer/lib/explorer/chain/token/instance.ex +++ b/apps/explorer/lib/explorer/chain/token/instance.ex @@ -20,6 +20,7 @@ defmodule Explorer.Chain.Token.Instance do metadata: Map.t() } + @primary_key false schema "token_instances" do field(:token_id, :decimal, primary_key: true) field(:metadata, :map) @@ -32,12 +33,14 @@ defmodule Explorer.Chain.Token.Instance do type: Hash.Address, primary_key: true ) + + timestamps() end - # def changeset(%Instance{} = instance, params \\ %{}) do - # instance - # |> cast([:token_id, :metadata, :token_contract_address_hash]) - # |> validate_required([:token_id, :token_contract_address_hash]) - # |> foreign_key_constraint(:token_contract_address_hash) - # end + def changeset(%Instance{} = instance, params \\ %{}) do + instance + |> cast(params, [:token_id, :metadata, :token_contract_address_hash]) + |> validate_required([:token_id, :token_contract_address_hash]) + |> foreign_key_constraint(:token_contract_address_hash) + end end diff --git a/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs b/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs index ef20e47e46..271b921dbb 100644 --- a/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs +++ b/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs @@ -13,6 +13,8 @@ defmodule Explorer.Repo.Migrations.CreateTokenInstances do ) add(:metadata, :jsonb) + + timestamps(null: false, type: :utc_datetime_usec) end end end diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index af00d78c12..9a665e7e7b 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -101,6 +101,63 @@ defmodule Explorer.ChainTest do end end + describe "upsert_token_instance/1" do + test "insert a new token instance with valid params" do + token = insert(:token) + + params = %{ + token_id: 1, + token_contract_address_hash: token.contract_address_hash, + metadata: %{uri: "http://example.com"} + } + + {:ok, result} = Chain.upsert_token_instance(params) + + assert result.token_id == Decimal.new(1) + assert result.metadata == params.metadata + assert result.token_contract_address_hash == token.contract_address_hash + end + + test "replaces existing token instance record" do + token = insert(:token) + + params = %{ + token_id: 1, + token_contract_address_hash: token.contract_address_hash, + metadata: %{uri: "http://example.com"} + } + + {:ok, _} = Chain.upsert_token_instance(params) + + params1 = %{ + token_id: 1, + token_contract_address_hash: token.contract_address_hash, + metadata: %{uri: "http://example1.com"} + } + + {:ok, result} = Chain.upsert_token_instance(params1) + + assert result.token_id == Decimal.new(1) + assert result.metadata == params1.metadata + assert result.token_contract_address_hash == token.contract_address_hash + end + + test "fails to import with invalid params" do + params = %{ + token_id: 1, + metadata: %{uri: "http://example.com"} + } + + {:error, + %{ + errors: [ + token_contract_address_hash: {"can't be blank", [validation: :required]} + ], + valid?: false + }} = Chain.upsert_token_instance(params) + end + end + describe "address_to_logs/2" do test "fetches logs" do %Address{hash: address_hash} = address = insert(:address) From a49b16a71888eb0bc1de819724834788b54462b7 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 10 Sep 2019 17:50:08 +0300 Subject: [PATCH 26/92] Fix gettext test after manual merging --- apps/block_scout_web/priv/gettext/default.pot | 5 ----- apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po | 5 ----- 2 files changed, 10 deletions(-) diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 689ecfe2e0..943fd671fe 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -1837,11 +1837,6 @@ msgstr "" msgid "Gas" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/layout/_footer.html.eex:19 -msgid "Github" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/transaction/not_found.html.eex:26 msgid "If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information." diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 689ecfe2e0..943fd671fe 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -1837,11 +1837,6 @@ msgstr "" msgid "Gas" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/layout/_footer.html.eex:19 -msgid "Github" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/transaction/not_found.html.eex:26 msgid "If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information." From c156865c2c199709e27566977b4c8cadfa6c2032 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Fri, 13 Sep 2019 15:03:37 +0300 Subject: [PATCH 27/92] add instance metadata retriever --- .../token/instance_metadata_retriever.ex | 60 +++++++++++++++++++ .../instance_metadata_retriever_test.exs | 53 ++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 apps/explorer/lib/explorer/token/instance_metadata_retriever.ex create mode 100644 apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs diff --git a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex new file mode 100644 index 0000000000..62fd568704 --- /dev/null +++ b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex @@ -0,0 +1,60 @@ +defmodule Explorer.Token.InstanceMetadataRetriever do + @moduledoc """ + Fetches ERC721 token instance metadata. + """ + + require Logger + + alias Explorer.SmartContract.Reader + alias HTTPoison.{Error, Response} + + @abi [ + %{ + "type" => "function", + "stateMutability" => "view", + "payable" => false, + "outputs" => [ + %{"type" => "string", "name" => ""} + ], + "name" => "tokenURI", + "inputs" => [ + %{ + "type" => "uint256", + "name" => "_tokenId" + } + ], + "constant" => true + } + ] + + def fetch_metadata(contract_address_hash, token_id) do + contract_functions = %{"tokenURI" => [token_id]} + + contract_address_hash + |> query_contract(contract_functions) + |> fetch_json() + end + + def query_contract(contract_address_hash, contract_functions) do + Reader.query_contract(contract_address_hash, @abi, contract_functions) + end + + defp fetch_json(%{"tokenURI" => {:ok, [token_uri]}}) do + case HTTPoison.get(token_uri) do + {:ok, %Response{body: body, status_code: 200}} -> + data = Jason.decode(body) + + {:ok, data} + + {:ok, %Response{body: body}} -> + {:error, body} + + {:error, %Error{reason: reason}} -> + {:error, reason} + end + end + + defp fetch_json(result) do + {:error, result} + end +end diff --git a/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs b/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs new file mode 100644 index 0000000000..459bb53b02 --- /dev/null +++ b/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs @@ -0,0 +1,53 @@ +defmodule Explorer.Token.InstanceMetadataRetrieverTest do + use EthereumJSONRPC.Case + + alias Explorer.Token.InstanceMetadataRetriever + + import Mox + + setup :verify_on_exit! + setup :set_mox_global + + describe "fetch_metadata/2" do + @tag :no_parity + @tag :no_geth + test "fetches json metadata", %{json_rpc_named_arguments: json_rpc_named_arguments} do + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_call", + params: [ + %{ + data: + "0xc87b56dd000000000000000000000000000000000000000000000000fdd5b9fa9d4bfb20", + to: "0x5caebd3b32e210e85ce3e9d51638b9c445481567" + }, + "latest" + ] + } + ], + _options -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: + "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003568747470733a2f2f7661756c742e7761727269646572732e636f6d2f31383239303732393934373636373130323439362e6a736f6e0000000000000000000000" + } + ]} + end) + end + + assert %{ + "tokenURI" => {:ok, ["https://vault.warriders.com/18290729947667102496.json"]} + } == + InstanceMetadataRetriever.query_contract("0x5caebd3b32e210e85ce3e9d51638b9c445481567", %{ + "tokenURI" => [18_290_729_947_667_102_496] + }) + end + end +end From 1831d86b808ebf17b906e333c47f7220f06bc5b0 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 16 Sep 2019 12:37:47 +0300 Subject: [PATCH 28/92] stream unfetched token instances --- apps/explorer/lib/explorer/chain.ex | 21 +++++++ .../20190905083522_create_token_instances.exs | 2 + apps/explorer/test/explorer/chain_test.exs | 55 +++++++++++++++++++ apps/explorer/test/support/factory.ex | 9 +++ 4 files changed, 87 insertions(+) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 1433e4c1ce..8caaaf8018 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2823,6 +2823,27 @@ defmodule Explorer.Chain do Repo.stream_reduce(query, initial, reducer) end + @spec stream_unfetched_token_instances( + initial :: accumulator, + reducer :: (entry :: map(), accumulator -> accumulator) + ) :: {:ok, accumulator} + when accumulator: term() + def stream_unfetched_token_instances(initial, reducer) when is_function(reducer, 2) do + query = + from( + token_transfer in TokenTransfer, + inner_join: token in Token, + on: token.contract_address_hash == token_transfer.token_contract_address_hash, + left_join: instance in Instance, + on: token_transfer.token_id == instance.token_id, + where: token.type == ^"ERC-721" and is_nil(instance.token_id), + distinct: token_transfer.token_id, + select: %{contract_address_hash: token_transfer.token_contract_address_hash, token_id: token_transfer.token_id} + ) + + Repo.stream_reduce(query, initial, reducer) + end + @doc """ Streams a list of token contract addresses that have been cataloged. """ diff --git a/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs b/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs index 271b921dbb..7ebf9c12e9 100644 --- a/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs +++ b/apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs @@ -16,5 +16,7 @@ defmodule Explorer.Repo.Migrations.CreateTokenInstances do timestamps(null: false, type: :utc_datetime_usec) end + + create_if_not_exists(index(:token_instances, [:token_id])) end end diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 9a665e7e7b..353622e515 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -3602,6 +3602,61 @@ defmodule Explorer.ChainTest do end end + describe "stream_unfetched_token_instances/2" do + test "reduces wuth given reducer and accumulator" do + token_contract_address = insert(:contract_address) + token = insert(:token, contract_address: token_contract_address, type: "ERC-721") + + 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: 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 "do not fetch records with token instances" do + token_contract_address = insert(:contract_address) + token = insert(:token, contract_address: token_contract_address, type: "ERC-721") + + 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: 11 + ) + + insert(:token_instance, + token_id: token_transfer.token_id, + token_contract_address_hash: token_transfer.token_contract_address_hash + ) + + assert {:ok, []} = Chain.stream_unfetched_token_instances([], &[&1 | &2]) + end + end + describe "search_token/1" do test "finds by part of the name" do token = insert(:token, name: "magic token", symbol: "MAGIC") diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index a6e07683a5..edad876a8b 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -26,6 +26,7 @@ defmodule Explorer.Factory do SmartContract, Token, TokenTransfer, + Token.Instance, Transaction, StakingPool, StakingPoolsDelegator @@ -542,6 +543,14 @@ defmodule Explorer.Factory do } end + def token_instance_factory do + %Instance{ + token_contract_address_hash: build(:address), + token_id: 5, + metadata: %{key: "value"} + } + end + def token_balance_factory do %TokenBalance{ address: build(:address), From 5c12d9e3cb1437883e4f8899a867eeb727c13c08 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 16 Sep 2019 14:16:48 +0300 Subject: [PATCH 29/92] add TokenInstance Fetcher --- .../lib/indexer/fetcher/token_instance.ex | 71 +++++++++++++++++++ apps/indexer/lib/indexer/supervisor.ex | 3 + 2 files changed, 74 insertions(+) create mode 100644 apps/indexer/lib/indexer/fetcher/token_instance.ex diff --git a/apps/indexer/lib/indexer/fetcher/token_instance.ex b/apps/indexer/lib/indexer/fetcher/token_instance.ex new file mode 100644 index 0000000000..af69b7d869 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/token_instance.ex @@ -0,0 +1,71 @@ +defmodule Indexer.Fetcher.TokenInstance do + @moduledoc """ + Fetches information about a token instance. + """ + + use Indexer.Fetcher + use Spandex.Decorators + + alias Explorer.Chain + alias Explorer.Token.InstanceMetadataRetriever + alias Indexer.BufferedTask + + @behaviour BufferedTask + + @defaults [ + flush_interval: 300, + max_batch_size: 1, + max_concurrency: 10, + task_supervisor: Indexer.Fetcher.TokenInstacne.TaskSupervisor + ] + + @doc false + def child_spec([init_options, gen_server_options]) do + {state, mergeable_init_options} = Keyword.pop(init_options, :json_rpc_named_arguments) + + unless state do + raise ArgumentError, + ":json_rpc_named_arguments must be provided to `#{__MODULE__}.child_spec " <> + "to allow for json_rpc calls when running." + end + + merged_init_opts = + @defaults + |> Keyword.merge(mergeable_init_options) + |> Keyword.put(:state, state) + + Supervisor.child_spec({BufferedTask, [{__MODULE__, merged_init_opts}, gen_server_options]}, id: __MODULE__) + end + + @impl BufferedTask + def init(initial_acc, reducer, _) do + {:ok, acc} = + Chain.stream_unfetched_token_instances(initial_acc, fn data, acc -> + reducer.(data, acc) + end) + + acc + end + + @impl BufferedTask + def run([%{token_contract_address_hash: token_contract_address_hash, token_id: token_id}], _json_rpc_named_arguments) do + {:ok, metadata} = InstanceMetadataRetriever.fetch_metadata(token_contract_address_hash, token_id) + + params = %{ + token_id: token_id, + token_contract_address_hash: token_contract_address_hash, + metadata: metadata + } + + {:ok, _result} = Chain.upsert_token_instance(params) + + :ok + end + + @doc """ + Fetches token instance data asynchronously. + """ + def async_fetch(data) do + BufferedTask.buffer(__MODULE__, data) + end +end diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index 00fba8d428..a5ffec4833 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -19,6 +19,7 @@ defmodule Indexer.Supervisor do StakingPools, Token, TokenBalance, + TokenInstance, TokenUpdater, UncleBlock } @@ -111,6 +112,8 @@ defmodule Indexer.Supervisor do {CoinBalance.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {Token.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, + {TokenInstance.Supervisor, + [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {ContractCode.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {TokenBalance.Supervisor, From c8db2a5f505fec669bd7b59e64455ff050a8f1d2 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 16 Sep 2019 14:55:08 +0300 Subject: [PATCH 30/92] fix token instance fetcher --- .../token/instance_metadata_retriever.ex | 8 ++++--- .../lib/indexer/fetcher/token_instance.ex | 22 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex index 62fd568704..4afb55c879 100644 --- a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex @@ -39,12 +39,14 @@ defmodule Explorer.Token.InstanceMetadataRetriever do Reader.query_contract(contract_address_hash, @abi, contract_functions) end + defp fetch_json(%{"tokenURI" => {:ok, [""]}}) do + {:error, :no_uri} + end + defp fetch_json(%{"tokenURI" => {:ok, [token_uri]}}) do case HTTPoison.get(token_uri) do {:ok, %Response{body: body, status_code: 200}} -> - data = Jason.decode(body) - - {:ok, data} + Jason.decode(body) {:ok, %Response{body: body}} -> {:error, body} diff --git a/apps/indexer/lib/indexer/fetcher/token_instance.ex b/apps/indexer/lib/indexer/fetcher/token_instance.ex index af69b7d869..8495309df8 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance.ex @@ -16,7 +16,7 @@ defmodule Indexer.Fetcher.TokenInstance do flush_interval: 300, max_batch_size: 1, max_concurrency: 10, - task_supervisor: Indexer.Fetcher.TokenInstacne.TaskSupervisor + task_supervisor: Indexer.Fetcher.TokenInstance.TaskSupervisor ] @doc false @@ -48,16 +48,20 @@ defmodule Indexer.Fetcher.TokenInstance do end @impl BufferedTask - def run([%{token_contract_address_hash: token_contract_address_hash, token_id: token_id}], _json_rpc_named_arguments) do - {:ok, metadata} = InstanceMetadataRetriever.fetch_metadata(token_contract_address_hash, token_id) + def run([%{contract_address_hash: token_contract_address_hash, token_id: token_id}], _json_rpc_named_arguments) do + case InstanceMetadataRetriever.fetch_metadata(token_contract_address_hash, Decimal.to_integer(token_id)) do + {:ok, metadata} -> + params = %{ + token_id: token_id, + token_contract_address_hash: token_contract_address_hash, + metadata: metadata + } - params = %{ - token_id: token_id, - token_contract_address_hash: token_contract_address_hash, - metadata: metadata - } + {:ok, _result} = Chain.upsert_token_instance(params) - {:ok, _result} = Chain.upsert_token_instance(params) + _other -> + :ok + end :ok end From 11ef5f50993f94d1dafb81a1c9aa3b00161df6a4 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 16 Sep 2019 15:42:02 +0300 Subject: [PATCH 31/92] add image instance --- apps/block_scout_web/assets/css/app.scss | 1 + .../_erc721_token_image_container.scss | 10 +++++++ .../lib/block_scout_web/csp_header.ex | 2 +- .../instance/overview/_details.html.eex | 30 ++++--------------- .../tokens/instance/transfer/index.html.eex | 1 + .../views/tokens/instance/overview_view.ex | 10 +++++++ apps/explorer/lib/explorer/chain.ex | 1 + .../lib/explorer/chain/token_transfer.ex | 8 +++++ 8 files changed, 38 insertions(+), 25 deletions(-) create mode 100644 apps/block_scout_web/assets/css/components/_erc721_token_image_container.scss diff --git a/apps/block_scout_web/assets/css/app.scss b/apps/block_scout_web/assets/css/app.scss index 4563ec33d5..93cc75e899 100644 --- a/apps/block_scout_web/assets/css/app.scss +++ b/apps/block_scout_web/assets/css/app.scss @@ -114,6 +114,7 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "components/radio_big"; @import "components/btn_no_border"; @import "components/custom_tooltips_block_details"; +@import "components/_erc721_token_image_container"; @import "theme/dark-theme"; diff --git a/apps/block_scout_web/assets/css/components/_erc721_token_image_container.scss b/apps/block_scout_web/assets/css/components/_erc721_token_image_container.scss new file mode 100644 index 0000000000..77ffc633fe --- /dev/null +++ b/apps/block_scout_web/assets/css/components/_erc721_token_image_container.scss @@ -0,0 +1,10 @@ +/* ERC721 image block */ +.erc721-image { + display: flex; + justify-content: center; +} + + .erc721-image img { + height: 200px; +} +/* ERC721 image block end */ diff --git a/apps/block_scout_web/lib/block_scout_web/csp_header.ex b/apps/block_scout_web/lib/block_scout_web/csp_header.ex index 199ee9f189..ace7894e86 100644 --- a/apps/block_scout_web/lib/block_scout_web/csp_header.ex +++ b/apps/block_scout_web/lib/block_scout_web/csp_header.ex @@ -15,7 +15,7 @@ defmodule BlockScoutWeb.CSPHeader do default-src 'self';\ script-src 'self' 'unsafe-inline' 'unsafe-eval';\ style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com;\ - img-src 'self' 'unsafe-inline' 'unsafe-eval' data:;\ + img-src 'self' 'unsafe-inline' 'unsafe-eval' data: https:;\ font-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.gstatic.com data:;\ " }) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex index 25a3c4f7ab..d8458a603e 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex @@ -67,34 +67,16 @@

- <%= if total_supply?(@token) do %> -
-
-
-

<%= gettext "Total Supply" %>

-
-

- <%= if decimals?(@token) do %> - <%= format_according_to_decimals(@token.total_supply, @token.decimals) %> - <% else %> - <%= format_integer_to_currency(@token.total_supply) %> - <% end %> <%= @token.symbol %> -

- <%= if @token.usd_value do %> -
- - | - -
- <% else %> -
- <% end %> -
+
+
+
+
+ />
- <% end %> +
+ + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex index c026783b08..fe0eb5c0e9 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex @@ -2,14 +2,14 @@ <%= link( gettext("Token Transfers"), class: "card-tab #{tab_status("token_transfers", @conn.request_path)}", - to: token_path(@conn, :show, @token.contract_address_hash) + to: token_instance_path(@conn, :show, @token.contract_address_hash, to_string(@token_instance.token_id)) ) %> - <%= if smart_contract_with_read_only_functions?(@token) do %> + <%= if @token_instance.instance do %> <%= link( - gettext("Read Contract"), - to: token_read_contract_path(@conn, :index, @token.contract_address_hash), - class: "card-tab #{tab_status("read_contract", @conn.request_path)}") + gettext("Metadata"), + to: token_instance_metadata_path(@conn, :index, @token.contract_address_hash, to_string(@token_instance.token_id)), + class: "card-tab #{tab_status("metadata", @conn.request_path)}") %> <% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/metadata_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/metadata_view.ex new file mode 100644 index 0000000000..5a19f39684 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/metadata_view.ex @@ -0,0 +1,9 @@ +defmodule BlockScoutWeb.Tokens.Instance.MetadataView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.Tokens.Instance.OverviewView + + def format_metadata(nil), do: "" + + def format_metadata(metadata), do: Poison.encode!(metadata, pretty: true) +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex index 9632cc6067..01cfa62aae 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex @@ -6,6 +6,8 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do import BlockScoutWeb.APIDocsView, only: [blockscout_url: 0] + @tabs ["token_transfers", "metadata"] + def token_name?(%Token{name: nil}), do: false def token_name?(%Token{name: _}), do: true @@ -48,4 +50,13 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do |> QRCode.to_png() |> Base.encode64() end + + def current_tab_name(request_path) do + @tabs + |> Enum.filter(&tab_active?(&1, request_path)) + |> tab_name() + end + + defp tab_name(["token_transfers"]), do: gettext("Token Transfers") + defp tab_name(["metadata"]), do: gettext("Metadata") end diff --git a/apps/block_scout_web/lib/block_scout_web/web_router.ex b/apps/block_scout_web/lib/block_scout_web/web_router.ex index 12ab36ec10..f5b1701aae 100644 --- a/apps/block_scout_web/lib/block_scout_web/web_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/web_router.ex @@ -191,6 +191,13 @@ defmodule BlockScoutWeb.WebRouter do only: [:index], as: :transfer ) + + resources( + "/metadata", + Tokens.Instance.MetadataController, + only: [:index], + as: :metadata + ) end end From 382725bd7579b79eec56c5f9d168c5f9059d7488 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 17 Sep 2019 13:27:33 +0300 Subject: [PATCH 37/92] fix gettext --- apps/block_scout_web/priv/gettext/default.pot | 14 +++++++++++++- .../priv/gettext/en/LC_MESSAGES/default.po | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index e8bb588024..97d62edfa3 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -942,6 +942,7 @@ msgstr "" #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:15 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:60 #: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/transaction_view.ex:313 msgid "Token Transfers" @@ -1283,7 +1284,6 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:58 -#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 #: lib/block_scout_web/views/address_view.ex:309 #: lib/block_scout_web/views/tokens/overview_view.ex:37 @@ -1861,3 +1861,15 @@ msgstr "" #: lib/block_scout_web/templates/chain/show.html.eex:56 msgid "Total transactions" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:20 +msgid "Copy Metadata" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:61 +msgid "Metadata" +msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index e8bb588024..97d62edfa3 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -942,6 +942,7 @@ msgstr "" #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:15 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:60 #: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/transaction_view.ex:313 msgid "Token Transfers" @@ -1283,7 +1284,6 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:58 -#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 #: lib/block_scout_web/views/address_view.ex:309 #: lib/block_scout_web/views/tokens/overview_view.ex:37 @@ -1861,3 +1861,15 @@ msgstr "" #: lib/block_scout_web/templates/chain/show.html.eex:56 msgid "Total transactions" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:20 +msgid "Copy Metadata" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:61 +msgid "Metadata" +msgstr "" From afe3cb287bab4a3ae9e6b2ee975eb42544af43a5 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 17 Sep 2019 15:47:26 +0300 Subject: [PATCH 38/92] add cryptokitties metadata --- .../views/tokens/instance/overview_view.ex | 8 ++++---- .../lib/explorer/token/instance_metadata_retriever.ex | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex index 01cfa62aae..5cc353388c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex @@ -20,10 +20,10 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do def image_src(nil), do: "/images/ether1_logo.svg" def image_src(instance) do - if instance.metadata && instance.metadata["image"] do - instance.metadata["image"] - else - image_src(nil) + cond do + instance.metadata && instance.metadata["image"] -> instance.metadata["image_url"] + instance.metadata && instance.metadata["image"] -> instance.metadata && instance.metadata["image"] + true -> image_src(nil) end end diff --git a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex index 4afb55c879..a4cf460e74 100644 --- a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex @@ -27,6 +27,13 @@ defmodule Explorer.Token.InstanceMetadataRetriever do } ] + @cryptokitties_address_hash "0x06012c8cf97bead5deae237070f9587f8e7a266d" + + def fetch_metadata(unquote(@cryptokitties_address_hash), token_id) do + %{"tokenURI" => {:ok, ["https://api.cryptokitties.co/kitties/#{token_id}"]}} + |> fetch_json() + end + def fetch_metadata(contract_address_hash, token_id) do contract_functions = %{"tokenURI" => [token_id]} From dff0c1a531d9a7b53f623e576a18f4fff43c0b08 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 18 Sep 2019 16:14:57 +0300 Subject: [PATCH 39/92] fix image src --- .../block_scout_web/views/tokens/instance/overview_view.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex index 5cc353388c..525662296f 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex @@ -21,8 +21,8 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do def image_src(instance) do cond do - instance.metadata && instance.metadata["image"] -> instance.metadata["image_url"] - instance.metadata && instance.metadata["image"] -> instance.metadata && instance.metadata["image"] + instance.metadata && instance.metadata["image_url"] -> instance.metadata["image_url"] + instance.metadata && instance.metadata["image"] -> instance.metadata["image"] true -> image_src(nil) end end From 026cbdb13ef1faf7fb82228a6acb65e26f52a9aa Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 18 Sep 2019 16:20:42 +0300 Subject: [PATCH 40/92] fix token instances query --- apps/explorer/lib/explorer/chain.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 43f448b6c6..23cbd7bd3a 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2837,7 +2837,7 @@ defmodule Explorer.Chain do left_join: instance in Instance, on: token_transfer.token_id == instance.token_id, where: token.type == ^"ERC-721" and is_nil(instance.token_id), - distinct: token_transfer.token_id, + distinct: [token_transfer.token_contract_address_hash, token_transfer.token_id], select: %{contract_address_hash: token_transfer.token_contract_address_hash, token_id: token_transfer.token_id} ) From 91493752cc518b447a9d52738ec04ae9624bbfb1 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 18 Sep 2019 16:24:01 +0300 Subject: [PATCH 41/92] fix gettext --- apps/block_scout_web/priv/gettext/default.pot | 25 ++++++++++++++++-- .../priv/gettext/en/LC_MESSAGES/default.po | 26 ++++++++++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 53ba1bed56..573789b896 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -300,7 +300,6 @@ msgstr "" msgid "Clear" msgstr "" - #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:42 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:165 @@ -1575,7 +1574,6 @@ msgstr "" msgid "Transactions sent" msgstr "" - #, elixir-format #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:40 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:47 @@ -1852,3 +1850,26 @@ msgstr "" #: lib/block_scout_web/views/tokens/instance/overview_view.ex:61 msgid "Metadata" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37 +#: lib/block_scout_web/templates/address/overview.html.eex:145 +#: lib/block_scout_web/templates/address/overview.html.eex:153 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:87 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:95 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:110 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:118 +msgid "Close" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:62 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:67 +msgid "Decimals" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:60 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:64 +msgid "Transfers" +msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 00be0f004e..b3484d2901 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -300,7 +300,6 @@ msgstr "" msgid "Clear" msgstr "" - #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:42 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:165 @@ -517,7 +516,6 @@ msgstr "" msgid "Data" msgstr "" - #, elixir-format #: lib/block_scout_web/templates/address_logs/_logs.html.eex:31 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:37 @@ -1576,7 +1574,6 @@ msgstr "" msgid "Transactions sent" msgstr "" - #, elixir-format #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:40 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:47 @@ -1853,3 +1850,26 @@ msgstr "" #: lib/block_scout_web/views/tokens/instance/overview_view.ex:61 msgid "Metadata" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37 +#: lib/block_scout_web/templates/address/overview.html.eex:145 +#: lib/block_scout_web/templates/address/overview.html.eex:153 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:87 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:95 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:110 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:118 +msgid "Close" +msgstr "" + +#, elixir-format, fuzzy +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:62 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:67 +msgid "Decimals" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:60 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:64 +msgid "Transfers" +msgstr "" From 01079123001b1128c33c452b940bdff98b29c12e Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 18 Sep 2019 16:38:14 +0300 Subject: [PATCH 42/92] fix token instance metadata tab --- .../templates/tokens/instance/metadata/index.html.eex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex index 3fa8615cdd..ed95835d00 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex @@ -11,8 +11,8 @@
-
<%= render OverviewView, "_tabs.html", assigns %> +

<%= gettext "Metadata" %>

From a349299c2176381d68b9cae1a41554b0e62db2f6 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 19 Sep 2019 10:51:23 +0300 Subject: [PATCH 43/92] fix style issues --- CHANGELOG.md | 2 +- .../templates/tokens/instance/metadata/index.html.eex | 1 - apps/explorer/lib/explorer/chain/token_transfer.ex | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd6d2e21e8..3486f1b2f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Current ### Features +- [#2642](https://github.com/poanetwork/blockscout/pull/2642) - add ERC721 coin instance page - [#2679](https://github.com/poanetwork/blockscout/pull/2679) - added fixed height for card chain blocks and card chain transactions - [#2678](https://github.com/poanetwork/blockscout/pull/2678) - fixed dashboard banner height bug - [#2672](https://github.com/poanetwork/blockscout/pull/2672) - added new theme for xUSDT @@ -8,7 +9,6 @@ - [#2666](https://github.com/poanetwork/blockscout/pull/2666) - fetch token counters in parallel - [#2665](https://github.com/poanetwork/blockscout/pull/2665) - new menu layout for mobile devices - [#2663](https://github.com/poanetwork/blockscout/pull/2663) - Fetch address counters in parallel -- [#2642](https://github.com/poanetwork/blockscout/pull/2642) - add ERC721 coin instance page ### Fixes - [#2707](https://github.com/poanetwork/blockscout/pull/2707) - fix for dashboard banner chart legend items diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex index ed95835d00..ca5cc7ee20 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex @@ -23,7 +23,6 @@
<%= format_metadata(@token_instance.instance.metadata) %>
             
-
diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 67ed409da6..216a01daf0 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -155,9 +155,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 and - not is_nil(tt.block_number), + where: tt.token_contract_address_hash == ^token_address_hash, + where: tt.token_id == ^token_id, + where: not is_nil(tt.block_number), preload: [{:transaction, :block}, :token, :from_address, :to_address], order_by: [desc: tt.block_number, desc: tt.log_index] ) From 9fbc836571d372d510feb2fb1e969216bf033745 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 19 Sep 2019 14:30:11 +0300 Subject: [PATCH 44/92] fix image html --- .../css/components/_erc721_token_image_container.scss | 2 +- .../templates/tokens/instance/overview/_details.html.eex | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/block_scout_web/assets/css/components/_erc721_token_image_container.scss b/apps/block_scout_web/assets/css/components/_erc721_token_image_container.scss index 77ffc633fe..261cab7d78 100644 --- a/apps/block_scout_web/assets/css/components/_erc721_token_image_container.scss +++ b/apps/block_scout_web/assets/css/components/_erc721_token_image_container.scss @@ -5,6 +5,6 @@ } .erc721-image img { - height: 200px; + height: 150px; } /* ERC721 image block end */ diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex index d8458a603e..22032732a8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex @@ -1,6 +1,6 @@
-
+

@@ -67,8 +67,8 @@

-
-
+
+
/> From 4e1f7bcaf9fde42b9c2652991985960a98d8e398 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 19 Sep 2019 14:53:06 +0300 Subject: [PATCH 45/92] change default instance image --- .../assets/static/images/controller@3x.png | Bin 0 -> 85381 bytes .../views/tokens/instance/overview_view.ex | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 apps/block_scout_web/assets/static/images/controller@3x.png diff --git a/apps/block_scout_web/assets/static/images/controller@3x.png b/apps/block_scout_web/assets/static/images/controller@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..951774ba4a6f8815a280b95ba298f27048538c5d GIT binary patch literal 85381 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

j%!I4u%FrsM9C55ozR_@=7YgUkGQqc4!`8$J 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 Ef>!d{!T Date: Thu, 19 Sep 2019 15:56:11 +0300 Subject: [PATCH 46/92] use svg image --- .../assets/static/images/controller.svg | 7 +++++++ .../assets/static/images/controller@3x.png | Bin 85381 -> 0 bytes .../views/tokens/instance/overview_view.ex | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 apps/block_scout_web/assets/static/images/controller.svg delete mode 100644 apps/block_scout_web/assets/static/images/controller@3x.png diff --git a/apps/block_scout_web/assets/static/images/controller.svg b/apps/block_scout_web/assets/static/images/controller.svg new file mode 100644 index 0000000000..c62d55bebe --- /dev/null +++ b/apps/block_scout_web/assets/static/images/controller.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/block_scout_web/assets/static/images/controller@3x.png b/apps/block_scout_web/assets/static/images/controller@3x.png deleted file mode 100644 index 951774ba4a6f8815a280b95ba298f27048538c5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85381 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

j%!I4u%FrsM9C55ozR_@=7YgUkGQqc4!`8$J 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 Ef>!d{!T Date: Mon, 23 Sep 2019 11:47:04 +0300 Subject: [PATCH 47/92] handle not existing metadata --- .../tokens/instance/metadata_controller.ex | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/metadata_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/metadata_controller.ex index 101dccb431..90b2006543 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/metadata_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/metadata_controller.ex @@ -10,14 +10,18 @@ defmodule BlockScoutWeb.Tokens.Instance.MetadataController do {: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 - render( - conn, - "index.html", - token_instance: token_transfer, - current_path: current_path(conn), - token: Market.add_price(token), - total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id) - ) + if token_transfer.instance && token_transfer.instance.metadata do + render( + conn, + "index.html", + token_instance: token_transfer, + current_path: current_path(conn), + token: Market.add_price(token), + total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id) + ) + else + not_found(conn) + end else _ -> not_found(conn) From ef346f6cfb40db57b31c57e1e33b6d3807041701 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 23 Sep 2019 14:33:10 +0300 Subject: [PATCH 48/92] add contract button --- apps/block_scout_web/assets/css/app.scss | 1 + .../assets/css/components/_btn_contract.scss | 6 +++++ .../assets/css/theme/_dark-theme.scss | 8 +++---- .../instance/overview/_details.html.eex | 23 ++++++++++++------- 4 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 apps/block_scout_web/assets/css/components/_btn_contract.scss diff --git a/apps/block_scout_web/assets/css/app.scss b/apps/block_scout_web/assets/css/app.scss index 93cc75e899..a6c5e40c01 100644 --- a/apps/block_scout_web/assets/css/app.scss +++ b/apps/block_scout_web/assets/css/app.scss @@ -100,6 +100,7 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "components/form"; @import "components/btn_copy"; @import "components/btn_qr"; +@import "components/btn_contract"; @import "components/btn_address_card"; @import "components/btn_dropdown_line"; @import "components/transaction"; diff --git a/apps/block_scout_web/assets/css/components/_btn_contract.scss b/apps/block_scout_web/assets/css/components/_btn_contract.scss new file mode 100644 index 0000000000..e78fbc200a --- /dev/null +++ b/apps/block_scout_web/assets/css/components/_btn_contract.scss @@ -0,0 +1,6 @@ +$btn-contract-color: $primary !default; +$btn-contract-dimensions: 31px !default; + +.btn-contract-icon { + @include square-icon-button($btn-contract-color, $btn-contract-dimensions); +} diff --git a/apps/block_scout_web/assets/css/theme/_dark-theme.scss b/apps/block_scout_web/assets/css/theme/_dark-theme.scss index bd4a97aa11..fec11f0146 100644 --- a/apps/block_scout_web/assets/css/theme/_dark-theme.scss +++ b/apps/block_scout_web/assets/css/theme/_dark-theme.scss @@ -234,7 +234,7 @@ $labels-dark: #8a8dba; // header nav, labels } } - .btn-copy-icon, .btn-qr-icon, .btn-address-card-icon { + .btn-copy-icon, .btn-qr-icon, .btn-address-card-icon .btn-contract-icon { border-color: $dark-primary; path { fill: $dark-primary; @@ -504,7 +504,7 @@ $labels-dark: #8a8dba; // header nav, labels border-top-color: darken($labels-dark, 30); border-bottom-color: darken($labels-dark, 30); } - + // Form Controlls .form-control { background-color: $dark-light; @@ -749,7 +749,7 @@ $labels-dark: #8a8dba; // header nav, labels color: #fff; } } - + // alerts .alert-link { color: $labels-dark; @@ -823,4 +823,4 @@ $labels-dark: #8a8dba; // header nav, labels left: 24%; top: 14%; color: $dark-primary; - } \ No newline at end of file + } diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex index 22032732a8..991c8e13f6 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex @@ -11,6 +11,21 @@ <% end %> + + ' + > + + + + + + <%= to_string(@token_id) %>
- - <%= link to: - address_path(@conn, :show, @token.contract_address_hash), - "data-test": "token_contract_address" - do %> - <%= gettext "View Contract" %> - <% end %> -
<%= @token.type %> <%= @total_token_transfers %> <%= gettext "Transfers" %> From 9e182cbbefc0d243abc6f1cf50c804c510960204 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 23 Sep 2019 14:36:26 +0300 Subject: [PATCH 49/92] fix gettext --- apps/block_scout_web/priv/gettext/default.pot | 21 +++++++++-------- .../priv/gettext/en/LC_MESSAGES/default.po | 23 ++++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index caca0552fc..562804f764 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -1236,8 +1236,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/overview.html.eex:33 #: lib/block_scout_web/templates/address/overview.html.eex:144 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:36 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:86 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:51 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:93 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:109 msgid "QR Code" @@ -1356,7 +1356,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/overview.html.eex:34 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:37 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:52 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:37 msgid "Show QR Code" msgstr "" @@ -1695,7 +1695,8 @@ msgid "View All Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:55 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:55 msgid "View Contract" msgstr "" @@ -1803,8 +1804,8 @@ msgid "true" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:31 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:35 msgid "Copy Token ID" msgstr "" @@ -1855,21 +1856,21 @@ msgstr "" #: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37 #: lib/block_scout_web/templates/address/overview.html.eex:145 #: lib/block_scout_web/templates/address/overview.html.eex:153 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:87 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:95 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:94 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:102 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:110 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:118 msgid "Close" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:62 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:69 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:67 msgid "Decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:60 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:67 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:64 msgid "Transfers" msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 64032a884e..562804f764 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -1236,8 +1236,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/overview.html.eex:33 #: lib/block_scout_web/templates/address/overview.html.eex:144 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:36 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:86 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:51 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:93 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:109 msgid "QR Code" @@ -1356,7 +1356,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/overview.html.eex:34 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:37 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:52 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:37 msgid "Show QR Code" msgstr "" @@ -1695,7 +1695,8 @@ msgid "View All Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:55 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:55 msgid "View Contract" msgstr "" @@ -1803,8 +1804,8 @@ msgid "true" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:31 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:35 msgid "Copy Token ID" msgstr "" @@ -1855,21 +1856,21 @@ msgstr "" #: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37 #: lib/block_scout_web/templates/address/overview.html.eex:145 #: lib/block_scout_web/templates/address/overview.html.eex:153 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:87 -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:95 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:94 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:102 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:110 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:118 msgid "Close" msgstr "" -#, elixir-format, fuzzy -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:62 +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:69 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:67 msgid "Decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:60 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:67 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:64 msgid "Transfers" msgstr "" From 818cf823bbb62c3e71569707b56580097a7b9d70 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 23 Sep 2019 17:19:04 +0300 Subject: [PATCH 50/92] convert smart contract hash to string --- apps/indexer/lib/indexer/fetcher/token_instance.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/indexer/lib/indexer/fetcher/token_instance.ex b/apps/indexer/lib/indexer/fetcher/token_instance.ex index 8495309df8..32d9ac0064 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance.ex @@ -49,7 +49,7 @@ defmodule Indexer.Fetcher.TokenInstance do @impl BufferedTask def run([%{contract_address_hash: token_contract_address_hash, token_id: token_id}], _json_rpc_named_arguments) do - case InstanceMetadataRetriever.fetch_metadata(token_contract_address_hash, Decimal.to_integer(token_id)) do + case InstanceMetadataRetriever.fetch_metadata(to_string(token_contract_address_hash), Decimal.to_integer(token_id)) do {:ok, metadata} -> params = %{ token_id: token_id, From a5062595378ebb6fc7762d587a7c2c83e00a9010 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 24 Sep 2019 12:59:16 +0300 Subject: [PATCH 51/92] fix library verification bytecode of a library contains address of a deployed library --- .../lib/explorer/smart_contract/verifier.ex | 14 +++++++++- .../explorer/smart_contract/verifier_test.exs | 28 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/verifier.ex index 4c2daa680c..4303cf5b6b 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier.ex @@ -69,7 +69,8 @@ defmodule Explorer.SmartContract.Verifier do blockchain_bytecode_without_whisper = extract_bytecode(blockchain_bytecode) cond do - generated_bytecode != blockchain_bytecode_without_whisper -> + generated_bytecode != blockchain_bytecode_without_whisper && + !try_library_verification(generated_bytecode, blockchain_bytecode_without_whisper) -> {:error, :generated_bytecode} has_constructor_with_params?(abi) && @@ -81,6 +82,17 @@ defmodule Explorer.SmartContract.Verifier do end end + defp try_library_verification( + "730000000000000000000000000000000000000000" <> bytecode, + <<_address::binary-size(42)>> <> bytecode + ) do + true + end + + defp try_library_verification(_, _) do + false + end + @doc """ In order to discover the bytecode we need to remove the `swarm source` from the hash. diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs index fba0622964..088820beea 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier_test.exs @@ -144,6 +144,34 @@ defmodule Explorer.SmartContract.VerifierTest do assert abi != nil end + test "verifies a library" do + bytecode = + "0x7349f540c22cba15c47a08c235e20081474201a742301460806040526004361060335760003560e01c8063c2985578146038575b600080fd5b603e60b0565b6040805160208082528351818301528351919283929083019185019080838360005b8381101560765781810151838201526020016060565b50505050905090810190601f16801560a25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080518082019091526003815262666f6f60e81b60208201529056fea265627a7a72315820174b282a3ef3b9778d79fbc2e4c36bc939c54dfaaaa51d3122ee6e648093844c64736f6c634300050b0032" + + contract_address = insert(:contract_address, contract_code: bytecode) + + code = """ + pragma solidity 0.5.11; + + library Foo { + function foo() external pure returns (string memory) { + return "foo"; + } + } + """ + + params = %{ + "contract_source_code" => code, + "compiler_version" => "v0.5.11+commit.c082d0b4", + "evm_version" => "default", + "name" => "Foo", + "optimization" => true + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + test "verifies smart contract compiled with Solidity 0.5.9 (includes new metadata in bytecode) with constructor args" do path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol" contract = File.read!(path) From 6b442635ebe8123a8c2559737cfe72742aab0526 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 24 Sep 2019 13:02:24 +0300 Subject: [PATCH 52/92] add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c805d8f45..4a53de6a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - [#2663](https://github.com/poanetwork/blockscout/pull/2663) - Fetch address counters in parallel ### Fixes +- [#2731](https://github.com/poanetwork/blockscout/pull/2731) - fix library verification - [#2718](https://github.com/poanetwork/blockscout/pull/2718) - Include all addresses taking part in transactions in wallets' addresses counter - [#2709](https://github.com/poanetwork/blockscout/pull/2709) - Fix stuck label and value for uncle block height - [#2707](https://github.com/poanetwork/blockscout/pull/2707) - fix for dashboard banner chart legend items From 10b19cefa313153acadbcdfbbd522bcaa5a96a99 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 24 Sep 2019 13:27:39 +0300 Subject: [PATCH 53/92] fix gettext --- apps/block_scout_web/priv/gettext/default.pot | 79 ++++++++++++++++++- .../priv/gettext/en/LC_MESSAGES/default.po | 79 ++++++++++++++++++- 2 files changed, 156 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index f5eb44b04a..da3dbf5da7 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -1801,4 +1801,81 @@ msgstr "" #, elixir-format #: lib/block_scout_web/views/address_contract_view.ex:22 msgid "true" -msgstr "" \ No newline at end of file +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37 +#: lib/block_scout_web/templates/address/overview.html.eex:145 +#: lib/block_scout_web/templates/address/overview.html.eex:153 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:94 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:102 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:110 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:118 +msgid "Close" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:20 +msgid "Copy Metadata" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:31 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:35 +msgid "Copy Token ID" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:69 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:67 +msgid "Decimals" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/overview.html.eex:232 +msgid "Gas" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:26 +msgid "If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/layout/app.html.eex:58 +msgid "Indexing Tokens" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:22 +#: lib/block_scout_web/templates/chain/show.html.eex:13 +msgid "Loading chart" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:189 +msgid "Log Index" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:61 +msgid "Metadata" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:58 +msgid "Module" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/chain/show.html.eex:56 +msgid "Total transactions" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:67 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:64 +msgid "Transfers" +msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index f5eb44b04a..6112629db5 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -1801,4 +1801,81 @@ msgstr "" #, elixir-format #: lib/block_scout_web/views/address_contract_view.ex:22 msgid "true" -msgstr "" \ No newline at end of file +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37 +#: lib/block_scout_web/templates/address/overview.html.eex:145 +#: lib/block_scout_web/templates/address/overview.html.eex:153 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:94 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:102 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:110 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:118 +msgid "Close" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:20 +msgid "Copy Metadata" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:31 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:35 +msgid "Copy Token ID" +msgstr "" + +#, elixir-format, fuzzy +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:69 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:67 +msgid "Decimals" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/overview.html.eex:232 +msgid "Gas" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:26 +msgid "If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/layout/app.html.eex:58 +msgid "Indexing Tokens" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:22 +#: lib/block_scout_web/templates/chain/show.html.eex:13 +msgid "Loading chart" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:189 +msgid "Log Index" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:61 +msgid "Metadata" +msgstr "" + +#, elixir-format, fuzzy +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:58 +msgid "Module" +msgstr "" + +#, elixir-format, fuzzy +#: lib/block_scout_web/templates/chain/show.html.eex:56 +msgid "Total transactions" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:67 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:64 +msgid "Transfers" +msgstr "" From ec20050a662e8a37c5506944b3e021fa2198e83f Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 24 Sep 2019 14:14:21 +0300 Subject: [PATCH 54/92] add comment --- apps/explorer/lib/explorer/smart_contract/verifier.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/verifier.ex index 4303cf5b6b..ed1b6532c5 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier.ex @@ -82,6 +82,7 @@ defmodule Explorer.SmartContract.Verifier do end end + # 730000000000000000000000000000000000000000 - default library address that returned by the compiler defp try_library_verification( "730000000000000000000000000000000000000000" <> bytecode, <<_address::binary-size(42)>> <> bytecode From 749f51a7670ee49b055399167e8f6ef8c26288fc Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 24 Sep 2019 14:43:55 +0300 Subject: [PATCH 55/92] remove ordering --- apps/explorer/lib/explorer/chain.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 6ddda050d4..6abd211335 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3164,7 +3164,6 @@ defmodule Explorer.Chain do query = from(tt in TokenTransfer, where: tt.token_contract_address_hash == ^token_contract_address and tt.token_id == ^token_id, - order_by: [desc: tt.block_number], preload: [:instance], limit: 1 ) From 15c66652a8c3daebc8a533010174a3f2c5be64c5 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 24 Sep 2019 15:55:31 +0300 Subject: [PATCH 56/92] fix query --- apps/explorer/lib/explorer/chain.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 6abd211335..248482b3bc 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3163,9 +3163,11 @@ defmodule Explorer.Chain do def erc721_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, where: tt.token_contract_address_hash == ^token_contract_address and tt.token_id == ^token_id, - preload: [:instance], - limit: 1 + limit: 1, + select: %{tt | instance: instance} ) case Repo.one(query) do From 624946a12bf090e1e02e47755e5fbf5fcc954319 Mon Sep 17 00:00:00 2001 From: pasqu4le Date: Tue, 24 Sep 2019 19:13:26 +0200 Subject: [PATCH 57/92] Add pending transactions cache --- CHANGELOG.md | 1 + .../block_scout_web/test/support/conn_case.ex | 2 + .../test/support/feature_case.ex | 2 + apps/explorer/config/config.exs | 4 ++ apps/explorer/lib/explorer/application.ex | 4 +- apps/explorer/lib/explorer/chain.ex | 19 +++++++++ .../chain/cache/pending_transactions.ex | 42 +++++++++++++++++++ .../explorer/chain/import/runner/blocks.ex | 12 +----- .../chain/cache/pending_transactions_test.exs | 17 ++++++++ apps/explorer/test/support/data_case.ex | 2 + apps/indexer/lib/indexer/block/fetcher.ex | 8 ++-- .../indexer/fetcher/pending_transaction.ex | 3 +- .../lib/indexer/fetcher/uncle_block.ex | 8 ++-- 13 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 apps/explorer/lib/explorer/chain/cache/pending_transactions.ex create mode 100644 apps/explorer/test/explorer/chain/cache/pending_transactions_test.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2311bc9d4c..68d65f4c69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Current ### Features +- [#2735](https://github.com/poanetwork/blockscout/pull/2735) - Add pending transactions cache - [#2717](https://github.com/poanetwork/blockscout/pull/2717) - Improve speed of nonconsensus data removal - [#2679](https://github.com/poanetwork/blockscout/pull/2679) - added fixed height for card chain blocks and card chain transactions - [#2678](https://github.com/poanetwork/blockscout/pull/2678) - fixed dashboard banner height bug diff --git a/apps/block_scout_web/test/support/conn_case.ex b/apps/block_scout_web/test/support/conn_case.ex index ed3f094c34..5540346d34 100644 --- a/apps/block_scout_web/test/support/conn_case.ex +++ b/apps/block_scout_web/test/support/conn_case.ex @@ -44,6 +44,8 @@ defmodule BlockScoutWeb.ConnCase do Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Transactions.child_id()) Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Accounts.child_id()) Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Accounts.child_id()) + Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.PendingTransactions.child_id()) + Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.PendingTransactions.child_id()) {:ok, conn: Phoenix.ConnTest.build_conn()} end diff --git a/apps/block_scout_web/test/support/feature_case.ex b/apps/block_scout_web/test/support/feature_case.ex index f6b1aedf24..2dd1d0635c 100644 --- a/apps/block_scout_web/test/support/feature_case.ex +++ b/apps/block_scout_web/test/support/feature_case.ex @@ -31,6 +31,8 @@ defmodule BlockScoutWeb.FeatureCase do Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Transactions.child_id()) Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Accounts.child_id()) Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Accounts.child_id()) + Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.PendingTransactions.child_id()) + Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.PendingTransactions.child_id()) metadata = Phoenix.Ecto.SQL.Sandbox.metadata_for(Explorer.Repo, self()) {:ok, session} = Wallaby.start_session(metadata: metadata) diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 0cf1a05b53..1d4fbc6204 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -145,6 +145,10 @@ config :explorer, Explorer.Chain.Cache.Accounts, ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false), global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5)) +config :explorer, Explorer.Chain.Cache.PendingTransactions, + ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false), + global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5)) + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 53d8c03ea3..9a8dff98d0 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -13,6 +13,7 @@ defmodule Explorer.Application do BlockNumber, Blocks, NetVersion, + PendingTransactions, TransactionCount, Transactions } @@ -51,7 +52,8 @@ defmodule Explorer.Application do con_cache_child_spec(MarketHistoryCache.cache_name()), con_cache_child_spec(RSK.cache_name(), ttl_check_interval: :timer.minutes(1), global_ttl: :timer.minutes(30)), Transactions, - Accounts + Accounts, + PendingTransactions ] children = base_children ++ configurable_children() diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index f89d933d6d..81f6061da5 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -53,6 +53,7 @@ defmodule Explorer.Chain do BlockCount, BlockNumber, Blocks, + PendingTransactions, TransactionCount, Transactions } @@ -2215,6 +2216,24 @@ defmodule Explorer.Chain do necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) paging_options = Keyword.get(options, :paging_options, @default_paging_options) + if is_nil(paging_options.key) do + paging_options.page_size + |> PendingTransactions.take_enough() + |> case do + nil -> + pending_transactions = fetch_recent_pending_transactions(paging_options, necessity_by_association) + PendingTransactions.update(pending_transactions) + pending_transactions + + pending_transactions -> + pending_transactions + end + else + fetch_recent_pending_transactions(paging_options, necessity_by_association) + end + end + + defp fetch_recent_pending_transactions(paging_options, necessity_by_association) do Transaction |> page_pending_transaction(paging_options) |> limit(^paging_options.page_size) diff --git a/apps/explorer/lib/explorer/chain/cache/pending_transactions.ex b/apps/explorer/lib/explorer/chain/cache/pending_transactions.ex new file mode 100644 index 0000000000..686b5e7372 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/cache/pending_transactions.ex @@ -0,0 +1,42 @@ +defmodule Explorer.Chain.Cache.PendingTransactions do + @moduledoc """ + Caches the latest pending transactions + """ + + alias Explorer.Chain.Transaction + + use Explorer.Chain.OrderedCache, + name: :pending_transactions, + max_size: 51, + preloads: [ + :block, + created_contract_address: :names, + from_address: :names, + to_address: :names, + token_transfers: :token, + token_transfers: :from_address, + token_transfers: :to_address + ], + ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval], + global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl] + + @type element :: Transaction.t() + + @type id :: {non_neg_integer(), non_neg_integer()} + + def element_to_id(%Transaction{inserted_at: inserted_at, hash: hash}) do + {inserted_at, hash} + end + + def update_pending(transactions) when is_nil(transactions), do: :ok + + def update_pending(transactions) do + transactions + |> Enum.filter(&pending?(&1)) + |> update() + end + + defp pending?(transaction) do + is_nil(transaction.block_hash) and (is_nil(transaction.error) or transaction.error != "dropped/replaced") + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex index 394e21cbaa..c6eab88d10 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -168,11 +168,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do query = from( transaction in where_forked(blocks_changes), - select: %{ - block_hash: transaction.block_hash, - index: transaction.index, - hash: transaction.hash - }, + select: transaction, # Enforce Transaction ShareLocks order (see docs: sharelocks.md) order_by: [asc: :hash], lock: "FOR UPDATE" @@ -196,11 +192,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do updated_at: ^updated_at ] ], - select: %{ - block_hash: s.block_hash, - index: s.index, - hash: s.hash - } + select: s ) {_num, transactions} = repo.update_all(update_query, [], timeout: timeout) diff --git a/apps/explorer/test/explorer/chain/cache/pending_transactions_test.exs b/apps/explorer/test/explorer/chain/cache/pending_transactions_test.exs new file mode 100644 index 0000000000..26339bc3d7 --- /dev/null +++ b/apps/explorer/test/explorer/chain/cache/pending_transactions_test.exs @@ -0,0 +1,17 @@ +defmodule Explorer.Chain.Cache.PendingTransactionsTest do + use Explorer.DataCase + + alias Explorer.Chain.Cache.PendingTransactions + + describe "update_pending/1" do + test "adds a new pending transaction" do + transaction = insert(:transaction, block_hash: nil, error: nil) + + PendingTransactions.update([transaction]) + + transaction_hash = transaction.hash + + assert [%{hash: transaction_hash}] = PendingTransactions.all() + end + end +end diff --git a/apps/explorer/test/support/data_case.ex b/apps/explorer/test/support/data_case.ex index e12dd24a82..90167e676d 100644 --- a/apps/explorer/test/support/data_case.ex +++ b/apps/explorer/test/support/data_case.ex @@ -47,6 +47,8 @@ defmodule Explorer.DataCase do Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Transactions.child_id()) Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Accounts.child_id()) Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Accounts.child_id()) + Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.PendingTransactions.child_id()) + Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.PendingTransactions.child_id()) :ok end diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 1b95d84086..d7cdd88664 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -13,7 +13,7 @@ defmodule Indexer.Block.Fetcher do alias Explorer.Chain alias Explorer.Chain.{Address, Block, Hash, Import, Transaction} alias Explorer.Chain.Cache.Blocks, as: BlocksCache - alias Explorer.Chain.Cache.{Accounts, BlockNumber, Transactions} + alias Explorer.Chain.Cache.{Accounts, BlockNumber, PendingTransactions, Transactions} alias Indexer.Block.Fetcher.Receipts alias Indexer.Fetcher.{ @@ -175,7 +175,7 @@ defmodule Indexer.Block.Fetcher do ) do result = {:ok, %{inserted: inserted, errors: blocks_errors}} update_block_cache(inserted[:blocks]) - update_transactions_cache(inserted[:transactions]) + update_transactions_cache(inserted[:transactions], inserted[:fork_transactions]) update_addresses_cache(inserted[:addresses]) result else @@ -194,8 +194,10 @@ defmodule Indexer.Block.Fetcher do defp update_block_cache(_), do: :ok - defp update_transactions_cache(transactions) do + defp update_transactions_cache(transactions, forked_transactions) do Transactions.update(transactions) + PendingTransactions.update_pending(transactions) + PendingTransactions.update_pending(forked_transactions) end defp update_addresses_cache(addresses), do: Accounts.drop(addresses) diff --git a/apps/indexer/lib/indexer/fetcher/pending_transaction.ex b/apps/indexer/lib/indexer/fetcher/pending_transaction.ex index 176d8dc9b7..0a8389a991 100644 --- a/apps/indexer/lib/indexer/fetcher/pending_transaction.ex +++ b/apps/indexer/lib/indexer/fetcher/pending_transaction.ex @@ -14,7 +14,7 @@ defmodule Indexer.Fetcher.PendingTransaction do alias Ecto.Changeset alias Explorer.Chain - alias Explorer.Chain.Cache.Accounts + alias Explorer.Chain.Cache.{Accounts, PendingTransactions} alias Indexer.Fetcher.PendingTransaction alias Indexer.Transform.Addresses @@ -151,6 +151,7 @@ defmodule Indexer.Fetcher.PendingTransaction do }) do {:ok, imported} -> Accounts.drop(imported[:addresses]) + PendingTransactions.update_pending(imported[:transactions]) :ok {:error, [%Changeset{} | _] = changesets} -> diff --git a/apps/indexer/lib/indexer/fetcher/uncle_block.ex b/apps/indexer/lib/indexer/fetcher/uncle_block.ex index e597d16ca2..3e7ab8da63 100644 --- a/apps/indexer/lib/indexer/fetcher/uncle_block.ex +++ b/apps/indexer/lib/indexer/fetcher/uncle_block.ex @@ -12,7 +12,7 @@ defmodule Indexer.Fetcher.UncleBlock do alias Ecto.Changeset alias EthereumJSONRPC.Blocks alias Explorer.Chain - alias Explorer.Chain.Cache.Accounts + alias Explorer.Chain.Cache.{Accounts, PendingTransactions} alias Explorer.Chain.Hash alias Indexer.{Block, BufferedTask, Tracer} alias Indexer.Fetcher.UncleBlock @@ -155,7 +155,7 @@ defmodule Indexer.Fetcher.UncleBlock do @impl Block.Fetcher def import(_, options) when is_map(options) do - with {:ok, %{block_second_degree_relations: block_second_degree_relations}} = ok <- + with {:ok, %{block_second_degree_relations: block_second_degree_relations} = imported} <- options |> Map.drop(@ignored_options) |> uncle_blocks() @@ -168,8 +168,10 @@ defmodule Indexer.Fetcher.UncleBlock do # * TokenBalance.async_fetch is not called because it uses block numbers from consensus, not uncles UncleBlock.async_fetch_blocks(block_second_degree_relations) + PendingTransactions.update_pending(imported[:transactions]) + PendingTransactions.update_pending(imported[:fork_transactions]) - ok + {:ok, imported} end end From 156c6ca4551036ff42fc9c8df8cbea371f523a65 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 25 Sep 2019 13:36:50 +0300 Subject: [PATCH 58/92] do not update cache if no blocks were inserted --- apps/indexer/lib/indexer/block/fetcher.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 1b95d84086..85b47cba6f 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -184,6 +184,8 @@ defmodule Indexer.Block.Fetcher do end end + defp update_block_cache([]), do: :ok + defp update_block_cache(blocks) when is_list(blocks) do {min_block, max_block} = Enum.min_max_by(blocks, & &1.number) From 3fa83faf2caeaaada3445d9b8257c80753ab3144 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 25 Sep 2019 13:39:09 +0300 Subject: [PATCH 59/92] add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6791e814b7..da3cf2b091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - [#2663](https://github.com/poanetwork/blockscout/pull/2663) - Fetch address counters in parallel ### Fixes +- [#2736](https://github.com/poanetwork/blockscout/pull/2736) - do not update cache if no blocks were inserted - [#2718](https://github.com/poanetwork/blockscout/pull/2718) - Include all addresses taking part in transactions in wallets' addresses counter - [#2709](https://github.com/poanetwork/blockscout/pull/2709) - Fix stuck label and value for uncle block height - [#2707](https://github.com/poanetwork/blockscout/pull/2707) - fix for dashboard banner chart legend items From 5665e514976cb4888c1d29223ec373f8b19413e1 Mon Sep 17 00:00:00 2001 From: Yegor San Date: Wed, 25 Sep 2019 14:30:40 +0300 Subject: [PATCH 60/92] Removed hardcoded subnetwork value. --- .../lib/block_scout_web/templates/layout/_topnav.html.eex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex index 0463d28a15..925028503c 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex @@ -16,7 +16,7 @@ - Sokol Testnet + <%= subnetwork_title() %>