Merge pull request #4793 from blockscout/vb-crc-total-balance-async

Get CRC total balance in non-blocking address page manner
pull/4796/head
Victor Baranov 3 years ago committed by GitHub
commit 59c5be076a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 2
      apps/block_scout_web/assets/css/components/_navbar.scss
  3. 19
      apps/block_scout_web/assets/js/pages/address.js
  4. 68
      apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex
  5. 35
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex
  6. 11
      apps/block_scout_web/lib/block_scout_web/templates/address_token/_tokens.html.eex
  7. 9
      apps/block_scout_web/lib/block_scout_web/templates/address_token/overview.html.eex
  8. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_token/overview_item.html.eex
  9. 3
      apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex
  10. 10
      apps/block_scout_web/lib/block_scout_web/templates/bridged_tokens/_tile.html.eex
  11. 2
      apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex
  12. 11
      apps/block_scout_web/lib/block_scout_web/templates/search/_tile.html.eex
  13. 10
      apps/block_scout_web/lib/block_scout_web/templates/tokens/_tile.html.eex
  14. 2
      apps/block_scout_web/lib/block_scout_web/templates/tokens/_token_icon.html.eex
  15. 12
      apps/block_scout_web/priv/gettext/default.pot
  16. 12
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  17. 8
      apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs
  18. 8
      apps/explorer/lib/explorer/chain.ex

