Merge pull request #8177 from blockscout/np-address-counters

Refactor address counter functions
np-add-tab-counters-endpoint
Victor Baranov 1 year ago committed by GitHub
commit 7aa5e96524
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      .github/workflows/config.yml
  2. 1
      CHANGELOG.md
  3. 5
      apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex
  4. 3
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex
  5. 3
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex
  6. 3
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  7. 3
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  8. 10
      apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex
  9. 1
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  10. 11
      apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex
  11. 36
      apps/block_scout_web/priv/gettext/default.pot
  12. 36
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  13. 7
      apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs
  14. 280
      apps/explorer/lib/explorer/chain.ex
  15. 290
      apps/explorer/lib/explorer/chain/address/counters.ex
  16. 5
      apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex
  17. 5
      apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex
  18. 5
      apps/explorer/lib/explorer/counters/address_transactions_counter.ex
  19. 4
      apps/explorer/lib/explorer/counters/addresses_counter.ex
  20. 4
      apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex
  21. 15
      apps/explorer/test/explorer/chain_test.exs

@ -48,7 +48,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-
@ -106,7 +106,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -130,7 +130,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -153,7 +153,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -162,7 +162,7 @@ jobs:
id: dialyzer-cache
with:
path: priv/plts
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-"
@ -193,7 +193,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -219,7 +219,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -248,7 +248,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -296,7 +296,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -342,7 +342,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -399,7 +399,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -453,7 +453,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -518,7 +518,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -582,7 +582,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_21-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"

