diff --git a/CHANGELOG.md b/CHANGELOG.md index 44a0a91567..1aa1af4fbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ ## Current ### Features +- [#3125](https://github.com/poanetwork/blockscout/pull/3125) - Availability to configure a number of days to consider at coin balance history chart via environment variable ### Fixes +- [#3140](https://github.com/poanetwork/blockscout/pull/3140) - Fix performance of the balance changing history list loading - [#3133](https://github.com/poanetwork/blockscout/pull/3133) - Take into account FIRST_BLOCK in trace_ReplayBlockTransactions requests - [#3132](https://github.com/poanetwork/blockscout/pull/3132) - Fix performance of coin supply API endpoints - [#3130](https://github.com/poanetwork/blockscout/pull/3130) - Take into account FIRST_BLOCK for block rewards fetching diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex index 538f8fd7f4..b4e21e3393 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex @@ -23,6 +23,16 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do {coin_balances, next_page} = split_list_by_page(coin_balances_plus_one) + deduplicated_coin_balances = + coin_balances + |> Enum.dedup_by(fn record -> + if record.delta == Decimal.new(0) do + :dup + else + System.unique_integer() + end + end) + next_page_url = case next_page_params(next_page, coin_balances, params) do nil -> @@ -38,7 +48,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do end coin_balances_json = - Enum.map(coin_balances, fn coin_balance -> + Enum.map(deduplicated_coin_balances, fn coin_balance -> View.render_to_string( AddressCoinBalanceView, "_coin_balances.html", diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 481ae4a730..f89e94a365 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3493,17 +3493,62 @@ defmodule Explorer.Chain do def address_to_coin_balances(address_hash, options) do paging_options = Keyword.get(options, :paging_options, @default_paging_options) - address_hash - |> CoinBalance.fetch_coin_balances(paging_options) - |> page_coin_balances(paging_options) - |> Repo.all() - |> Enum.dedup_by(fn record -> - if record.delta == Decimal.new(0) do - :dup + balances_raw = + address_hash + |> CoinBalance.fetch_coin_balances(paging_options) + |> page_coin_balances(paging_options) + |> Repo.all() + + min_block_number = + balances_raw + |> Enum.min_by(fn balance -> balance.block_number end) + |> Map.get(:block_number) + + max_block_number = + balances_raw + |> Enum.max_by(fn balance -> balance.block_number end) + |> Map.get(:block_number) + + min_block_timestamp = find_block_timestamp(min_block_number) + max_block_timestamp = find_block_timestamp(max_block_number) + + min_block_unix_timestamp = + min_block_timestamp + |> Timex.to_unix() + + max_block_unix_timestamp = + max_block_timestamp + |> Timex.to_unix() + + blocks_delta = max_block_number - min_block_number + + balances_with_dates = + if blocks_delta > 0 do + balances_raw + |> Enum.map(fn balance -> + date = + trunc( + min_block_unix_timestamp + + (balance.block_number - min_block_number) * (max_block_unix_timestamp - min_block_unix_timestamp) / + blocks_delta + ) + + formatted_date = Timex.from_unix(date) + %{balance | block_timestamp: formatted_date} + end) else - System.unique_integer() + balances_raw + |> Enum.map(fn balance -> + date = min_block_unix_timestamp + + formatted_date = Timex.from_unix(date) + %{balance | block_timestamp: formatted_date} + end) end - end) + + balances_with_dates + |> Enum.filter(fn balance -> balance.value end) + |> Enum.sort(fn balance1, balance2 -> balance1.block_timestamp >= balance2.block_timestamp end) end def get_coin_balance(address_hash, block_number) do @@ -4334,4 +4379,11 @@ defmodule Explorer.Chain do block_index end end + + defp find_block_timestamp(number) do + Block + |> where([b], b.number == ^number) + |> select([b], b.timestamp) + |> Repo.one() + end end diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance.ex b/apps/explorer/lib/explorer/chain/address/coin_balance.ex index f3647a8f54..8e410e993f 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance.ex @@ -76,12 +76,9 @@ defmodule Explorer.Chain.Address.CoinBalance do cb in CoinBalance, where: cb.address_hash == ^address_hash, where: not is_nil(cb.value), - inner_join: b in Block, - on: cb.block_number == b.number, order_by: [desc: :block_number], limit: ^page_size, - select_merge: %{delta: fragment("value - coalesce(lag(value, 1) over (order by block_number), 0)")}, - select_merge: %{block_timestamp: b.timestamp} + select_merge: %{delta: fragment("value - coalesce(lead(value, 1) over (order by block_number desc), 0)")} ) end diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 158e11fe58..76774460c1 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -5025,32 +5025,6 @@ defmodule Explorer.ChainTest do end end - describe "address_to_coin_balances/2" do - test "deduplicates records by zero delta" do - address = insert(:address) - - 1..5 - |> Enum.each(fn block_number -> - insert(:block, number: block_number) - insert(:fetched_balance, value: 1, block_number: block_number, address_hash: address.hash) - end) - - insert(:block, number: 6) - insert(:fetched_balance, value: 2, block_number: 6, address_hash: address.hash) - - assert [first, second, third] = Chain.address_to_coin_balances(address.hash, []) - - assert first.block_number == 6 - assert first.delta == Decimal.new(1) - - assert second.block_number == 5 - assert second.delta == Decimal.new(0) - - assert third.block_number == 1 - assert third.delta == Decimal.new(1) - end - end - describe "extract_db_name/1" do test "extracts correct db name" do db_url = "postgresql://viktor:@localhost:5432/blockscout-dev-1"