@ -7,7 +7,7 @@
- [#4739](https://github.com/blockscout/blockscout/pull/4739) - Improve logs and inputs decoding
- [#4747](https://github.com/blockscout/blockscout/pull/4747) - Advanced CSV export
- [#4745](https://github.com/blockscout/blockscout/pull/4745) - Vyper contracts verification
- [#4699](https://github.com/blockscout/blockscout/pull/4699) - Address page facelifting
- [#4699](https://github.com/blockscout/blockscout/pull/4699), [#4793](https://github.com/blockscout/blockscout/pull/4793) - Address page facelifting
- [#4667](https://github.com/blockscout/blockscout/pull/4667) - Transaction Page: Add expand/collapse button for long contract method data
- [#4641](https://github.com/blockscout/blockscout/pull/4641), [#4733](https://github.com/blockscout/blockscout/pull/4733) - Improve Read Contract page logic
- [#4660](https://github.com/blockscout/blockscout/pull/4660) - Save Sourcify path instead of filename

@ -253,7 +253,7 @@ $navbar-logo-width: auto !default;
margin-left: 5px;
line-height: 28px;
&.footer {
&.in-footer {
color: #fff;
}
}

@ -49,6 +49,7 @@ export function reducer (state = initialState, action) {
tokenTransferCount: action.tokenTransferCount,
gasUsageCount: action.gasUsageCount,
validationCount: action.validationCount,
crcTotalWorth: action.crcTotalWorth,
countersFetched: true
})
}
@ -183,6 +184,24 @@ const elements = {
$('.address-validation-count-item').css('display', 'none')
}
}
},
'[data-test="address-tokens-panel-crc-total-worth"]': {
load ($el) {
return { countersFetched: numeral($el.text()).value() }
},
render ($el, state, oldState) {
if (state.countersFetched && state.crcTotalWorth) {
if (oldState.crcTotalWorth === state.crcTotalWorth) return
$el.empty().append(`${state.crcTotalWorth} CRC`)
if (state.crcTotalWorth !== '0') {
$('[data-test="address-tokens-panel-crc-total-worth-container"]').removeClass('d-none')
} else {
$('[data-test="address-tokens-panel-crc-total-worth-container"]').addClass('d-none')
}
} else {
$('[data-test="address-tokens-panel-crc-total-worth-container"]').addClass('d-none')
}
}
}
}

@ -3,9 +3,9 @@ defmodule BlockScoutWeb.AddressController do
import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias BlockScoutWeb.{AccessHelpers, AddressView, Controller}
alias BlockScoutWeb.{AccessHelpers, AddressView, Controller, CurrencyHelpers}
alias Explorer.Counters.{AddressTokenTransfersCounter, AddressTransactionsCounter, AddressTransactionsGasUsageCounter}
alias Explorer.{Chain, Market}
alias Explorer.{Chain, CustomContractsHelpers, Market}
alias Explorer.ExchangeRates.Token
alias Phoenix.View
@ -84,13 +84,15 @@ defmodule BlockScoutWeb.AddressController do
def address_counters(conn, %{"id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
{transaction_count, token_transfer_count, gas_usage_count, validation_count} = address_counters(address)
{transaction_count, token_transfer_count, gas_usage_count, validation_count, crc_total_worth} =
address_counters(address)
json(conn, %{
transaction_count: transaction_count,
token_transfer_count: token_transfer_count,
gas_usage_count: gas_usage_count,
validation_count: validation_count
validation_count: validation_count,
crc_total_worth: crc_total_worth
})
else
_ ->
@ -98,7 +100,8 @@ defmodule BlockScoutWeb.AddressController do
transaction_count: 0,
token_transfer_count: 0,
gas_usage_count: 0,
validation_count: 0
validation_count: 0,
crc_total_worth: 0
})
end
end
@ -124,7 +127,18 @@ defmodule BlockScoutWeb.AddressController do
validation_count(address)
end)
[transaction_count_task, token_transfer_count_task, gas_usage_count_task, validation_count_task]
crc_total_worth_task =
Task.async(fn ->
crc_total_worth(address)
end)
[
transaction_count_task,
token_transfer_count_task,
gas_usage_count_task,
validation_count_task,
crc_total_worth_task
]
|> Task.yield_many(:timer.seconds(60))
|> Enum.map(fn {_task, res} ->
case res do
@ -156,4 +170,46 @@ defmodule BlockScoutWeb.AddressController do
defp validation_count(address) do
Chain.address_to_validation_count(address.hash)
end
defp crc_total_worth(address) do
circles_total_balance(address.hash)
end
defp circles_total_balance(address_hash) do
circles_addresses_list = CustomContractsHelpers.get_custom_addresses_list(:circles_addresses)
token_balances =
address_hash
|> Chain.fetch_last_token_balances()
token_balances_except_bridged =
token_balances
|> Enum.filter(fn {_, _, token} -> !token.bridged end)
circles_total_balance_raw =
if Enum.count(circles_addresses_list) > 0 do
token_balances_except_bridged
|> Enum.reduce(Decimal.new(0), fn {token_balance, _, token}, acc_balance ->
{:ok, token_address} = Chain.hash_to_address(token.contract_address_hash)
from_address = AddressView.from_address_hash(token_address)
created_from_address_hash =
if from_address,
do: "0x" <> Base.encode16(from_address.bytes, case: :lower),
else: nil
if Enum.member?(circles_addresses_list, created_from_address_hash) && token.name == "Circles" &&
token.symbol == "CRC" do
Decimal.add(acc_balance, token_balance.value)
else
acc_balance
end
end)
else
Decimal.new(0)
end
CurrencyHelpers.format_according_to_decimals(circles_total_balance_raw, Decimal.new(18))
end
end

@ -1,9 +1,8 @@
defmodule BlockScoutWeb.AddressTokenBalanceController do
use BlockScoutWeb, :controller
import BlockScoutWeb.AddressView, only: [from_address_hash: 1]
alias BlockScoutWeb.AccessHelpers
alias Explorer.{Chain, CustomContractsHelpers, Market}
alias Explorer.{Chain, Market}
alias Explorer.Chain.Address
alias Indexer.Fetcher.TokenBalanceOnDemand
@ -18,40 +17,10 @@ defmodule BlockScoutWeb.AddressTokenBalanceController do
TokenBalanceOnDemand.trigger_fetch(address_hash, token_balances)
end)
circles_addresses_list = CustomContractsHelpers.get_custom_addresses_list(:circles_addresses)
token_balances_with_price =
token_balances
|> Market.add_price()
token_balances_except_bridged =
token_balances
|> Enum.filter(fn {token_balance, _, _} -> !token_balance.token.bridged end)
circles_total_balance =
if Enum.count(circles_addresses_list) > 0 do
token_balances_except_bridged
|> Enum.reduce(Decimal.new(0), fn {token_balance, _, _}, acc_balance ->
{:ok, token_address} = Chain.hash_to_address(token_balance.address_hash)
from_address = from_address_hash(token_address)
created_from_address_hash =
if from_address,
do: "0x" <> Base.encode16(from_address.bytes, case: :lower),
else: nil
if Enum.member?(circles_addresses_list, created_from_address_hash) && token_balance.token.name == "Circles" &&
token_balance.token.symbol == "CRC" do
Decimal.add(acc_balance, token_balance.value)
else
acc_balance
end
end)
else
Decimal.new(0)
end
case AccessHelpers.restricted_access?(address_hash_string, params) do
{:ok, false} ->
conn
@ -60,7 +29,6 @@ defmodule BlockScoutWeb.AddressTokenBalanceController do
|> render("_token_balances.html",
address_hash: Address.checksum(address_hash),
token_balances: token_balances_with_price,
circles_total_balance: circles_total_balance,
conn: conn
)
@ -71,7 +39,6 @@ defmodule BlockScoutWeb.AddressTokenBalanceController do
|> render("_token_balances.html",
address_hash: Address.checksum(address_hash),
token_balances: [],
circles_total_balance: Decimal.new(0),
conn: conn
)
end

@ -11,11 +11,12 @@
<%= if System.get_env("DISPLAY_TOKEN_ICONS") === "true" do %>
<% chain_id_for_token_icon = if @bridged_token && @bridged_token.foreign_chain_id, do: @bridged_token.foreign_chain_id |> Decimal.to_integer() |> to_string(), else: System.get_env("CHAIN_ID") %>
<% address_hash = if @bridged_token && @bridged_token.foreign_token_contract_address_hash, do: @bridged_token.foreign_token_contract_address_hash, else: @token.contract_address_hash %>
<% token_icon_url = Chain.get_token_icon_url_by(chain_id_for_token_icon, Address.checksum(address_hash)) %>
<%= if token_icon_url do %>
<img heigth=15 width=15 src="<%= token_icon_url %>" style="margin-top: -2px;"/>
<% end %>
<%=
render BlockScoutWeb.TokensView,
"_token_icon.html",
chain_id: chain_id_for_token_icon,
address: Address.checksum(address_hash)
%>
<% end %>
</span>
<%= link(

@ -61,4 +61,13 @@
data_test: "address-tokens-panel-tokens-worth",
classes: ["fs-14"]
%>
<%= render BlockScoutWeb.AddressTokenView, "overview_item.html",
title: gettext("CRC Worth"),
tooltip: gettext("Shows the total CRC balance in the address."),
value: "0 CRC",
data_test: "address-tokens-panel-crc-total-worth",
data_test_container: "address-tokens-panel-crc-total-worth-container",
classes: ["fs-14"],
container_classes: ["d-none"]
%>
</div>

@ -1,4 +1,4 @@
<div class="d-flex mr-4" style="padding: 10px;">
<div class="mr-4 <%= if assigns[:container_classes] do @container_classes |> Enum.join(" ") end %>" data-test="<%= if assigns[:data_test_container], do: @data_test_container %>" style="padding: 10px;">
<div>
<div class="d-flex">
<h1 class="card-title mb-2"><%= @title %></h1>

@ -1,7 +1,4 @@
<div class="d-flex">
<%= if Decimal.cmp(@circles_total_balance, 0) == :gt do %>
<p class="address-current-balance"><%= format_according_to_decimals(@circles_total_balance, Decimal.new(18)) %> CRC</p>
<% end %>
<%= if Enum.any?(@token_balances) do %>
<a
href="#"

@ -15,10 +15,12 @@
<% chain_id_for_token_icon = if foreign_chain_id, do: foreign_chain_id |> Decimal.to_integer() |> to_string(), else: System.get_env("CHAIN_ID") %>
<% foreign_token_contract_address_hash = if Map.has_key?(@bridged_token, :foreign_token_contract_address_hash), do: Address.checksum(@bridged_token.foreign_token_contract_address_hash), else: nil %>
<% token_hash_for_token_icon = if foreign_token_contract_address_hash, do: foreign_token_contract_address_hash, else: Address.checksum(@token.contract_address_hash) %>
<% token_icon_url = Explorer.Chain.get_token_icon_url_by(chain_id_for_token_icon, token_hash_for_token_icon) %>
<%= if token_icon_url do %>
<img heigth=15 width=15 src="<%= token_icon_url %>" style="margin-top: -3px;" class="mr-1"/>
<% end %>
<%=
render BlockScoutWeb.TokensView,
"_token_icon.html",
chain_id: chain_id_for_token_icon,
address: token_hash_for_token_icon
%>
<% end %>
<%= link(token,
to: token_path(BlockScoutWeb.Endpoint, :show, @token.contract_address_hash),

@ -6,7 +6,7 @@
<%= link to: webapp_url(@conn), class: "footer-brand" do %>
<img class="footer-logo" src="<%= static_path(@conn, logo_footer()) %>" alt="<%= subnetwork_title() %>" />
<%= if logo_text() do %>
<span class="logo-text footer"> <%= logo_text() %> </span>
<span class="logo-text in-footer"> <%= logo_text() %> </span>
<% end %>
<% end %>
</div>

@ -15,11 +15,12 @@
<%= if System.get_env("DISPLAY_TOKEN_ICONS") === "true" do %>
<% chain_id_for_token_icon = if @result.foreign_chain_id, do: @result.foreign_chain_id |> Decimal.to_integer() |> to_string(), else: System.get_env("CHAIN_ID") %>
<% address_hash = if @result.foreign_token_hash, do: @result.foreign_token_hash, else: @result.address_hash %>
<% token_icon_url = Chain.get_token_icon_url_by(chain_id_for_token_icon, address_hash) %>
<%= if token_icon_url do %>
<img heigth=15 width=15 src="<%= token_icon_url %>" style="margin-top: -2px;"/>
<% end %>
<%=
render BlockScoutWeb.TokensView,
"_token_icon.html",
chain_id: chain_id_for_token_icon,
address: address_hash
%>
<% end %>
</span>
<% res = @result.name <> " (" <> @result.symbol <> ")" %>

@ -10,10 +10,12 @@
<% chain_id_for_token_icon = System.get_env("CHAIN_ID") %>
<% foreign_token_contract_address_hash = nil %>
<% token_hash_for_token_icon = if foreign_token_contract_address_hash, do: foreign_token_contract_address_hash, else: Address.checksum(@token.contract_address_hash) %>
<% token_icon_url = Explorer.Chain.get_token_icon_url_by(chain_id_for_token_icon, token_hash_for_token_icon) %>
<%= if token_icon_url do %>
<img heigth=15 width=15 src="<%= token_icon_url %>" style="margin-top: -3px;"/>
<% end %>
<%=
render BlockScoutWeb.TokensView,
"_token_icon.html",
chain_id: chain_id_for_token_icon,
address: token_hash_for_token_icon
%>
<% end %>
<% token = token_display_name(@token) %>
<%= link(token,

@ -0,0 +1,2 @@
<% token_icon_url = Explorer.Chain.get_token_icon_url_by(@chain_id, @address) %>
<img heigth=15 width=15 src="<%= token_icon_url %>" style="margin-top: -2px;" onerror="this.style.visibility = 'hidden'"/>

@ -1956,7 +1956,7 @@ msgid "Search network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:50
#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:47
msgid "Search tokens"
msgstr ""
@ -3206,3 +3206,13 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10
msgid "Error"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/overview.html.eex:65
msgid "CRC Worth"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/overview.html.eex:66
msgid "Shows the total CRC balance in the address."
msgstr ""

@ -1956,7 +1956,7 @@ msgid "Search network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:50
#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:47
msgid "Search tokens"
msgstr ""
@ -3206,3 +3206,13 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10
msgid "Error"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/overview.html.eex:65
msgid "CRC Worth"
msgstr ""
#, elixir-format, fuzzy
#: lib/block_scout_web/templates/address_token/overview.html.eex:66
msgid "Shows the total CRC balance in the address."
msgstr ""

@ -85,7 +85,13 @@ defmodule BlockScoutWeb.AddressControllerTest do
assert conn.status == 200
{:ok, response} = Jason.decode(conn.resp_body)
assert %{"transaction_count" => 0, "token_transfer_count" => 0, "validation_count" => 0, "gas_usage_count" => 0} ==
assert %{
"transaction_count" => 0,
"token_transfer_count" => 0,
"validation_count" => 0,
"gas_usage_count" => 0,
"crc_total_worth" => "0"
} ==
response
end
end

@ -7152,13 +7152,7 @@ defmodule Explorer.Chain do
try_url =
"https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/#{chain_name}/assets/#{address_hash}/logo.png"
%HTTPoison.Response{status_code: status_code} = HTTPoison.get!(try_url)
if status_code == 200 do
try_url
else
nil
end
try_url
else
nil
end

Loading…
Cancel
Save