@ -23,6 +23,7 @@
### Chore
- [#8177](https://github.com/blockscout/blockscout/pull/8177) - Refactor address counter functions
- [#8183](https://github.com/blockscout/blockscout/pull/8183) - Update frontend envs in order to pass their validation
- [#8167](https://github.com/blockscout/blockscout/pull/8167) - Manage concurrency for Token and TokenBalance fetcher
- [#8179](https://github.com/blockscout/blockscout/pull/8179) - Enhance nginx config

@ -15,6 +15,7 @@ defmodule BlockScoutWeb.AddressController do
}
alias Explorer.{Chain, Market}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.Wei
alias Indexer.Fetcher.CoinBalanceOnDemand
alias Phoenix.View
@ -82,7 +83,7 @@ defmodule BlockScoutWeb.AddressController do
render(conn, "index.html",
current_path: Controller.current_full_path(conn),
address_count: Chain.address_estimated_count(),
address_count: Counters.address_estimated_count(),
total_supply: total_supply
)
end
@ -146,7 +147,7 @@ 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
{validation_count} = Chain.address_counters(address)
{validation_count} = Counters.address_counters(address)
transactions_from_db = address.transactions_count || 0
token_transfers_from_db = address.token_transfers_count || 0

@ -17,6 +17,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
alias BlockScoutWeb.AccessHelper
alias BlockScoutWeb.API.V2.{BlockView, TransactionView, WithdrawalView}
alias Explorer.{Chain, Market}
alias Explorer.Chain.Address.Counters
alias Indexer.Fetcher.{CoinBalanceOnDemand, TokenBalanceOnDemand}
@transaction_necessity_by_association [
@ -73,7 +74,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params),
{:not_found, {:ok, address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do
{validation_count} = Chain.address_counters(address, @api_true)
{validation_count} = Counters.address_counters(address, @api_true)
transactions_from_db = address.transactions_count || 0
token_transfers_from_db = address.token_transfers_count || 0

@ -4,6 +4,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do
alias BlockScoutWeb.API.V2.Helper
alias BlockScoutWeb.Chain.MarketHistoryChartController
alias Explorer.{Chain, Market}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.Cache.Block, as: BlockCache
alias Explorer.Chain.Cache.{GasPriceOracle, GasUsage}
alias Explorer.Chain.Cache.Transaction, as: TransactionCache
@ -43,7 +44,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do
conn,
%{
"total_blocks" => BlockCache.estimated_count() |> to_string(),
"total_addresses" => @api_true |> Chain.address_estimated_count() |> to_string(),
"total_addresses" => @api_true |> Counters.address_estimated_count() |> to_string(),
"total_transactions" => TransactionCache.estimated_count() |> to_string(),
"average_block_time" => AverageBlockTime.average_block_time() |> Duration.to_milliseconds(),
"coin_price" => exchange_rate.usd_value,

@ -6,6 +6,7 @@ defmodule BlockScoutWeb.ChainController do
alias BlockScoutWeb.API.V2.Helper
alias BlockScoutWeb.{ChainView, Controller}
alias Explorer.{Chain, PagingOptions, Repo}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.{Address, Block, Transaction}
alias Explorer.Chain.Cache.Block, as: BlockCache
alias Explorer.Chain.Cache.GasUsage
@ -19,7 +20,7 @@ defmodule BlockScoutWeb.ChainController do
transaction_estimated_count = TransactionCache.estimated_count()
total_gas_usage = GasUsage.total()
block_count = BlockCache.estimated_count()
address_count = Chain.address_estimated_count()
address_count = Counters.address_estimated_count()
market_cap_calculation =
case Application.get_env(:explorer, :supply) do

@ -17,6 +17,7 @@ defmodule BlockScoutWeb.Notifier do
}
alias Explorer.{Chain, Market, Repo}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.{Address, InternalTransaction, Transaction}
alias Explorer.Chain.Supply.RSK
alias Explorer.Chain.Transaction.History.TransactionStats
@ -27,7 +28,7 @@ defmodule BlockScoutWeb.Notifier do
@check_broadcast_sequence_period 500
def handle_event({:chain_event, :addresses, type, addresses}) when type in [:realtime, :on_demand] do
Endpoint.broadcast("addresses:new_address", "count", %{count: Chain.address_estimated_count()})
Endpoint.broadcast("addresses:new_address", "count", %{count: Counters.address_estimated_count()})
addresses
|> Stream.reject(fn %Address{fetched_coin_balance: fetched_coin_balance} -> is_nil(fetched_coin_balance) end)

@ -8,7 +8,7 @@
class: "card-tab #{tab_status("transactions", @conn.request_path)}",
to: AccessHelper.get_path(@conn, :address_transaction_path, :index, @address.hash)
) %>
<%= if Chain.check_if_token_transfers_at_address(@address.hash) do %>
<%= if Counters.check_if_token_transfers_at_address(@address.hash) do %>
<%= link(
gettext("Token Transfers"),
class: "card-tab #{tab_status("token-transfers", @conn.request_path)}",
@ -16,7 +16,7 @@
to: AccessHelper.get_path(@conn, :address_token_transfers_path, :index, @address.hash)
) %>
<% end %>
<%= if Chain.check_if_tokens_at_address(@address.hash) do %>
<%= if Counters.check_if_tokens_at_address(@address.hash) do %>
<%= link(
gettext("Tokens"),
class: "card-tab #{tab_status("tokens", @conn.request_path)}",
@ -24,7 +24,7 @@
"data-test": "tokens_tab_link"
) %>
<% end %>
<%= if Chain.check_if_withdrawals_at_address(@address.hash) do %>
<%= if Counters.check_if_withdrawals_at_address(@address.hash) do %>
<%= link(
gettext("Withdrawals"),
class: "card-tab #{tab_status("withdrawals", @conn.request_path)}",
@ -44,14 +44,14 @@
"data-test": "coin_balance_tab_link",
to: AccessHelper.get_path(@conn, :address_coin_balance_path, :index, @address.hash)
) %>
<%= if Chain.check_if_logs_at_address(@address.hash) do %>
<%= if Counters.check_if_logs_at_address(@address.hash) do %>
<%= link(
gettext("Logs"),
class: "card-tab #{tab_status("logs", @conn.request_path)}",
to: AccessHelper.get_path(@conn, :address_logs_path, :index, @address.hash)
) %>
<% end %>
<%= if Chain.check_if_validated_blocks_at_address(@address.hash) do %>
<%= if Counters.check_if_validated_blocks_at_address(@address.hash) do %>
<%= link(
gettext("Blocks Validated"),
class: "card-tab #{tab_status("validations", @conn.request_path)}",

@ -6,6 +6,7 @@ defmodule BlockScoutWeb.AddressView do
alias BlockScoutWeb.{AccessHelper, LayoutView}
alias Explorer.Account.CustomABI
alias Explorer.{Chain, CustomContractsHelper, Repo}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.{Address, Hash, InternalTransaction, Log, SmartContract, Token, TokenTransfer, Transaction, Wei}
alias Explorer.Chain.Block.Reward
alias Explorer.ExchangeRates.Token, as: TokenExchangeRate

@ -7,6 +7,7 @@ defmodule BlockScoutWeb.API.V2.AddressView do
alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenView}
alias BlockScoutWeb.API.V2.Helper
alias Explorer.{Chain, Market}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.{Address, SmartContract}
@api_true [api?: true]
@ -102,12 +103,12 @@ defmodule BlockScoutWeb.API.V2.AddressView do
"has_methods_read_proxy" => is_proxy,
"has_methods_write_proxy" => AddressView.smart_contract_with_write_functions?(address) && is_proxy,
"has_decompiled_code" => AddressView.has_decompiled_code?(address),
"has_validated_blocks" => Chain.check_if_validated_blocks_at_address(address.hash, @api_true),
"has_logs" => Chain.check_if_logs_at_address(address.hash, @api_true),
"has_tokens" => Chain.check_if_tokens_at_address(address.hash, @api_true),
"has_token_transfers" => Chain.check_if_token_transfers_at_address(address.hash, @api_true),
"has_validated_blocks" => Counters.check_if_validated_blocks_at_address(address.hash, @api_true),
"has_logs" => Counters.check_if_logs_at_address(address.hash, @api_true),
"has_tokens" => Counters.check_if_tokens_at_address(address.hash, @api_true),
"has_token_transfers" => Counters.check_if_token_transfers_at_address(address.hash, @api_true),
"watchlist_address_id" => Chain.select_watchlist_address_id(get_watchlist_id(conn), address.hash),
"has_beacon_chain_withdrawals" => Chain.check_if_withdrawals_at_address(address.hash, @api_true)
"has_beacon_chain_withdrawals" => Counters.check_if_withdrawals_at_address(address.hash, @api_true)
})
end

@ -265,7 +265,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20
#: lib/block_scout_web/templates/transaction_state/index.html.eex:34
#: lib/block_scout_web/templates/verified_contracts/index.html.eex:60
#: lib/block_scout_web/views/address_view.ex:107
#: lib/block_scout_web/views/address_view.ex:108
#, elixir-autogen, elixir-format
msgid "Address"
msgstr ""
@ -556,7 +556,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:56
#: lib/block_scout_web/templates/address/overview.html.eex:275
#: lib/block_scout_web/templates/address_validation/index.html.eex:11
#: lib/block_scout_web/views/address_view.ex:384
#: lib/block_scout_web/views/address_view.ex:385
#, elixir-autogen, elixir-format
msgid "Blocks Validated"
msgstr ""
@ -656,13 +656,13 @@ msgstr ""
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149
#: lib/block_scout_web/views/address_view.ex:377
#: lib/block_scout_web/views/address_view.ex:378
#, elixir-autogen, elixir-format
msgid "Code"
msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:42
#: lib/block_scout_web/views/address_view.ex:383
#: lib/block_scout_web/views/address_view.ex:384
#, elixir-autogen, elixir-format
msgid "Coin Balance History"
msgstr ""
@ -771,14 +771,14 @@ msgstr ""
#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:18
#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:29
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex:3
#: lib/block_scout_web/views/address_view.ex:105
#: lib/block_scout_web/views/address_view.ex:106
#, elixir-autogen, elixir-format
msgid "Contract Address"
msgstr ""
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:16
#: lib/block_scout_web/views/address_view.ex:45
#: lib/block_scout_web/views/address_view.ex:79
#: lib/block_scout_web/views/address_view.ex:46
#: lib/block_scout_web/views/address_view.ex:80
#, elixir-autogen, elixir-format
msgid "Contract Address Pending"
msgstr ""
@ -1084,7 +1084,7 @@ msgstr ""
msgid "Decoded"
msgstr ""
#: lib/block_scout_web/views/address_view.ex:378
#: lib/block_scout_web/views/address_view.ex:379
#, elixir-autogen, elixir-format
msgid "Decompiled Code"
msgstr ""
@ -1601,7 +1601,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:17
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6
#: lib/block_scout_web/views/address_view.ex:374
#: lib/block_scout_web/views/address_view.ex:375
#: lib/block_scout_web/views/transaction_view.ex:533
#, elixir-autogen, elixir-format
msgid "Internal Transactions"
@ -1718,7 +1718,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_logs/index.html.eex:10
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8
#: lib/block_scout_web/views/address_view.ex:385
#: lib/block_scout_web/views/address_view.ex:386
#: lib/block_scout_web/views/transaction_view.ex:534
#, elixir-autogen, elixir-format
msgid "Logs"
@ -1732,7 +1732,7 @@ msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:53
#: lib/block_scout_web/templates/layout/app.html.eex:50
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:85
#: lib/block_scout_web/views/address_view.ex:145
#: lib/block_scout_web/views/address_view.ex:146
#, elixir-autogen, elixir-format
msgid "Market Cap"
msgstr ""
@ -2208,7 +2208,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:89
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:27
#: lib/block_scout_web/views/address_view.ex:379
#: lib/block_scout_web/views/address_view.ex:380
#: lib/block_scout_web/views/tokens/overview_view.ex:41
#, elixir-autogen, elixir-format
msgid "Read Contract"
@ -2216,7 +2216,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:96
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:41
#: lib/block_scout_web/views/address_view.ex:380
#: lib/block_scout_web/views/address_view.ex:381
#, elixir-autogen, elixir-format
msgid "Read Proxy"
msgstr ""
@ -2903,7 +2903,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/address_view.ex:376
#: lib/block_scout_web/views/address_view.ex:377
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:114
#: lib/block_scout_web/views/tokens/overview_view.ex:39
#: lib/block_scout_web/views/transaction_view.ex:532
@ -2927,7 +2927,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:13
#: lib/block_scout_web/templates/layout/_topnav.html.eex:84
#: lib/block_scout_web/templates/tokens/index.html.eex:10
#: lib/block_scout_web/views/address_view.ex:373
#: lib/block_scout_web/views/address_view.ex:374
#, elixir-autogen, elixir-format
msgid "Tokens"
msgstr ""
@ -3099,7 +3099,7 @@ msgstr ""
#: lib/block_scout_web/templates/block/overview.html.eex:80
#: lib/block_scout_web/templates/chain/show.html.eex:214
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49
#: lib/block_scout_web/views/address_view.ex:375
#: lib/block_scout_web/views/address_view.ex:376
#, elixir-autogen, elixir-format
msgid "Transactions"
msgstr ""
@ -3469,14 +3469,14 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:103
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:34
#: lib/block_scout_web/views/address_view.ex:381
#: lib/block_scout_web/views/address_view.ex:382
#, elixir-autogen, elixir-format
msgid "Write Contract"
msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:110
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:48
#: lib/block_scout_web/views/address_view.ex:382
#: lib/block_scout_web/views/address_view.ex:383
#, elixir-autogen, elixir-format
msgid "Write Proxy"
msgstr ""

@ -265,7 +265,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20
#: lib/block_scout_web/templates/transaction_state/index.html.eex:34
#: lib/block_scout_web/templates/verified_contracts/index.html.eex:60
#: lib/block_scout_web/views/address_view.ex:107
#: lib/block_scout_web/views/address_view.ex:108
#, elixir-autogen, elixir-format
msgid "Address"
msgstr ""
@ -556,7 +556,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:56
#: lib/block_scout_web/templates/address/overview.html.eex:275
#: lib/block_scout_web/templates/address_validation/index.html.eex:11
#: lib/block_scout_web/views/address_view.ex:384
#: lib/block_scout_web/views/address_view.ex:385
#, elixir-autogen, elixir-format
msgid "Blocks Validated"
msgstr ""
@ -656,13 +656,13 @@ msgstr ""
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149
#: lib/block_scout_web/views/address_view.ex:377
#: lib/block_scout_web/views/address_view.ex:378
#, elixir-autogen, elixir-format
msgid "Code"
msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:42
#: lib/block_scout_web/views/address_view.ex:383
#: lib/block_scout_web/views/address_view.ex:384
#, elixir-autogen, elixir-format
msgid "Coin Balance History"
msgstr ""
@ -771,14 +771,14 @@ msgstr ""
#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:18
#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:29
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex:3
#: lib/block_scout_web/views/address_view.ex:105
#: lib/block_scout_web/views/address_view.ex:106
#, elixir-autogen, elixir-format
msgid "Contract Address"
msgstr ""
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:16
#: lib/block_scout_web/views/address_view.ex:45
#: lib/block_scout_web/views/address_view.ex:79
#: lib/block_scout_web/views/address_view.ex:46
#: lib/block_scout_web/views/address_view.ex:80
#, elixir-autogen, elixir-format
msgid "Contract Address Pending"
msgstr ""
@ -1084,7 +1084,7 @@ msgstr ""
msgid "Decoded"
msgstr ""
#: lib/block_scout_web/views/address_view.ex:378
#: lib/block_scout_web/views/address_view.ex:379
#, elixir-autogen, elixir-format
msgid "Decompiled Code"
msgstr ""
@ -1601,7 +1601,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:17
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6
#: lib/block_scout_web/views/address_view.ex:374
#: lib/block_scout_web/views/address_view.ex:375
#: lib/block_scout_web/views/transaction_view.ex:533
#, elixir-autogen, elixir-format
msgid "Internal Transactions"
@ -1718,7 +1718,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_logs/index.html.eex:10
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8
#: lib/block_scout_web/views/address_view.ex:385
#: lib/block_scout_web/views/address_view.ex:386
#: lib/block_scout_web/views/transaction_view.ex:534
#, elixir-autogen, elixir-format
msgid "Logs"
@ -1732,7 +1732,7 @@ msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:53
#: lib/block_scout_web/templates/layout/app.html.eex:50
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:85
#: lib/block_scout_web/views/address_view.ex:145
#: lib/block_scout_web/views/address_view.ex:146
#, elixir-autogen, elixir-format
msgid "Market Cap"
msgstr ""
@ -2208,7 +2208,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:89
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:27
#: lib/block_scout_web/views/address_view.ex:379
#: lib/block_scout_web/views/address_view.ex:380
#: lib/block_scout_web/views/tokens/overview_view.ex:41
#, elixir-autogen, elixir-format
msgid "Read Contract"
@ -2216,7 +2216,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:96
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:41
#: lib/block_scout_web/views/address_view.ex:380
#: lib/block_scout_web/views/address_view.ex:381
#, elixir-autogen, elixir-format
msgid "Read Proxy"
msgstr ""
@ -2903,7 +2903,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/address_view.ex:376
#: lib/block_scout_web/views/address_view.ex:377
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:114
#: lib/block_scout_web/views/tokens/overview_view.ex:39
#: lib/block_scout_web/views/transaction_view.ex:532
@ -2927,7 +2927,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:13
#: lib/block_scout_web/templates/layout/_topnav.html.eex:84
#: lib/block_scout_web/templates/tokens/index.html.eex:10
#: lib/block_scout_web/views/address_view.ex:373
#: lib/block_scout_web/views/address_view.ex:374
#, elixir-autogen, elixir-format
msgid "Tokens"
msgstr ""
@ -3099,7 +3099,7 @@ msgstr ""
#: lib/block_scout_web/templates/block/overview.html.eex:80
#: lib/block_scout_web/templates/chain/show.html.eex:214
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49
#: lib/block_scout_web/views/address_view.ex:375
#: lib/block_scout_web/views/address_view.ex:376
#, elixir-autogen, elixir-format
msgid "Transactions"
msgstr ""
@ -3469,14 +3469,14 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:103
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:34
#: lib/block_scout_web/views/address_view.ex:381
#: lib/block_scout_web/views/address_view.ex:382
#, elixir-autogen, elixir-format
msgid "Write Contract"
msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:110
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:48
#: lib/block_scout_web/views/address_view.ex:382
#: lib/block_scout_web/views/address_view.ex:383
#, elixir-autogen, elixir-format
msgid "Write Proxy"
msgstr ""

@ -3,6 +3,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
alias BlockScoutWeb.Models.UserFromAuth
alias Explorer.{Chain, Repo}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.{
Address,
@ -159,9 +160,9 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
insert(:block, miner: address)
Chain.transaction_count(address)
Chain.token_transfers_count(address)
Chain.gas_usage_count(address)
Counters.transaction_count(address)
Counters.token_transfers_count(address)
Counters.gas_usage_count(address)
request = get(conn, "/api/v2/addresses/#{address.hash}/counters")

@ -81,20 +81,11 @@ defmodule Explorer.Chain do
}
alias Explorer.Chain.Cache.Block, as: BlockCache
alias Explorer.Chain.Cache.Helper, as: CacheHelper
alias Explorer.Chain.Cache.PendingBlockOperation, as: PendingBlockOperationCache
alias Explorer.Chain.Fetcher.{CheckBytecodeMatchingOnDemand, LookUpSmartContractSourcesOnDemand}
alias Explorer.Chain.Import.Runner
alias Explorer.Chain.InternalTransaction.{CallType, Type}
alias Explorer.Counters.{
AddressesCounter,
AddressesWithBalanceCounter,
AddressTokenTransfersCounter,
AddressTransactionsCounter,
AddressTransactionsGasUsageCounter
}
alias Explorer.Market.MarketHistoryCache
alias Explorer.{PagingOptions, Repo}
alias Explorer.SmartContract.Helper
@ -122,8 +113,6 @@ defmodule Explorer.Chain do
"commit" => "f14fcbc8"
}
@max_incoming_transactions_count 10_000
@revert_msg_prefix_1 "Revert: "
@revert_msg_prefix_2 "revert: "
@revert_msg_prefix_3 "reverted "
@ -179,56 +168,7 @@ defmodule Explorer.Chain do
@typep necessity_by_association_option :: {:necessity_by_association, necessity_by_association}
@typep paging_options :: {:paging_options, PagingOptions.t()}
@typep balance_by_day :: %{date: String.t(), value: Wei.t()}
@typep api? :: {:api?, true | false}
@doc """
Gets from the cache the count of `t:Explorer.Chain.Address.t/0`'s where the `fetched_coin_balance` is > 0
"""
@spec count_addresses_with_balance_from_cache :: non_neg_integer()
def count_addresses_with_balance_from_cache do
AddressesWithBalanceCounter.fetch()
end
@doc """
Estimated count of `t:Explorer.Chain.Address.t/0`.
Estimated count of addresses.
"""
@spec address_estimated_count() :: non_neg_integer()
def address_estimated_count(options \\ []) do
cached_value = AddressesCounter.fetch()
if is_nil(cached_value) || cached_value == 0 do
count = CacheHelper.estimated_count_from("addresses", options)
max(count, 0)
else
cached_value
end
end
@doc """
Counts the number of addresses with fetched coin balance > 0.
This function should be used with caution. In larger databases, it may take a
while to have the return back.
"""
def count_addresses_with_balance do
Repo.one(
Address.count_with_fetched_coin_balance(),
timeout: :infinity
)
end
@doc """
Counts the number of all addresses.
This function should be used with caution. In larger databases, it may take a
while to have the return back.
"""
def count_addresses do
Repo.aggregate(Address, :count, timeout: :infinity)
end
@type api? :: {:api?, true | false}
@doc """
`t:Explorer.Chain.InternalTransaction/0`s from the address with the given `hash`.
@ -327,25 +267,6 @@ defmodule Explorer.Chain do
)
end
@doc """
Get the total number of transactions sent by the address with the given hash according to the last block indexed.
We have to increment +1 in the last nonce result because it works like an array position, the first
nonce has the value 0. When last nonce is nil, it considers that the given address has 0 transactions.
"""
@spec total_transactions_sent_by_address(Hash.Address.t()) :: non_neg_integer()
def total_transactions_sent_by_address(address_hash) do
last_nonce =
address_hash
|> Transaction.last_nonce_by_address_query()
|> Repo.one(timeout: :infinity)
case last_nonce do
nil -> 0
value -> value + 1
end
end
@doc """
Fetches the transactions related to the address with the given hash, including
transactions that only have the address in the `token_transfers` related table
@ -1028,53 +949,6 @@ defmodule Explorer.Chain do
|> select_repo(options).exists?()
end
@spec address_to_incoming_transaction_count(Hash.Address.t()) :: non_neg_integer()
def address_to_incoming_transaction_count(address_hash) do
to_address_query =
from(
transaction in Transaction,
where: transaction.to_address_hash == ^address_hash
)
Repo.aggregate(to_address_query, :count, :hash, timeout: :infinity)
end
@spec address_hash_to_transaction_count(Hash.Address.t()) :: non_neg_integer()
def address_hash_to_transaction_count(address_hash) do
query =
from(
transaction in Transaction,
where: transaction.to_address_hash == ^address_hash or transaction.from_address_hash == ^address_hash
)
Repo.aggregate(query, :count, :hash, timeout: :infinity)
end
@spec address_to_incoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil
def address_to_incoming_transaction_gas_usage(address_hash) do
to_address_query =
from(
transaction in Transaction,
where: transaction.to_address_hash == ^address_hash
)
Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity)
end
@spec address_to_outcoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil
def address_to_outcoming_transaction_gas_usage(address_hash) do
to_address_query =
from(
transaction in Transaction,
where: transaction.from_address_hash == ^address_hash
)
Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity)
end
@spec max_incoming_transactions_count() :: non_neg_integer()
def max_incoming_transactions_count, do: @max_incoming_transactions_count
@doc """
How many blocks have confirmed `block` based on the current `max_block_number`
@ -2613,56 +2487,6 @@ defmodule Explorer.Chain do
|> select_repo(options).all()
end
def check_if_validated_blocks_at_address(address_hash, options \\ []) do
select_repo(options).exists?(from(b in Block, where: b.miner_hash == ^address_hash))
end
def check_if_logs_at_address(address_hash, options \\ []) do
select_repo(options).exists?(from(l in Log, where: l.address_hash == ^address_hash))
end
def check_if_internal_transactions_at_address(address_hash) do
internal_transactions_exists_by_created_contract_address_hash =
Repo.exists?(from(it in InternalTransaction, where: it.created_contract_address_hash == ^address_hash))
internal_transactions_exists_by_from_address_hash =
Repo.exists?(from(it in InternalTransaction, where: it.from_address_hash == ^address_hash))
internal_transactions_exists_by_to_address_hash =
Repo.exists?(from(it in InternalTransaction, where: it.to_address_hash == ^address_hash))
internal_transactions_exists_by_created_contract_address_hash || internal_transactions_exists_by_from_address_hash ||
internal_transactions_exists_by_to_address_hash
end
def check_if_token_transfers_at_address(address_hash, options \\ []) do
token_transfers_exists_by_from_address_hash =
select_repo(options).exists?(from(tt in TokenTransfer, where: tt.from_address_hash == ^address_hash))
token_transfers_exists_by_to_address_hash =
select_repo(options).exists?(from(tt in TokenTransfer, where: tt.to_address_hash == ^address_hash))
token_transfers_exists_by_from_address_hash ||
token_transfers_exists_by_to_address_hash
end
def check_if_tokens_at_address(address_hash, options \\ []) do
select_repo(options).exists?(
from(
tb in CurrentTokenBalance,
where: tb.address_hash == ^address_hash,
where: tb.value > 0
)
)
end
@spec check_if_withdrawals_at_address(Hash.Address.t()) :: boolean()
def check_if_withdrawals_at_address(address_hash, options \\ []) do
address_hash
|> Withdrawal.address_hash_to_withdrawals_unordered_query()
|> select_repo(options).exists?()
end
@doc """
Counts all of the block validations and groups by the `miner_hash`.
"""
@ -2679,53 +2503,6 @@ defmodule Explorer.Chain do
Repo.stream_each(query, fun)
end
@doc """
Counts the number of `t:Explorer.Chain.Block.t/0` validated by the address with the given `hash`.
"""
@spec address_to_validation_count(Hash.Address.t(), [api?]) :: non_neg_integer()
def address_to_validation_count(hash, options) do
query = from(block in Block, where: block.miner_hash == ^hash, select: fragment("COUNT(*)"))
select_repo(options).one(query)
end
@spec address_to_transaction_count(Address.t()) :: non_neg_integer()
def address_to_transaction_count(address) do
address_hash_to_transaction_count(address.hash)
end
@spec address_to_token_transfer_count(Address.t()) :: non_neg_integer()
def address_to_token_transfer_count(address) do
query =
from(
token_transfer in TokenTransfer,
where: token_transfer.to_address_hash == ^address.hash,
or_where: token_transfer.from_address_hash == ^address.hash
)
Repo.aggregate(query, :count, timeout: :infinity)
end
@spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil
def address_to_gas_usage_count(address) do
if contract?(address) do
incoming_transaction_gas_usage = address_to_incoming_transaction_gas_usage(address.hash)
cond do
!incoming_transaction_gas_usage ->
address_to_outcoming_transaction_gas_usage(address.hash)
Decimal.compare(incoming_transaction_gas_usage, 0) == :eq ->
address_to_outcoming_transaction_gas_usage(address.hash)
true ->
incoming_transaction_gas_usage
end
else
address_to_outcoming_transaction_gas_usage(address.hash)
end
end
@doc """
Return the balance in usd corresponding to this token. Return nil if the fiat_value of the token is not present.
"""
@ -2742,9 +2519,9 @@ defmodule Explorer.Chain do
Decimal.mult(tokens, fiat_value)
end
defp contract?(%{contract_code: nil}), do: false
def contract?(%{contract_code: nil}), do: false
defp contract?(%{contract_code: _}), do: true
def contract?(%{contract_code: _}), do: true
@doc """
Returns a stream of unfetched `t:Explorer.Chain.Address.CoinBalance.t/0`.
@ -5624,7 +5401,7 @@ defmodule Explorer.Chain do
def count_token_holders_from_token_hash(contract_address_hash) do
query =
from(ctb in CurrentTokenBalance.token_holders_query_for_count(contract_address_hash),
select: fragment("COUNT(DISTINCT(address_hash))")
select: fragment("COUNT(DISTINCT(?))", ctb.address_hash)
)
Repo.one!(query, timeout: :infinity)
@ -6696,55 +6473,6 @@ defmodule Explorer.Chain do
NewContractsCounter.fetch(options)
end
def address_counters(address, options \\ []) do
validation_count_task =
Task.async(fn ->
address_to_validation_count(address.hash, options)
end)
Task.start_link(fn ->
transaction_count(address)
end)
Task.start_link(fn ->
token_transfers_count(address)
end)
Task.start_link(fn ->
gas_usage_count(address)
end)
[
validation_count_task
]
|> Task.yield_many(:infinity)
|> Enum.map(fn {_task, res} ->
case res do
{:ok, result} ->
result
{:exit, reason} ->
raise "Query fetching address counters terminated: #{inspect(reason)}"
nil ->
raise "Query fetching address counters timed out."
end
end)
|> List.to_tuple()
end
def transaction_count(address) do
AddressTransactionsCounter.fetch(address)
end
def token_transfers_count(address) do
AddressTokenTransfersCounter.fetch(address)
end
def gas_usage_count(address) do
AddressTransactionsGasUsageCounter.fetch(address)
end
def fetch_token_counters(address_hash, timeout) do
total_token_transfers_task =
Task.async(fn ->

@ -0,0 +1,290 @@
defmodule Explorer.Chain.Address.Counters do
@moduledoc """
Functions related to Explorer.Chain.Address counters
"""
import Ecto.Query, only: [from: 2]
import Explorer.Chain,
only: [select_repo: 1]
alias Explorer.{Chain, Repo}
alias Explorer.Counters.{
AddressesCounter,
AddressesWithBalanceCounter,
AddressTokenTransfersCounter,
AddressTransactionsCounter,
AddressTransactionsGasUsageCounter
}
alias Explorer.Chain.{
Address,
Address.CurrentTokenBalance,
Block,
Hash,
InternalTransaction,
Log,
TokenTransfer,
Transaction,
Withdrawal
}
alias Explorer.Chain.Cache.Helper, as: CacheHelper
def check_if_validated_blocks_at_address(address_hash, options \\ []) do
select_repo(options).exists?(from(b in Block, where: b.miner_hash == ^address_hash))
end
def check_if_logs_at_address(address_hash, options \\ []) do
select_repo(options).exists?(from(l in Log, where: l.address_hash == ^address_hash))
end
def check_if_internal_transactions_at_address(address_hash) do
Repo.exists?(from(it in InternalTransaction, where: it.created_contract_address_hash == ^address_hash)) ||
Repo.exists?(from(it in InternalTransaction, where: it.from_address_hash == ^address_hash)) ||
Repo.exists?(from(it in InternalTransaction, where: it.to_address_hash == ^address_hash))
end
def check_if_token_transfers_at_address(address_hash, options \\ []) do
token_transfers_exists_by_from_address_hash =
select_repo(options).exists?(from(tt in TokenTransfer, where: tt.from_address_hash == ^address_hash))
token_transfers_exists_by_to_address_hash =
select_repo(options).exists?(from(tt in TokenTransfer, where: tt.to_address_hash == ^address_hash))
token_transfers_exists_by_from_address_hash ||
token_transfers_exists_by_to_address_hash
end
def check_if_tokens_at_address(address_hash, options \\ []) do
select_repo(options).exists?(
from(
tb in CurrentTokenBalance,
where: tb.address_hash == ^address_hash,
where: tb.value > 0
)
)
end
@spec check_if_withdrawals_at_address(Hash.Address.t()) :: boolean()
def check_if_withdrawals_at_address(address_hash, options \\ []) do
address_hash
|> Withdrawal.address_hash_to_withdrawals_unordered_query()
|> select_repo(options).exists?()
end
@doc """
Gets from the cache the count of `t:Explorer.Chain.Address.t/0`'s where the `fetched_coin_balance` is > 0
"""
@spec count_addresses_with_balance_from_cache :: non_neg_integer()
def count_addresses_with_balance_from_cache do
AddressesWithBalanceCounter.fetch()
end
@doc """
Estimated count of `t:Explorer.Chain.Address.t/0`.
Estimated count of addresses.
"""
@spec address_estimated_count() :: non_neg_integer()
def address_estimated_count(options \\ []) do
cached_value = AddressesCounter.fetch()
if is_nil(cached_value) || cached_value == 0 do
count = CacheHelper.estimated_count_from("addresses", options)
max(count, 0)
else
cached_value
end
end
@doc """
Counts the number of all addresses.
This function should be used with caution. In larger databases, it may take a
while to have the return back.
"""
def count_addresses do
Repo.aggregate(Address, :count, timeout: :infinity)
end
@doc """
Get the total number of transactions sent by the address with the given hash according to the last block indexed.
We have to increment +1 in the last nonce result because it works like an array position, the first
nonce has the value 0. When last nonce is nil, it considers that the given address has 0 transactions.
"""
@spec total_transactions_sent_by_address(Hash.Address.t()) :: non_neg_integer()
def total_transactions_sent_by_address(address_hash) do
last_nonce =
address_hash
|> Transaction.last_nonce_by_address_query()
|> Repo.one(timeout: :infinity)
case last_nonce do
nil -> 0
value -> value + 1
end
end
def address_hash_to_transaction_count_query(address_hash) do
from(
transaction in Transaction,
where: transaction.to_address_hash == ^address_hash or transaction.from_address_hash == ^address_hash
)
end
@spec address_hash_to_transaction_count(Hash.Address.t()) :: non_neg_integer()
def address_hash_to_transaction_count(address_hash) do
query = address_hash_to_transaction_count_query(address_hash)
Repo.aggregate(query, :count, :hash, timeout: :infinity)
end
@spec address_to_transaction_count(Address.t()) :: non_neg_integer()
def address_to_transaction_count(address) do
address_hash_to_transaction_count(address.hash)
end
def address_hash_to_validation_count_query(hash) do
from(block in Block, where: block.miner_hash == ^hash, select: fragment("COUNT(*)"))
end
@doc """
Counts the number of `t:Explorer.Chain.Block.t/0` validated by the address with the given `hash`.
"""
@spec address_to_validation_count(Hash.Address.t(), [Chain.api?()]) :: non_neg_integer()
def address_to_validation_count(hash, options) do
query = address_hash_to_validation_count_query(hash)
select_repo(options).one(query)
end
@doc """
Counts the number of addresses with fetched coin balance > 0.
This function should be used with caution. In larger databases, it may take a
while to have the return back.
"""
def count_addresses_with_balance do
Repo.one(
Address.count_with_fetched_coin_balance(),
timeout: :infinity
)
end
@spec address_to_incoming_transaction_count(Hash.Address.t()) :: non_neg_integer()
def address_to_incoming_transaction_count(address_hash) do
to_address_query =
from(
transaction in Transaction,
where: transaction.to_address_hash == ^address_hash
)
Repo.aggregate(to_address_query, :count, :hash, timeout: :infinity)
end
@spec address_to_incoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil
def address_to_incoming_transaction_gas_usage(address_hash) do
to_address_query =
from(
transaction in Transaction,
where: transaction.to_address_hash == ^address_hash
)
Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity)
end
@spec address_to_outcoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil
def address_to_outcoming_transaction_gas_usage(address_hash) do
to_address_query =
from(
transaction in Transaction,
where: transaction.from_address_hash == ^address_hash
)
Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity)
end
@spec address_to_token_transfer_count(Address.t()) :: non_neg_integer()
def address_to_token_transfer_count(address) do
query =
from(
token_transfer in TokenTransfer,
where: token_transfer.to_address_hash == ^address.hash,
or_where: token_transfer.from_address_hash == ^address.hash
)
Repo.aggregate(query, :count, timeout: :infinity)
end
@spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil
def address_to_gas_usage_count(address) do
if Chain.contract?(address) do
incoming_transaction_gas_usage = address_to_incoming_transaction_gas_usage(address.hash)
cond do
!incoming_transaction_gas_usage ->
address_to_outcoming_transaction_gas_usage(address.hash)
Decimal.compare(incoming_transaction_gas_usage, 0) == :eq ->
address_to_outcoming_transaction_gas_usage(address.hash)
true ->
incoming_transaction_gas_usage
end
else
address_to_outcoming_transaction_gas_usage(address.hash)
end
end
def address_counters(address, options \\ []) do
validation_count_task =
Task.async(fn ->
address_to_validation_count(address.hash, options)
end)
Task.start_link(fn ->
transaction_count(address)
end)
Task.start_link(fn ->
token_transfers_count(address)
end)
Task.start_link(fn ->
gas_usage_count(address)
end)
[
validation_count_task
]
|> Task.yield_many(:infinity)
|> Enum.map(fn {_task, res} ->
case res do
{:ok, result} ->
result
{:exit, reason} ->
raise "Query fetching address counters terminated: #{inspect(reason)}"
nil ->
raise "Query fetching address counters timed out."
end
end)
|> List.to_tuple()
end
def transaction_count(address) do
AddressTransactionsCounter.fetch(address)
end
def token_transfers_count(address) do
AddressTokenTransfersCounter.fetch(address)
end
def gas_usage_count(address) do
AddressTransactionsGasUsageCounter.fetch(address)
end
end

