Token balances refactoring: put token to preloads; fix pagination bugs

np-token-balances-refactoring
Nikita Pozdniakov 1 year ago
parent 7bf491986e
commit 25f50471e2
No known key found for this signature in database
GPG Key ID: F344106F9804FE5F
  1. 23
      apps/block_scout_web/lib/block_scout_web/chain.ex
  2. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex
  3. 4
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex
  4. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_token/_tokens.html.eex
  5. 6
      apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex
  6. 26
      apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_tokens.html.eex
  7. 2
      apps/block_scout_web/lib/block_scout_web/views/address_token_balance_view.ex
  8. 6
      apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex
  9. 18
      apps/explorer/lib/explorer/chain.ex
  10. 13
      apps/explorer/lib/explorer/chain/address/current_token_balance.ex
  11. 6
      apps/explorer/lib/explorer/counters/address_tokens_usd_sum.ex
  12. 11
      apps/indexer/lib/indexer/fetcher/token_balance_on_demand.ex

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.Chain do
import Explorer.Chain,
only: [
balance_in_fiat: 2,
balance_in_fiat: 1,
find_or_insert_address_from_hash: 1,
hash_to_block: 1,
hash_to_transaction: 1,
@ -100,10 +100,17 @@ defmodule BlockScoutWeb.Chain do
end
end
def next_page_params([], _list, _params), do: nil
def next_page_params(next_page, list, params, is_ctb_with_fiat_value \\ false)
def next_page_params(_, list, params) do
next_page_params = Map.merge(params, paging_params(List.last(list)))
def next_page_params([], _list, _params, _), do: nil
def next_page_params(_, list, params, is_ctb_with_fiat_value) do
paging_params =
if is_ctb_with_fiat_value,
do: paging_params_with_fiat_value(List.last(list)),
else: paging_params(List.last(list))
next_page_params = Map.merge(params, paging_params)
current_items_count_str = Map.get(next_page_params, "items_count")
items_count =
@ -451,10 +458,6 @@ defmodule BlockScoutWeb.Chain do
%{"address_hash" => to_string(address_hash), "value" => Decimal.to_integer(value)}
end
defp paging_params({%CurrentTokenBalance{id: id, value: value} = ctb, token}) do
%{"fiat_value" => balance_in_fiat(ctb, token), "value" => value, "id" => id}
end
defp paging_params(%CoinBalance{block_number: block_number}) do
%{"block_number" => block_number}
end
@ -498,6 +501,10 @@ defmodule BlockScoutWeb.Chain do
%{"state_changes" => nil}
end
defp paging_params_with_fiat_value(%CurrentTokenBalance{id: id, value: value} = ctb) do
%{"fiat_value" => balance_in_fiat(ctb), "value" => value, "id" => id}
end
defp block_or_transaction_from_param(param) do
with {:error, :not_found} <- transaction_from_param(param) do
hash_string_to_block(param)

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressTokenController do
use BlockScoutWeb, :controller
import BlockScoutWeb.Chain, only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1]
import BlockScoutWeb.Chain, only: [next_page_params: 4, paging_options: 1, split_list_by_page: 1]
import BlockScoutWeb.Account.AuthController, only: [current_user: 1]
import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2]
@ -22,7 +22,7 @@ defmodule BlockScoutWeb.AddressTokenController do
{tokens, next_page} = split_list_by_page(token_balances_plus_one)
next_page_path =
case next_page_params(next_page, tokens, params) do
case next_page_params(next_page, tokens, params, true) do
nil ->
nil
@ -32,12 +32,12 @@ defmodule BlockScoutWeb.AddressTokenController do
items =
tokens
|> Enum.map(fn {token_balance, token} ->
|> Enum.map(fn token_balance ->
View.render_to_string(
AddressTokenView,
"_tokens.html",
token_balance: token_balance,
token: token,
token: token_balance.token,
address: address,
conn: conn
)

@ -4,6 +4,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
import BlockScoutWeb.Chain,
only: [
next_page_params: 3,
next_page_params: 4,
token_transfers_next_page_params: 3,
paging_options: 1,
split_list_by_page: 1,
@ -365,7 +366,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do
{tokens, next_page} = split_list_by_page(results_plus_one)
next_page_params = next_page |> next_page_params(tokens, params) |> delete_parameters_from_next_page_params()
next_page_params =
next_page |> next_page_params(tokens, params, true) |> delete_parameters_from_next_page_params()
conn
|> put_status(200)

@ -40,7 +40,7 @@
<td class="stakes-td">
<%= if token_price && @token.decimals do %>
<p class="mb-0 col-md-6 text-right">
<span data-selector="token-balance-usd" data-usd-value="<%= Chain.balance_in_fiat(@token_balance, @token) %>"><%= ChainView.format_usd_value(Chain.balance_in_fiat(@token_balance, @token)) %></span>
<span data-selector="token-balance-usd" data-usd-value="<%= Chain.balance_in_fiat(@token_balance) %>"><%= ChainView.format_usd_value(Chain.balance_in_fiat(@token_balance)) %></span>
</p>
<% end %>
</td>

@ -47,7 +47,7 @@
placeholder: gettext("Search tokens")
) %>
</div>
<%= if Enum.any?(@token_balances, fn {_token_balance, token} -> token.type == "ERC-721" end) do %>
<%= if Enum.any?(@token_balances, fn token_balance -> token_balance.token.type == "ERC-721" end) do %>
<%= render(
"_tokens.html",
conn: @conn,
@ -56,7 +56,7 @@
) %>
<% end %>
<%= if Enum.any?(@token_balances, fn {_token_balance, token} -> token.type == "ERC-1155" end) do %>
<%= if Enum.any?(@token_balances, fn token_balance -> token_balance.token.type == "ERC-1155" end) do %>
<%= render(
"_tokens.html",
conn: @conn,
@ -65,7 +65,7 @@
) %>
<% end %>
<%= if Enum.any?(@token_balances, fn {_token_balance, token} -> token.type == "ERC-20" end) do %>
<%= if Enum.any?(@token_balances, fn token_balance -> token_balance.token.type == "ERC-20" end) do %>
<%= render(
"_tokens.html",
conn: @conn,

@ -3,17 +3,17 @@
<%= @type %> (<span data-number-of-tokens-by-type="<%= @type %>"><%= Enum.count(@token_balances)%></span>)
</h6>
<%= for {token_balance, token} <- @token_balances do %>
<%= for token_balance <- @token_balances do %>
<div
class="border-bottom"
data-dropdown-token-balance-test
data-token-name="<%= token_name(token) %>"
data-token-symbol="<%= token_symbol(token) %>"
data-token-name="<%= token_name(token_balance.token) %>"
data-token-symbol="<%= token_symbol(token_balance.token) %>"
>
<% path = cond do
token_balance.token_type == "ERC-721" && !is_nil(token_balance.token_id) -> token_instance_path(@conn, :show, token.contract_address_hash, to_string(token_balance.token_id))
token_balance.token_type == "ERC-1155" && !is_nil(token_balance.token_id) -> token_instance_path(@conn, :show, token.contract_address_hash, to_string(token_balance.token_id))
true -> token_path(@conn, :show, to_string(token.contract_address_hash))
token_balance.token_type == "ERC-721" && !is_nil(token_balance.token_id) -> token_instance_path(@conn, :show, token_balance.token.contract_address_hash, to_string(token_balance.token_id))
token_balance.token_type == "ERC-1155" && !is_nil(token_balance.token_id) -> token_instance_path(@conn, :show, token_balance.token.contract_address_hash, to_string(token_balance.token_id))
true -> token_path(@conn, :show, to_string(token_balance.token.contract_address_hash))
end
%>
<%= link(
@ -23,7 +23,7 @@
<div class="row dropdown-row wh-sp">
<%= if Application.get_env(:block_scout_web, :display_token_icons) do %>
<% chain_id_for_token_icon = Application.get_env(:block_scout_web, :chain_id) %>
<% address_hash = token.contract_address_hash %>
<% address_hash = token_balance.token.contract_address_hash %>
<%=
render BlockScoutWeb.TokensView,
"_token_icon.html",
@ -32,18 +32,18 @@
additional_classes: ["token-wallet-icon"]
%>
<% end %>
<p class="mb-0 col-md-6 pl-0 pr-0 el-1 flex-grow-2 <%= if !Application.get_env(:block_scout_web, :display_token_icons), do: "ml-5px" %>"><%= token_name(token) %>
<p class="mb-0 col-md-6 pl-0 pr-0 el-1 flex-grow-2 <%= if !Application.get_env(:block_scout_web, :display_token_icons), do: "ml-5px" %>"><%= token_name(token_balance.token) %>
</p>
<%= if token.fiat_value && token.decimals do %>
<%= if token_balance.token.fiat_value && token_balance.token.decimals do %>
<p class="mb-0 col-md-6 text-right usd-total">
<span data-selector="token-balance-usd" data-usd-value="<%= Chain.balance_in_fiat(token_balance, token) %>"></span>
<span data-selector="token-balance-usd" data-usd-value="<%= Chain.balance_in_fiat(token_balance) %>"></span>
</p>
<% end %>
</div>
<div class="row dropdown-row wh-sp">
<%= if token.fiat_value do %>
<%= if token_balance.token.fiat_value do %>
<p class="mb-0 text-right text-muted usd-rate">
<span data-selector="token-price" data-token-usd-value="<%= token.fiat_value %>"></span>
<span data-selector="token-price" data-token-usd-value="<%= token_balance.token.fiat_value %>"></span>
</p>
<% end %>
</div>
@ -52,7 +52,7 @@
<%= if token_balance.token_type == "ERC-721" && !is_nil(token_balance.token_id) do %>
1
<% else %>
<%= format_according_to_decimals(token_balance.value, token.decimals) %> <%= token_symbol(token) %>
<%= format_according_to_decimals(token_balance.value, token_balance.token.decimals) %> <%= token_symbol(token_balance.token) %>
<% end %>
<%= if (token_balance.token_type == "ERC-721" && !is_nil(token_balance.token_id)) or token_balance.token_type == "ERC-1155" do %>
<%= " TokenID " <> to_string(token_balance.token_id) %>

@ -11,7 +11,7 @@ defmodule BlockScoutWeb.AddressTokenBalanceView do
end
def filter_by_type(token_balances, type) do
Enum.filter(token_balances, fn {_token_balance, token} -> token.type == type end)
Enum.filter(token_balances, fn token_balance -> token_balance.token.type == type end)
end
def address_tokens_usd_sum_cache(address, token_balances) do

@ -111,17 +111,17 @@ defmodule BlockScoutWeb.API.V2.AddressView do
})
end
def prepare_token_balance({token_balance, token}, fetch_token_instance? \\ false) do
def prepare_token_balance(token_balance, fetch_token_instance? \\ false) do
%{
"value" => token_balance.value,
"token" => TokenView.render("token.json", %{token: token}),
"token" => TokenView.render("token.json", %{token: token_balance.token}),
"token_id" => token_balance.token_id,
"token_instance" =>
if(fetch_token_instance? && token_balance.token_id,
do:
fetch_and_render_token_instance(
token_balance.token_id,
token,
token_balance.token,
token_balance.address_hash
)
)

@ -2690,24 +2690,8 @@ defmodule Explorer.Chain do
@doc """
Return the balance in usd corresponding to this token. Return nil if the fiat_value of the token is not present.
"""
def balance_in_fiat(_token_balance, %{fiat_value: fiat_value, decimals: decimals})
when nil in [fiat_value, decimals] do
nil
end
def balance_in_fiat(token_balance, %{fiat_value: fiat_value, decimals: decimals}) do
tokens = CurrencyHelper.divide_decimals(token_balance.value, decimals)
Decimal.mult(tokens, fiat_value)
end
def balance_in_fiat(%{token: %{fiat_value: nil}}) do
nil
end
def balance_in_fiat(token_balance) do
tokens = CurrencyHelper.divide_decimals(token_balance.value, token_balance.token.decimals)
price = token_balance.token.fiat_value
Decimal.mult(tokens, price)
token_balance.fiat_value
end
defp contract?(%{contract_code: nil}), do: false

@ -47,6 +47,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
field(:value_fetched_at, :utc_datetime_usec)
field(:token_id, :decimal)
field(:token_type, :string)
field(:fiat_value, :decimal, virtual: true)
# A transient field for deriving token holder count deltas during address_current_token_balances upserts
field(:old_value, :decimal)
@ -170,9 +171,11 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
where: ctb.address_hash == ^address_hash,
where: ctb.value > 0,
where: ctb.token_type == ^type,
left_join: t in Token,
left_join: t in assoc(ctb, :token),
on: ctb.token_contract_address_hash == t.contract_address_hash,
select: {ctb, t},
preload: [token: t],
select: ctb,
select_merge: ^%{fiat_value: fiat_balance},
order_by: ^[desc_nulls_last: fiat_balance],
order_by: [desc: ctb.value, desc: ctb.id]
)
@ -185,9 +188,11 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
ctb in __MODULE__,
where: ctb.address_hash == ^address_hash,
where: ctb.value > 0,
left_join: t in Token,
left_join: t in assoc(ctb, :token),
on: ctb.token_contract_address_hash == t.contract_address_hash,
select: {ctb, t},
preload: [token: t],
select: ctb,
select_merge: ^%{fiat_value: fiat_balance},
order_by: ^[desc_nulls_last: fiat_balance],
order_by: [desc: ctb.value, desc: ctb.id]
)

@ -53,9 +53,9 @@ defmodule Explorer.Counters.AddressTokenUsdSum do
@spec address_tokens_fiat_sum([{Address.CurrentTokenBalance, Explorer.Chain.Token}]) :: Decimal.t()
defp address_tokens_fiat_sum(token_balances) do
token_balances
|> Enum.reduce(Decimal.new(0), fn {token_balance, token}, acc ->
if token_balance.value && token.fiat_value && token.decimals do
Decimal.add(acc, Chain.balance_in_fiat(token_balance, token))
|> Enum.reduce(Decimal.new(0), fn token_balance, acc ->
if token_balance.value && token_balance.token.fiat_value && token_balance.token.decimals do
Decimal.add(acc, Chain.balance_in_fiat(token_balance))
else
acc
end

@ -49,7 +49,7 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do
when not is_nil(address_hash) do
stale_current_token_balances =
current_token_balances
|> Enum.filter(fn {current_token_balance, _} -> current_token_balance.block_number < stale_balance_window end)
|> Enum.filter(fn current_token_balance -> current_token_balance.block_number < stale_balance_window end)
if Enum.count(stale_current_token_balances) > 0 do
fetch_and_update(latest_block_number, address_hash, stale_current_token_balances)
@ -63,10 +63,11 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do
defp fetch_and_update(block_number, address_hash, stale_current_token_balances) do
current_token_balances_update_params =
stale_current_token_balances
|> Enum.map(fn {%{token_id: token_id} = stale_current_token_balance, token} ->
|> Enum.map(fn %{token_id: token_id} = stale_current_token_balance ->
stale_current_token_balances_to_fetch = [
%{
token_contract_address_hash: "0x" <> Base.encode16(token.contract_address_hash.bytes),
token_contract_address_hash:
"0x" <> Base.encode16(stale_current_token_balance.token.contract_address_hash.bytes),
address_hash: "0x" <> Base.encode16(address_hash.bytes),
block_number: block_number,
token_id: token_id && Decimal.to_integer(token_id)
@ -84,8 +85,8 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do
if updated_balance do
%{}
|> Map.put(:address_hash, stale_current_token_balance.address_hash)
|> Map.put(:token_contract_address_hash, token.contract_address_hash)
|> Map.put(:token_type, token.type)
|> Map.put(:token_contract_address_hash, stale_current_token_balance.token.contract_address_hash)
|> Map.put(:token_type, stale_current_token_balance.token.type)
|> Map.put(:token_id, token_id)
|> Map.put(:block_number, block_number)
|> Map.put(:value, Decimal.new(updated_balance))

Loading…
Cancel
Save