From 6c111614de8ea32f317cada3e733897ef18abfe4 Mon Sep 17 00:00:00 2001 From: Nikita Pozdniakov Date: Sat, 18 Feb 2023 22:12:01 +0300 Subject: [PATCH] Add broadcast for token total supply --- .../assets/js/pages/token_counters.js | 25 +++++++++++++++++++ .../block_scout_web/channels/token_channel.ex | 25 ++++++++++++++++++- .../channels/transaction_channel.ex | 3 +-- .../controllers/tokens/holder_controller.ex | 2 ++ .../lib/block_scout_web/notifier.ex | 8 ++++++ .../block_scout_web/realtime_event_handler.ex | 1 + .../tokens/overview/_details.html.eex | 16 ++++++++++-- .../lib/explorer/chain/events/publisher.ex | 2 +- .../lib/explorer/chain/events/subscriber.ex | 2 +- .../fetcher/token_total_supply_on_demand.ex | 13 +++++----- 10 files changed, 83 insertions(+), 14 deletions(-) diff --git a/apps/block_scout_web/assets/js/pages/token_counters.js b/apps/block_scout_web/assets/js/pages/token_counters.js index e8fe2b5837..5950037380 100644 --- a/apps/block_scout_web/assets/js/pages/token_counters.js +++ b/apps/block_scout_web/assets/js/pages/token_counters.js @@ -7,6 +7,7 @@ import '../app' import { openQrModal } from '../lib/modals' +import { subscribeChannel } from '../socket' export const initialState = { channelDisconnected: false, @@ -26,6 +27,11 @@ export function reducer (state = initialState, action) { tokenHolderCount: action.tokenHolderCount }) } + case 'RECEIVED_NEW_TOTAL_SUPPLY': { + return Object.assign({}, state, { + totalSupply: action.msg.totalSupply + }) + } default: return state } @@ -55,6 +61,16 @@ const elements = { return $el.show() } } + }, + '[data-selector="total-supply-row"]': { + render ($el, state) { + if (state.totalSupply) { + const value = $el.find('[data-selector="total-supply-value"]') + value.empty().append(state.totalSupply + ' ' + value[0].dataset.tokenSymbol) + state.totalSupply = null + return $el.removeClass('d-none') + } + } } } @@ -81,6 +97,15 @@ if ($tokenPage.length) { function updateCounters () { const store = createStore(reducer) connectElements({ store, elements }) + const addressHash = $('[data-page="token-details"]')[0].dataset.pageAddressHash + const tokensChannel = subscribeChannel(`tokens:${addressHash}`) + tokensChannel.on('total_supply', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_TOTAL_SUPPLY', + msg: humps.camelizeKeys(msg) + }) + }) + loadCounters(store) } diff --git a/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex index 56b22c06f3..13c2b370b7 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex @@ -4,12 +4,13 @@ defmodule BlockScoutWeb.TokenChannel do """ use BlockScoutWeb, :channel + alias BlockScoutWeb.{CurrencyHelpers, TokensView} alias BlockScoutWeb.Tokens.TransferView alias Explorer.Chain alias Explorer.Chain.Hash alias Phoenix.View - intercept(["token_transfer"]) + intercept(["token_transfer", "token_total_supply"]) {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") @burn_address_hash burn_address_hash @@ -48,4 +49,26 @@ defmodule BlockScoutWeb.TokenChannel do {:noreply, socket} end + + def handle_out( + "token_total_supply", + %{token: %Explorer.Chain.Token{total_supply: total_supply}}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + push(socket, "total_supply", %{total_supply: to_string(total_supply)}) + + {:noreply, socket} + end + + def handle_out("token_total_supply", %{token: token}, socket) do + push(socket, "total_supply", %{ + total_supply: + if(TokensView.decimals?(token), + do: CurrencyHelpers.format_according_to_decimals(token.total_supply, token.decimals), + else: CurrencyHelpers.format_integer_to_currency(token.total_supply) + ) + }) + + {:noreply, socket} + end end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex index de62c4a625..75f2487329 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex @@ -99,8 +99,7 @@ defmodule BlockScoutWeb.TransactionChannel do internal_transactions = Chain.all_transaction_to_internal_transactions(transaction_hash) push(socket, "raw_trace", %{ - raw_trace: - TransactionViewV2.render("raw_trace.json", %{internal_transactions: internal_transactions}) + raw_trace: TransactionViewV2.render("raw_trace.json", %{internal_transactions: internal_transactions}) }) {:noreply, socket} diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex index 710ff74e75..391a25e6ac 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex @@ -8,6 +8,7 @@ defmodule BlockScoutWeb.Tokens.HolderController do alias BlockScoutWeb.Tokens.HolderView alias Explorer.{Chain, Market} alias Explorer.Chain.Address + alias Indexer.Fetcher.TokenTotalSupplyOnDemand alias Phoenix.View import BlockScoutWeb.Chain, @@ -70,6 +71,7 @@ defmodule BlockScoutWeb.Tokens.HolderController do current_path: Controller.current_full_path(conn), token: Market.add_price(token), counters_path: token_path(conn, :token_counters, %{"id" => Address.checksum(address_hash)}), + token_total_supply_status: TokenTotalSupplyOnDemand.trigger_fetch(address_hash), tags: get_address_tags(address_hash, current_user(conn)) ) else diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index 2e04b29a17..dc95e511c8 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -213,6 +213,14 @@ defmodule BlockScoutWeb.Notifier do Endpoint.broadcast("transactions:stats", "update", %{stats: stats}) end + def handle_event( + {:chain_event, :token_total_supply, :on_demand, + [%Explorer.Chain.Token{contract_address_hash: contract_address_hash, total_supply: total_supply} = token]} + ) + when not is_nil(total_supply) do + Endpoint.broadcast("tokens:#{to_string(contract_address_hash)}", "token_total_supply", %{token: token}) + end + def handle_event(_), do: nil def fetch_compiler_version(compiler) do diff --git a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex index dc37aeef0e..eaf882b3e7 100644 --- a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex +++ b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex @@ -28,6 +28,7 @@ defmodule BlockScoutWeb.RealtimeEventHandler do Subscriber.to(:address_coin_balances, :on_demand) Subscriber.to(:address_token_balances, :on_demand) Subscriber.to(:contract_verification_result, :on_demand) + Subscriber.to(:token_total_supply, :on_demand) # Does not come from the indexer Subscriber.to(:exchange_rate) Subscriber.to(:transaction_stats) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex index 86470ae594..20847a7b33 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex @@ -62,13 +62,13 @@ <%= if total_supply?(@token) do %> -
+
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", text: gettext("The total amount of tokens issued") %> <%= gettext("Total supply") %>
-
+
<%= if decimals?(@token) do %> <%= format_according_to_decimals(@token.total_supply, @token.decimals) %> <% else %> @@ -76,6 +76,18 @@ <% end %> <%= @token.symbol %>
+ <% else %> +
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The total amount of tokens issued") %> + <%= gettext("Total supply") %> +
+
+
+
+ <% end %> + <%= if total_supply?(@token) do %> <%= if @token.usd_value do %>
diff --git a/apps/explorer/lib/explorer/chain/events/publisher.ex b/apps/explorer/lib/explorer/chain/events/publisher.ex index 29d36be7ed..8ebdebff70 100644 --- a/apps/explorer/lib/explorer/chain/events/publisher.ex +++ b/apps/explorer/lib/explorer/chain/events/publisher.ex @@ -3,7 +3,7 @@ defmodule Explorer.Chain.Events.Publisher do Publishes events related to the Chain context. """ - @allowed_events ~w(addresses address_coin_balances address_token_balances blocks block_rewards internal_transactions last_block_number token_transfers transactions contract_verification_result)a + @allowed_events ~w(addresses address_coin_balances address_token_balances blocks block_rewards internal_transactions last_block_number token_transfers transactions contract_verification_result token_total_supply)a def broadcast(_data, false), do: :ok diff --git a/apps/explorer/lib/explorer/chain/events/subscriber.ex b/apps/explorer/lib/explorer/chain/events/subscriber.ex index 295eff9ada..cbaa8c2102 100644 --- a/apps/explorer/lib/explorer/chain/events/subscriber.ex +++ b/apps/explorer/lib/explorer/chain/events/subscriber.ex @@ -3,7 +3,7 @@ defmodule Explorer.Chain.Events.Subscriber do Subscribes to events related to the Chain context. """ - @allowed_broadcast_events ~w(addresses address_coin_balances address_token_balances blocks block_rewards internal_transactions last_block_number token_transfers transactions contract_verification_result)a + @allowed_broadcast_events ~w(addresses address_coin_balances address_token_balances blocks block_rewards internal_transactions last_block_number token_transfers transactions contract_verification_result token_total_supply)a @allowed_broadcast_types ~w(catchup realtime on_demand contract_verification_result)a diff --git a/apps/indexer/lib/indexer/fetcher/token_total_supply_on_demand.ex b/apps/indexer/lib/indexer/fetcher/token_total_supply_on_demand.ex index 6b8d8417a9..9a8366d0d0 100644 --- a/apps/indexer/lib/indexer/fetcher/token_total_supply_on_demand.ex +++ b/apps/indexer/lib/indexer/fetcher/token_total_supply_on_demand.ex @@ -9,6 +9,7 @@ defmodule Indexer.Fetcher.TokenTotalSupplyOnDemand do alias Explorer.{Chain, Repo} alias Explorer.Chain.{Address, Token} alias Explorer.Chain.Cache.BlockNumber + alias Explorer.Chain.Events.Publisher alias Explorer.Token.MetadataRetriever @ttl_in_blocks 1 @@ -33,14 +34,14 @@ defmodule Indexer.Fetcher.TokenTotalSupplyOnDemand do @impl true def handle_cast({:fetch_and_update, address}, state) do - do_trigger_fetch(address) + do_fetch(address) {:noreply, state} end ## Implementation - defp do_trigger_fetch(address) when not is_nil(address) do + defp do_fetch(address) when not is_nil(address) do token = Token |> Repo.get_by(contract_address_hash: address) @@ -54,12 +55,10 @@ defmodule Indexer.Fetcher.TokenTotalSupplyOnDemand do token_address_hash |> MetadataRetriever.get_total_supply_of() - token = - Token - |> Repo.get_by(contract_address_hash: address) - |> Repo.preload([:contract_address]) + {:ok, token} = + Chain.update_token(token, Map.put(token_params, :total_supply_updated_at_block, BlockNumber.get_max())) - {:ok, _} = Chain.update_token(token, Map.put(token_params, :total_supply_updated_at_block, BlockNumber.get_max())) + Publisher.broadcast(%{token_total_supply: [token]}, :on_demand) :ok end end