@ -5,8 +5,9 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do
use GenServer
alias Ecto.Changeset
alias Explorer.{Chain, Repo}
alias Explorer.Chain.Address.Counters
alias Explorer.Counters.Helper
alias Explorer.Repo
@cache_name :address_transactions_gas_usage_counter
@last_update_key "last_update"
@ -67,7 +68,7 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do
defp update_cache(address) do
address_hash_string = to_string(address.hash)
put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", Helper.current_time())
new_data = Chain.address_to_gas_usage_count(address)
new_data = Counters.address_to_gas_usage_count(address)
put_into_cache("hash_#{address_hash_string}", new_data)
put_into_db(address, new_data)
end

@ -5,8 +5,9 @@ defmodule Explorer.Counters.AddressTokenTransfersCounter do
use GenServer
alias Ecto.Changeset
alias Explorer.{Chain, Repo}
alias Explorer.Chain.Address.Counters
alias Explorer.Counters.Helper
alias Explorer.Repo
@cache_name :address_token_transfers_counter
@last_update_key "last_update"
@ -67,7 +68,7 @@ defmodule Explorer.Counters.AddressTokenTransfersCounter do
defp update_cache(address) do
address_hash_string = to_string(address.hash)
put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", Helper.current_time())
new_data = Chain.address_to_token_transfer_count(address)
new_data = Counters.address_to_token_transfer_count(address)
put_into_cache("hash_#{address_hash_string}", new_data)
put_into_db(address, new_data)
end

@ -5,8 +5,9 @@ defmodule Explorer.Counters.AddressTransactionsCounter do
use GenServer
alias Ecto.Changeset
alias Explorer.{Chain, Repo}
alias Explorer.Chain.Address.Counters
alias Explorer.Counters.Helper
alias Explorer.Repo
@cache_name :address_transactions_counter
@last_update_key "last_update"
@ -67,7 +68,7 @@ defmodule Explorer.Counters.AddressTransactionsCounter do
defp update_cache(address) do
address_hash_string = to_string(address.hash)
put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", Helper.current_time())
new_data = Chain.address_to_transaction_count(address)
new_data = Counters.address_to_transaction_count(address)
put_into_cache("hash_#{address_hash_string}", new_data)
put_into_db(address, new_data)
end

@ -7,7 +7,7 @@ defmodule Explorer.Counters.AddressesCounter do
use GenServer
alias Explorer.Chain
alias Explorer.Chain.Address.Counters
@table :addresses_counter
@ -104,7 +104,7 @@ defmodule Explorer.Counters.AddressesCounter do
Consolidates the info by populating the `:ets` table with the current database information.
"""
def consolidate do
counter = Chain.count_addresses()
counter = Counters.count_addresses()
insert_counter({cache_key(), counter})
end

@ -7,7 +7,7 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do
use GenServer
alias Explorer.Chain
alias Explorer.Chain.Address.Counters
@table :addresses_with_balance_counter
@ -104,7 +104,7 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do
Consolidates the info by populating the `:ets` table with the current database information.
"""
def consolidate do
counter = Chain.count_addresses_with_balance()
counter = Counters.count_addresses_with_balance()
insert_counter({cache_key(), counter})
end

@ -28,6 +28,7 @@ defmodule Explorer.ChainTest do
}
alias Explorer.{Chain, Etherscan}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.Cache.Block, as: BlockCache
alias Explorer.Chain.Cache.Transaction, as: TransactionCache
alias Explorer.Chain.Cache.PendingBlockOperation, as: PendingBlockOperationCache
@ -84,7 +85,7 @@ defmodule Explorer.ChainTest do
start_supervised!(AddressesWithBalanceCounter)
AddressesWithBalanceCounter.consolidate()
addresses_with_balance = Chain.count_addresses_with_balance_from_cache()
addresses_with_balance = Counters.count_addresses_with_balance_from_cache()
assert is_integer(addresses_with_balance)
assert addresses_with_balance == 2
@ -100,7 +101,7 @@ defmodule Explorer.ChainTest do
start_supervised!(AddressesCounter)
AddressesCounter.consolidate()
addresses_with_balance = Chain.address_estimated_count()
addresses_with_balance = Counters.address_estimated_count()
assert is_integer(addresses_with_balance)
assert addresses_with_balance == 3
@ -108,7 +109,7 @@ defmodule Explorer.ChainTest do
test "returns 0 on empty table" do
start_supervised!(AddressesCounter)
assert 0 == Chain.address_estimated_count()
assert 0 == Counters.address_estimated_count()
end
end
@ -875,7 +876,7 @@ defmodule Explorer.ChainTest do
|> insert(nonce: 100, from_address: address)
|> with_block(insert(:block, number: 1000))
assert Chain.total_transactions_sent_by_address(address.hash) == 101
assert Counters.total_transactions_sent_by_address(address.hash) == 101
end
test "returns 0 when the address did not send transactions" do
@ -885,7 +886,7 @@ defmodule Explorer.ChainTest do
|> insert(nonce: 100, to_address: address)
|> with_block(insert(:block, number: 1000))
assert Chain.total_transactions_sent_by_address(address.hash) == 0
assert Counters.total_transactions_sent_by_address(address.hash) == 0
end
end
@ -1099,13 +1100,13 @@ defmodule Explorer.ChainTest do
test "without transactions" do
%Address{hash: address_hash} = insert(:address)
assert Chain.address_to_incoming_transaction_count(address_hash) == 0
assert Counters.address_to_incoming_transaction_count(address_hash) == 0
end
test "with transactions" do
%Transaction{to_address: to_address} = insert(:transaction)
assert Chain.address_to_incoming_transaction_count(to_address.hash) == 1
assert Counters.address_to_incoming_transaction_count(to_address.hash) == 1
end
end

Loading…
Cancel
Save