From cdb42ab90017fddcd9582f05f0cecfc5effdb883 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 30 May 2023 11:06:03 +0300 Subject: [PATCH 1/8] Store and display market cap from the DB --- CHANGELOG.md | 1 + .../channels/address_channel.ex | 7 +- .../account/api/v1/user_controller.ex | 7 +- .../address_coin_balance_controller.ex | 5 +- .../address_contract_controller.ex | 3 +- .../controllers/address_controller.ex | 7 +- .../address_decompiled_contract_controller.ex | 3 +- ...address_internal_transaction_controller.ex | 5 +- .../controllers/address_logs_controller.ex | 3 +- .../address_read_contract_controller.ex | 3 +- .../address_read_proxy_controller.ex | 3 +- .../controllers/address_token_controller.ex | 3 +- .../address_token_transfer_controller.ex | 5 +- .../address_transaction_controller.ex | 5 +- .../address_validation_controller.ex | 3 +- .../address_withdrawal_controller.ex | 5 +- .../address_write_contract_controller.ex | 3 +- .../address_write_proxy_controller.ex | 3 +- .../controllers/api/v2/address_controller.ex | 3 +- .../controllers/api/v2/stats_controller.ex | 9 +- .../chain/market_history_chart_controller.ex | 3 +- .../controllers/chain_controller.ex | 3 +- .../controllers/transaction_controller.ex | 5 +- ...saction_internal_transaction_controller.ex | 3 +- .../controllers/transaction_log_controller.ex | 3 +- .../transaction_raw_trace_controller.ex | 3 +- .../transaction_state_controller.ex | 3 +- .../transaction_token_transfer_controller.ex | 3 +- .../lib/block_scout_web/notifier.ex | 5 +- .../views/account/watchlist_view.ex | 3 +- .../views/api/v2/address_view.ex | 3 +- .../views/api/v2/transaction_view.ex | 3 +- .../api/v2/transaction_controller_test.exs | 27 +++++ .../address_transaction_csv_exporter.ex | 3 +- .../explorer/chain/supply/exchange_rate.ex | 3 +- .../lib/explorer/exchange_rates/token.ex | 20 ++-- .../lib/explorer/market/history/cataloger.ex | 110 +++++++++++++++--- .../market/history/source/market_cap.ex | 18 +++ .../history/source/market_cap/coin_gecko.ex | 60 ++++++++++ .../history/{source.ex => source/price.ex} | 6 +- .../source/{ => price}/crypto_compare.ex | 12 +- apps/explorer/lib/explorer/market/market.ex | 38 ++++-- .../lib/explorer/market/market_history.ex | 5 +- ...30074105_market_history_add_market_cap.exs | 9 ++ .../market/history/cataloger_test.exs | 55 +++++++-- .../{ => price}/crypto_compare_test.exs | 12 +- apps/explorer/test/test_helper.exs | 2 +- 47 files changed, 357 insertions(+), 149 deletions(-) create mode 100644 apps/explorer/lib/explorer/market/history/source/market_cap.ex create mode 100644 apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex rename apps/explorer/lib/explorer/market/history/{source.ex => source/price.ex} (58%) rename apps/explorer/lib/explorer/market/history/source/{ => price}/crypto_compare.ex (86%) create mode 100644 apps/explorer/priv/repo/migrations/20230530074105_market_history_add_market_cap.exs rename apps/explorer/test/explorer/market/history/source/{ => price}/crypto_compare_test.exs (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2706781162a..095242e27c43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - [#7653](https://github.com/blockscout/blockscout/pull/7653) - Add support for DEPOSIT and WITHDRAW token transfer event in older contracts - [#7628](https://github.com/blockscout/blockscout/pull/7628) - Support partially verified property from verifier MS; Add property to track contracts automatically verified via eth-bytecode-db - [#7603](https://github.com/blockscout/blockscout/pull/7603) - Add Polygon Edge and optimism genesis files support +- [#7585](https://github.com/blockscout/blockscout/pull/7585) - Store and display native coin market cap from the DB - [#7513](https://github.com/blockscout/blockscout/pull/7513) - Add Polygon Edge support - [#7532](https://github.com/blockscout/blockscout/pull/7532) - Handle empty id in json rpc responses - [#7544](https://github.com/blockscout/blockscout/pull/7544) - Add ERC-1155 signatures to uncataloged_token_transfer_block_numbers diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex index 6072545f34c4..ba808cc16f2b 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -18,7 +18,6 @@ defmodule BlockScoutWeb.AddressChannel do alias Explorer.{Chain, Market, Repo} alias Explorer.Chain.{Hash, Transaction, Wei} alias Explorer.Chain.Hash.Address, as: AddressHash - alias Explorer.ExchangeRates.Token alias Phoenix.View intercept([ @@ -43,7 +42,7 @@ defmodule BlockScoutWeb.AddressChannel do with {:ok, casted_address_hash} <- AddressHash.cast(socket.assigns.address_hash), {:ok, address = %{fetched_coin_balance: balance}} when not is_nil(balance) <- Chain.hash_to_address(casted_address_hash), - exchange_rate <- Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate <- Market.get_coin_exchange_rate(), {:ok, rendered} <- render_balance_card(address, exchange_rate, socket) do reply = {:ok, @@ -233,7 +232,7 @@ defmodule BlockScoutWeb.AddressChannel do ) do push(socket, "current_coin_balance", %{ coin_balance: (coin_balance && coin_balance.value) || %Wei{value: Decimal.new(0)}, - exchange_rate: (Market.get_exchange_rate(Explorer.coin()) || Token.null()).usd_value, + exchange_rate: Market.get_coin_exchange_rate().usd_value, block_number: block_number }) end @@ -248,7 +247,7 @@ defmodule BlockScoutWeb.AddressChannel do conn: socket, address: Chain.hash_to_address(hash), coin_balance: (coin_balance && coin_balance.value) || %Wei{value: Decimal.new(0)}, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate: Market.get_coin_exchange_rate() ) rendered_link = diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex index 42b67c7c1759..6c7f54e867f8 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do alias Explorer.Account.Api.Key, as: ApiKey alias Explorer.Account.CustomABI alias Explorer.Account.{Identity, PublicTagsRequest, TagAddress, TagTransaction, WatchlistAddress} - alias Explorer.ExchangeRates.Token alias Explorer.{Market, Repo} alias Plug.CSRFProtection @@ -34,7 +33,7 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do conn |> put_status(200) |> render(:watchlist_addresses, %{ - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), watchlist_addresses: watchlist_with_addresses.watchlist_addresses }) end @@ -103,7 +102,7 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do |> put_status(200) |> render(:watchlist_address, %{ watchlist_address: watchlist_address, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate: Market.get_coin_exchange_rate() }) end end @@ -160,7 +159,7 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do |> put_status(200) |> render(:watchlist_address, %{ watchlist_address: watchlist_address, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate: Market.get_coin_exchange_rate() }) end end 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 cd85a4c71626..4df49f06520d 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 @@ -12,7 +12,6 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do alias BlockScoutWeb.{AccessHelper, AddressCoinBalanceView, Controller} alias Explorer.{Chain, Market} alias Explorer.Chain.{Address, Wei} - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand alias Phoenix.View @@ -76,7 +75,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do render(conn, "index.html", address: address, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), current_path: Controller.current_full_path(conn), counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), tags: get_address_tags(address_hash, current_user(conn)) @@ -102,7 +101,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do "index.html", address: address, coin_balance_status: nil, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), current_path: Controller.current_full_path(conn), tags: get_address_tags(address_hash, current_user(conn)) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex index da6e864e6937..d049ce1511ed 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex @@ -7,7 +7,6 @@ defmodule BlockScoutWeb.AddressContractController do alias BlockScoutWeb.AccessHelper alias Explorer.{Chain, Market} - alias Explorer.ExchangeRates.Token alias Explorer.SmartContract.Solidity.PublishHelper alias Indexer.Fetcher.CoinBalanceOnDemand @@ -31,7 +30,7 @@ defmodule BlockScoutWeb.AddressContractController do "index.html", address: address, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), tags: get_address_tags(address_hash, current_user(conn)) ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex index 4331bdf6befe..9e37382e12a7 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -16,7 +16,6 @@ defmodule BlockScoutWeb.AddressController do alias Explorer.{Chain, Market} alias Explorer.Chain.Wei - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand alias Phoenix.View @@ -41,7 +40,7 @@ defmodule BlockScoutWeb.AddressController do ) end - exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate = Market.get_coin_exchange_rate() total_supply = Chain.total_supply() items_count_str = Map.get(params, "items_count") @@ -101,7 +100,7 @@ defmodule BlockScoutWeb.AddressController do "_show_address_transactions.html", address: address, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), filter: params["filter"], counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), current_path: Controller.current_full_path(conn), @@ -131,7 +130,7 @@ defmodule BlockScoutWeb.AddressController do "_show_address_transactions.html", address: address, coin_balance_status: nil, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), filter: params["filter"], counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), current_path: Controller.current_full_path(conn), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex index 292ea0f929c9..69c2d7dc0f6f 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex @@ -6,7 +6,6 @@ defmodule BlockScoutWeb.AddressDecompiledContractController do alias BlockScoutWeb.AccessHelper alias Explorer.{Chain, Market} - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand def index(conn, %{"address_id" => address_hash_string} = params) do @@ -18,7 +17,7 @@ defmodule BlockScoutWeb.AddressDecompiledContractController do "index.html", address: address, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), tags: get_address_tags(address_hash, current_user(conn)) ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex index daca356e1ff0..92fbe1712b44 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex @@ -12,7 +12,6 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do alias BlockScoutWeb.{AccessHelper, Controller, InternalTransactionView} alias Explorer.{Chain, Market} alias Explorer.Chain.{Address, Wei} - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand alias Phoenix.View @@ -86,7 +85,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do address: address, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), current_path: Controller.current_full_path(conn), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), filter: params["filter"], counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), tags: get_address_tags(address_hash, current_user(conn)) @@ -113,7 +112,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do address: address, filter: params["filter"], coin_balance_status: nil, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), current_path: Controller.current_full_path(conn), tags: get_address_tags(address_hash, current_user(conn)) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex index 67bbfc5958d9..ea6c656bac0f 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex @@ -11,7 +11,6 @@ defmodule BlockScoutWeb.AddressLogsController do alias BlockScoutWeb.{AccessHelper, AddressLogsView, Controller} alias Explorer.{Chain, Market} - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand alias Phoenix.View @@ -67,7 +66,7 @@ defmodule BlockScoutWeb.AddressLogsController do address: address, current_path: Controller.current_full_path(conn), coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), tags: get_address_tags(address_hash, current_user(conn)) ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex index b963cfa3e46c..ae534280cc74 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex @@ -15,7 +15,6 @@ defmodule BlockScoutWeb.AddressReadContractController do alias BlockScoutWeb.AddressView alias Explorer.{Chain, Market} alias Explorer.Chain.Address - alias Explorer.ExchangeRates.Token alias Explorer.SmartContract.Reader alias Indexer.Fetcher.CoinBalanceOnDemand @@ -40,7 +39,7 @@ defmodule BlockScoutWeb.AddressReadContractController do type: :regular, action: :read, custom_abi: custom_abi?, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate: Market.get_coin_exchange_rate() ] with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_read_proxy_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_proxy_controller.ex index 6e4534af7ce9..5a4d9c981d04 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_read_proxy_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_proxy_controller.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.AddressReadProxyController do alias BlockScoutWeb.AccessHelper alias Explorer.{Chain, Market} alias Explorer.Chain.Address - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand def index(conn, %{"address_id" => address_hash_string} = params) do @@ -33,7 +32,7 @@ defmodule BlockScoutWeb.AddressReadProxyController do type: :proxy, action: :read, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), tags: get_address_tags(address_hash, current_user(conn)) ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex index f896e75e3fd2..8762432af79a 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.AddressTokenController do alias BlockScoutWeb.{AccessHelper, AddressTokenView, Controller} alias Explorer.{Chain, Market} alias Explorer.Chain.Address - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand alias Phoenix.View @@ -73,7 +72,7 @@ defmodule BlockScoutWeb.AddressTokenController do address: address, current_path: Controller.current_full_path(conn), coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), tags: get_address_tags(address_hash, current_user(conn)) ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex index 8bb51818f688..e8d21d0d2c97 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex @@ -5,7 +5,6 @@ defmodule BlockScoutWeb.AddressTokenTransferController do import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] alias BlockScoutWeb.{AccessHelper, Controller, TransactionView} - alias Explorer.ExchangeRates.Token alias Explorer.{Chain, Market} alias Explorer.Chain.Address alias Indexer.Fetcher.CoinBalanceOnDemand @@ -109,7 +108,7 @@ defmodule BlockScoutWeb.AddressTokenTransferController do "index.html", address: address, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), filter: params["filter"], current_path: Controller.current_full_path(conn), token: token, @@ -202,7 +201,7 @@ defmodule BlockScoutWeb.AddressTokenTransferController do "index.html", address: address, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), filter: params["filter"], current_path: Controller.current_full_path(conn), counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex index 999c619bc9db..fc9aad5e2035 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex @@ -23,7 +23,6 @@ defmodule BlockScoutWeb.AddressTransactionController do alias Explorer.Chain.Wei - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand alias Phoenix.View @@ -124,7 +123,7 @@ defmodule BlockScoutWeb.AddressTransactionController do "index.html", address: address, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), filter: params["filter"], counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), current_path: Controller.current_full_path(conn), @@ -154,7 +153,7 @@ defmodule BlockScoutWeb.AddressTransactionController do "index.html", address: address, coin_balance_status: nil, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), filter: params["filter"], counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), current_path: Controller.current_full_path(conn), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex index a4893bf3cece..244deb3af04d 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex @@ -12,7 +12,6 @@ defmodule BlockScoutWeb.AddressValidationController do import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] alias BlockScoutWeb.{AccessHelper, BlockView, Controller} - alias Explorer.ExchangeRates.Token alias Explorer.{Chain, Market} alias Indexer.Fetcher.CoinBalanceOnDemand alias Phoenix.View @@ -83,7 +82,7 @@ defmodule BlockScoutWeb.AddressValidationController do coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), current_path: Controller.current_full_path(conn), counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), tags: get_address_tags(address_hash, current_user(conn)) ) else diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_withdrawal_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_withdrawal_controller.ex index 7fcb05d5b40b..937899617445 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_withdrawal_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_withdrawal_controller.ex @@ -16,7 +16,6 @@ defmodule BlockScoutWeb.AddressWithdrawalController do alias Explorer.Chain.Wei - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand alias Phoenix.View @@ -78,7 +77,7 @@ defmodule BlockScoutWeb.AddressWithdrawalController do "index.html", address: address, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), current_path: Controller.current_full_path(conn), tags: get_address_tags(address_hash, current_user(conn)) @@ -107,7 +106,7 @@ defmodule BlockScoutWeb.AddressWithdrawalController do "index.html", address: address, coin_balance_status: nil, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), current_path: Controller.current_full_path(conn), tags: get_address_tags(address_hash, current_user(conn)) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_write_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_write_contract_controller.ex index cd88e97a0bf9..32fc477ed283 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_write_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_write_contract_controller.ex @@ -15,7 +15,6 @@ defmodule BlockScoutWeb.AddressWriteContractController do alias BlockScoutWeb.AddressView alias Explorer.{Chain, Market} alias Explorer.Chain.Address - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand def index(conn, %{"address_id" => address_hash_string} = params) do @@ -35,7 +34,7 @@ defmodule BlockScoutWeb.AddressWriteContractController do type: :regular, action: :write, custom_abi: custom_abi?, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate: Market.get_coin_exchange_rate() ] with false <- AddressView.contract_interaction_disabled?(), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_write_proxy_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_write_proxy_controller.ex index 844c5d3ddcf4..68903261d6e3 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_write_proxy_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_write_proxy_controller.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.AddressWriteProxyController do alias BlockScoutWeb.{AccessHelper, AddressView} alias Explorer.{Chain, Market} alias Explorer.Chain.Address - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.CoinBalanceOnDemand def index(conn, %{"address_id" => address_hash_string} = params) do @@ -34,7 +33,7 @@ defmodule BlockScoutWeb.AddressWriteProxyController do type: :proxy, action: :write, coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), tags: get_address_tags(address_hash, current_user(conn)) ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 9b999f923476..bb20f5c63c06 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -15,7 +15,6 @@ defmodule BlockScoutWeb.API.V2.AddressController do alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.API.V2.{BlockView, TransactionView, WithdrawalView} - alias Explorer.ExchangeRates.Token alias Explorer.{Chain, Market} alias Indexer.Fetcher.{CoinBalanceOnDemand, TokenBalanceOnDemand} @@ -397,7 +396,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do next_page_params = next_page_params(next_page, addresses, params) - exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate = Market.get_coin_exchange_rate() total_supply = Chain.total_supply() conn diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex index d6974c090caf..3d93c7b83d1b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -9,7 +9,6 @@ defmodule BlockScoutWeb.API.V2.StatsController do alias Explorer.Chain.Supply.RSK alias Explorer.Chain.Transaction.History.TransactionStats alias Explorer.Counters.AverageBlockTime - alias Explorer.ExchangeRates.Token alias Timex.Duration @api_true [api?: true] @@ -24,7 +23,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do :standard end - exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate = Market.get_coin_exchange_rate() transaction_stats = Helper.get_transaction_stats() @@ -46,7 +45,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do "total_addresses" => @api_true |> Chain.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 || Market.get_native_coin_exchange_rate_from_db(), + "coin_price" => exchange_rate.usd_value, "total_gas_used" => GasUsage.total() |> to_string(), "transactions_today" => Enum.at(transaction_stats, 0).number_of_transactions |> to_string(), "gas_used_today" => Enum.at(transaction_stats, 0).gas_used, @@ -91,7 +90,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do end def market_chart(conn, _params) do - exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate = Market.get_coin_exchange_rate() recent_market_history = Market.fetch_recent_history() @@ -102,7 +101,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do [ %{ today - | closing_price: if(exchange_rate.usd_value, do: exchange_rate.usd_value, else: today.closing_price) + | closing_price: exchange_rate.usd_value } | the_rest ] diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex index 238f858769f7..467a87426bfc 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex @@ -2,11 +2,10 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do use BlockScoutWeb, :controller alias Explorer.{Chain, Market} - alias Explorer.ExchangeRates.Token def show(conn, _params) do if ajax?(conn) do - exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate = Market.get_coin_exchange_rate() recent_market_history = Market.fetch_recent_history() diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex index 95cd87fcf7e5..afa8143329b3 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex @@ -12,7 +12,6 @@ defmodule BlockScoutWeb.ChainController do alias Explorer.Chain.Cache.Transaction, as: TransactionCache alias Explorer.Chain.Supply.RSK alias Explorer.Counters.AverageBlockTime - alias Explorer.ExchangeRates.Token alias Explorer.Market alias Phoenix.View @@ -31,7 +30,7 @@ defmodule BlockScoutWeb.ChainController do :standard end - exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate = Market.get_coin_exchange_rate() transaction_stats = Helper.get_transaction_stats() diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex index 9158d1ca852f..5983ab8920cd 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex @@ -25,7 +25,6 @@ defmodule BlockScoutWeb.TransactionController do alias Explorer.{Chain, Market} alias Explorer.Chain.Cache.Transaction, as: TransactionCache - alias Explorer.ExchangeRates.Token alias Phoenix.View @necessity_by_association %{ @@ -161,7 +160,7 @@ defmodule BlockScoutWeb.TransactionController do render( conn, "show_token_transfers.html", - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), block_height: Chain.block_height(), current_path: Controller.current_full_path(conn), current_user: current_user(conn), @@ -199,7 +198,7 @@ defmodule BlockScoutWeb.TransactionController do render( conn, "show_internal_transactions.html", - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), current_path: Controller.current_full_path(conn), current_user: current_user(conn), block_height: Chain.block_height(), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex index 9f2b6c8fac98..4c6a017e776d 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do alias BlockScoutWeb.{AccessHelper, Controller, InternalTransactionView, TransactionController} alias Explorer.{Chain, Market} - alias Explorer.ExchangeRates.Token alias Phoenix.View def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do @@ -102,7 +101,7 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do render( conn, "index.html", - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), current_path: Controller.current_full_path(conn), current_user: current_user(conn), block_height: Chain.block_height(), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex index 70cb686badfc..53ba82ed3571 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.TransactionLogController do alias BlockScoutWeb.{AccessHelper, Controller, TransactionController, TransactionLogView} alias Explorer.{Chain, Market} - alias Explorer.ExchangeRates.Token alias Phoenix.View def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do @@ -99,7 +98,7 @@ defmodule BlockScoutWeb.TransactionLogController do current_path: Controller.current_full_path(conn), current_user: current_user(conn), transaction: transaction, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), tx_tags: diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex index 0fa47a09f2b9..93002d434822 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.TransactionRawTraceController do alias BlockScoutWeb.{AccessHelper, TransactionController} alias EthereumJSONRPC alias Explorer.{Chain, Market} - alias Explorer.ExchangeRates.Token alias Indexer.Fetcher.FirstTraceOnDemand def index(conn, %{"transaction_id" => hash_string} = params) do @@ -59,7 +58,7 @@ defmodule BlockScoutWeb.TransactionRawTraceController do render( conn, "index.html", - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), internal_transactions: internal_transactions, block_height: Chain.block_height(), current_user: current_user(conn), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex index 37a9a24dbf64..e2020a47bcca 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex @@ -10,7 +10,6 @@ defmodule BlockScoutWeb.TransactionStateController do } alias Explorer.{Chain, Market} - alias Explorer.ExchangeRates.Token alias Phoenix.View import BlockScoutWeb.Account.AuthController, only: [current_user: 1] @@ -107,7 +106,7 @@ defmodule BlockScoutWeb.TransactionStateController do render( conn, "index.html", - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), block_height: Chain.block_height(), current_path: Controller.current_full_path(conn), show_token_transfers: Chain.transaction_has_token_transfers?(transaction_hash), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex index e2fdffb4ed8d..d0f7fed1584f 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do alias BlockScoutWeb.{AccessHelper, Controller, TransactionController, TransactionTokenTransferView} alias Explorer.{Chain, Market} - alias Explorer.ExchangeRates.Token alias Phoenix.View {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") @@ -105,7 +104,7 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do render( conn, "index.html", - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + exchange_rate: Market.get_coin_exchange_rate(), block_height: Chain.block_height(), current_path: Controller.current_full_path(conn), current_user: current_user(conn), 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 7ca4a892a880..be32348540c7 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -21,7 +21,6 @@ defmodule BlockScoutWeb.Notifier do alias Explorer.Chain.Supply.RSK alias Explorer.Chain.Transaction.History.TransactionStats alias Explorer.Counters.{AverageBlockTime, Helper} - alias Explorer.ExchangeRates.Token alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler} alias Phoenix.View @@ -115,7 +114,7 @@ defmodule BlockScoutWeb.Notifier do end def handle_event({:chain_event, :exchange_rate}) do - exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate = Market.get_coin_exchange_rate() market_history_data = case Market.fetch_recent_history() do @@ -323,7 +322,7 @@ defmodule BlockScoutWeb.Notifier do "balance_update", %{ address: address, - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate: Market.get_coin_exchange_rate() } ) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_view.ex index fa3966323606..c6d513a2bff1 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_view.ex @@ -3,7 +3,6 @@ defmodule BlockScoutWeb.Account.WatchlistView do alias BlockScoutWeb.Account.WatchlistAddressView alias Explorer.Account.WatchlistAddress - alias Explorer.ExchangeRates.Token alias Explorer.Market alias Indexer.Fetcher.CoinBalanceOnDemand @@ -12,6 +11,6 @@ defmodule BlockScoutWeb.Account.WatchlistView do end def exchange_rate do - Market.get_exchange_rate(Explorer.coin()) || Token.null() + Market.get_coin_exchange_rate() end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index 90d85355e287..141a204803bb 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.API.V2.AddressView do alias BlockScoutWeb.API.V2.Helper alias Explorer.{Chain, Market} alias Explorer.Chain.{Address, SmartContract} - alias Explorer.ExchangeRates.Token @api_true [api?: true] @@ -78,7 +77,7 @@ defmodule BlockScoutWeb.API.V2.AddressView do end balance = address.fetched_coin_balance && address.fetched_coin_balance.value - exchange_rate = (Market.get_exchange_rate(Explorer.coin()) || Token.null()).usd_value + exchange_rate = Market.get_coin_exchange_rate().usd_value creator_hash = AddressView.from_address_hash(address) creation_tx = creator_hash && AddressView.transaction_hash(address) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index f12f320dc4bc..a546f2e2ccc3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -7,7 +7,6 @@ defmodule BlockScoutWeb.API.V2.TransactionView do alias BlockScoutWeb.Tokens.Helper, as: TokensHelper alias BlockScoutWeb.TransactionStateView alias Ecto.Association.NotLoaded - alias Explorer.ExchangeRates.Token, as: TokenRate alias Explorer.{Chain, Market} alias Explorer.Chain.{Address, Block, InternalTransaction, Log, Token, Transaction, Wei} alias Explorer.Chain.Block.Reward @@ -301,7 +300,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do "token_transfers" => token_transfers(transaction.token_transfers, conn, single_tx?), "token_transfers_overflow" => token_transfers_overflow(transaction.token_transfers, single_tx?), "actions" => transaction_actions(transaction.transaction_actions), - "exchange_rate" => (Market.get_exchange_rate(Explorer.coin()) || TokenRate.null()).usd_value, + "exchange_rate" => Market.get_coin_exchange_rate().usd_value, "method" => method_name(transaction, decoded_input), "tx_types" => tx_types(transaction), "tx_tag" => GetTransactionTags.get_transaction_tags(transaction.hash, current_user(single_tx? && conn)), diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index 14429b489b4f..4f4f12f8ff57 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -964,6 +964,33 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do assert Address.checksum(transaction.to_address_hash) == json["to"]["hash"] end + defp compare_item(%InternalTransaction{} = internal_tx, json) do + assert internal_tx.block_number == json["block"] + assert to_string(internal_tx.gas) == json["gas_limit"] + assert internal_tx.index == json["index"] + assert to_string(internal_tx.transaction_hash) == json["transaction_hash"] + assert Address.checksum(internal_tx.from_address_hash) == json["from"]["hash"] + assert Address.checksum(internal_tx.to_address_hash) == json["to"]["hash"] + end + + defp compare_item(%Log{} = log, json) do + assert to_string(log.data) == json["data"] + assert log.index == json["index"] + assert Address.checksum(log.address_hash) == json["address"]["hash"] + assert to_string(log.transaction_hash) == json["tx_hash"] + end + + defp compare_item(%TokenTransfer{} = token_transfer, json) do + assert Address.checksum(token_transfer.from_address_hash) == json["from"]["hash"] + assert Address.checksum(token_transfer.to_address_hash) == json["to"]["hash"] + assert to_string(token_transfer.transaction_hash) == json["tx_hash"] + assert json["timestamp"] == nil + assert json["method"] == nil + assert to_string(token_transfer.block_hash) == json["block_hash"] + assert to_string(token_transfer.log_index) == json["log_index"] + assert check_total(Repo.preload(token_transfer, [{:token, :contract_address}]).token, json["total"], token_transfer) + end + defp compare_item(%Transaction{} = transaction, json, wl_names) do assert to_string(transaction.hash) == json["hash"] assert transaction.block_number == json["block"] diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex index b53d1046e91c..ae47768cff0b 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex @@ -12,7 +12,6 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do alias Explorer.Market.MarketHistory alias Explorer.Chain.{Address, Transaction, Wei} alias Explorer.Chain.CSVExport.Helper - alias Explorer.ExchangeRates.Token @necessity_by_association [ necessity_by_association: %{ @@ -32,7 +31,7 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do @spec export(Address.t(), String.t(), String.t(), String.t() | nil, String.t() | nil) :: Enumerable.t() def export(address, from_period, to_period, filter_type \\ nil, filter_value \\ nil) do {from_block, to_block} = Helper.block_from_period(from_period, to_period) - exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + exchange_rate = Market.get_coin_exchange_rate() address.hash |> fetch_all_transactions(from_block, to_block, filter_type, filter_value, @paging_options) diff --git a/apps/explorer/lib/explorer/chain/supply/exchange_rate.ex b/apps/explorer/lib/explorer/chain/supply/exchange_rate.ex index d45a8edc02e4..1ef83b1a90c2 100644 --- a/apps/explorer/lib/explorer/chain/supply/exchange_rate.ex +++ b/apps/explorer/lib/explorer/chain/supply/exchange_rate.ex @@ -5,7 +5,6 @@ defmodule Explorer.Chain.Supply.ExchangeRate do use Explorer.Chain.Supply - alias Explorer.ExchangeRates.Token alias Explorer.Market def circulating do @@ -17,6 +16,6 @@ defmodule Explorer.Chain.Supply.ExchangeRate do end def exchange_rate do - Market.get_exchange_rate(Explorer.coin()) || Token.null() + Market.get_coin_exchange_rate() end end diff --git a/apps/explorer/lib/explorer/exchange_rates/token.ex b/apps/explorer/lib/explorer/exchange_rates/token.ex index 8aa6d3b721aa..1e26d3267e8a 100644 --- a/apps/explorer/lib/explorer/exchange_rates/token.ex +++ b/apps/explorer/lib/explorer/exchange_rates/token.ex @@ -18,16 +18,16 @@ defmodule Explorer.ExchangeRates.Token do * `:volume_24h_usd` - The volume from the last 24 hours in USD """ @type t :: %__MODULE__{ - available_supply: Decimal.t(), - total_supply: Decimal.t(), - btc_value: Decimal.t(), - id: String.t(), - last_updated: DateTime.t(), - market_cap_usd: Decimal.t(), - name: String.t(), - symbol: String.t(), - usd_value: Decimal.t(), - volume_24h_usd: Decimal.t() + available_supply: Decimal.t() | nil, + total_supply: Decimal.t() | nil, + btc_value: Decimal.t() | nil, + id: String.t() | nil, + last_updated: DateTime.t() | nil, + market_cap_usd: Decimal.t() | nil, + name: String.t() | nil, + symbol: String.t() | nil, + usd_value: Decimal.t() | nil, + volume_24h_usd: Decimal.t() | nil } @derive Jason.Encoder diff --git a/apps/explorer/lib/explorer/market/history/cataloger.ex b/apps/explorer/lib/explorer/market/history/cataloger.ex index 8545ffa159ba..41e9468a6422 100644 --- a/apps/explorer/lib/explorer/market/history/cataloger.ex +++ b/apps/explorer/lib/explorer/market/history/cataloger.ex @@ -28,38 +28,67 @@ defmodule Explorer.Market.History.Cataloger do @typep milliseconds :: non_neg_integer() + @market_cap_failed_attempts 3 + @impl GenServer def init(:ok) do - send(self(), {:fetch_history, 365}) + send(self(), {:fetch_price_history, 365}) {:ok, %{}} end @impl GenServer - def handle_info({:fetch_history, day_count}, state) do - fetch_history(day_count) + def handle_info({:fetch_price_history, day_count}, state) do + fetch_price_history(day_count) + + {:noreply, state} + end + + @impl GenServer + def handle_info(:fetch_market_cap_history, state) do + fetch_market_cap_history() {:noreply, state} end @impl GenServer # Record fetch successful. - def handle_info({_ref, {_, _, {:ok, records}}}, state) do - Market.bulk_insert_history(records) + def handle_info({_ref, {:price_history, {_, _, {:ok, records}}}}, state) do + Process.send(self(), :fetch_market_cap_history, []) + state = state |> Map.put_new(:price_records, records) - # Schedule next check for history - fetch_after = config_or_default(:history_fetch_interval, :timer.minutes(60)) - Process.send_after(self(), {:fetch_history, 1}, fetch_after) + {:noreply, state |> Map.put_new(:price_records, state)} + end + + @impl GenServer + # Record fetch successful. + def handle_info({_ref, {:market_cap_history, {_, {:ok, market_cap_records}}}}, state) do + records = compile_records(state.price_records, market_cap_records) + market_cap_history(records, state) + end + + @impl GenServer + # Record fetch successful. + def handle_info({:market_cap_history, {_, {:ok, nil}}}, state) do + market_cap_history(state.price_records, state) + end + + # Failed to get records. Try again. + @impl GenServer + def handle_info({_ref, {:price_history, {day_count, failed_attempts, :error}}}, state) do + Logger.warn(fn -> "Failed to fetch price history. Trying again." end) + + fetch_price_history(day_count, failed_attempts + 1) {:noreply, state} end # Failed to get records. Try again. @impl GenServer - def handle_info({_ref, {day_count, failed_attempts, :error}}, state) do - Logger.warn(fn -> "Failed to fetch market history. Trying again." end) + def handle_info({_ref, {:market_cap_history, {failed_attempts, :error}}}, state) do + Logger.warn(fn -> "Failed to fetch market cap history. Trying again." end) - fetch_history(day_count, failed_attempts + 1) + fetch_market_cap_history(failed_attempts + 1) {:noreply, state} end @@ -78,6 +107,16 @@ defmodule Explorer.Market.History.Cataloger do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) end + defp market_cap_history(records, state) do + Market.bulk_insert_history(records) + + # Schedule next check for history + fetch_after = config_or_default(:history_fetch_interval, :timer.seconds(10)) + Process.send_after(self(), {:fetch_price_history, 1}, fetch_after) + + {:noreply, state} + end + @spec base_backoff :: milliseconds() defp base_backoff do config_or_default(:base_backoff, 100) @@ -88,19 +127,56 @@ defmodule Explorer.Market.History.Cataloger do Application.get_env(:explorer, __MODULE__, [])[key] || default end - @spec source() :: module() - defp source do - config_or_default(:source, Explorer.Market.History.Source.CryptoCompare) + @spec source_price() :: module() + defp source_price do + config_or_default(:source, Explorer.Market.History.Source.Price.CryptoCompare) end - @spec fetch_history(non_neg_integer(), non_neg_integer()) :: Task.t() - defp fetch_history(day_count, failed_attempts \\ 0) do + @spec source_market_cap() :: module() + defp source_market_cap do + config_or_default(:source_market_cap, Explorer.Market.History.Source.MarketCap.CoinGecko) + end + + @spec fetch_price_history(non_neg_integer(), non_neg_integer()) :: Task.t() + defp fetch_price_history(day_count, failed_attempts \\ 0) do Task.Supervisor.async_nolink(Explorer.MarketTaskSupervisor, fn -> Process.sleep(delay(failed_attempts)) - {day_count, failed_attempts, source().fetch_history(day_count)} + {:price_history, {day_count, failed_attempts, source_price().fetch_price_history(day_count)}} end) end + @spec fetch_market_cap_history(non_neg_integer()) :: Task.t() + defp fetch_market_cap_history(failed_attempts \\ 0) do + Task.Supervisor.async_nolink(Explorer.MarketTaskSupervisor, fn -> + Process.sleep(delay(failed_attempts)) + + if failed_attempts < @market_cap_failed_attempts do + {:market_cap_history, {failed_attempts, source_market_cap().fetch_market_cap()}} + else + {:market_cap_history, {failed_attempts, {:ok, nil}}} + end + end) + end + + defp compile_records(results_price, results_market_cap) do + if results_market_cap do + today_index = + Enum.find_index(results_price, fn price -> + price.date == results_market_cap.date + end) + + today = + results_price + |> Enum.at(today_index) + |> Map.put(:market_cap, results_market_cap.market_cap) + + results_price + |> List.replace_at(today_index, today) + else + results_price + end + end + @spec delay(non_neg_integer()) :: milliseconds() defp delay(0), do: 0 defp delay(1), do: base_backoff() diff --git a/apps/explorer/lib/explorer/market/history/source/market_cap.ex b/apps/explorer/lib/explorer/market/history/source/market_cap.ex new file mode 100644 index 000000000000..f79e2d12c035 --- /dev/null +++ b/apps/explorer/lib/explorer/market/history/source/market_cap.ex @@ -0,0 +1,18 @@ +defmodule Explorer.Market.History.Source.MarketCap do + @moduledoc """ + Interface for a source that allows for fetching of market cap history. + """ + + @typedoc """ + Record of market values for a specific date. + """ + @type record :: %{ + date: Date.t(), + market_cap: Decimal.t() + } + + @doc """ + Fetch history for a specified amount of days in the past. + """ + @callback fetch_market_cap() :: {:ok, [record()]} | :error +end diff --git a/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex new file mode 100644 index 000000000000..1097e9430d9c --- /dev/null +++ b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex @@ -0,0 +1,60 @@ +defmodule Explorer.Market.History.Source.MarketCap.CoinGecko do + @moduledoc """ + Adapter for fetching market history from CoinGecko. + + The history is fetched for the configured coin. You can specify a + different coin by changing the targeted coin. + + # In config.exs + config :explorer, coin: "POA" + + """ + + alias Explorer.ExchangeRates.Source + alias Explorer.ExchangeRates.Source.CoinGecko, as: ExchangeRatesSourceCoinGecko + alias Explorer.Market.History.Source.MarketCap, as: SourceMarketCap + + @behaviour SourceMarketCap + + @impl SourceMarketCap + def fetch_market_cap do + url = ExchangeRatesSourceCoinGecko.source_url() + + if url do + case Source.http_request(url, ExchangeRatesSourceCoinGecko.headers()) do + {:ok, data} -> + result = + data + |> format_data() + + {:ok, result} + + _ -> + :error + end + else + :error + end + end + + @spec date(String.t()) :: Date.t() + defp date(date_time_string) do + with {:ok, datetime, _} <- DateTime.from_iso8601(date_time_string) do + datetime + |> DateTime.to_date() + end + end + + @spec format_data(term()) :: [SourceMarketCap.record()] | nil + defp format_data(nil), do: nil + + defp format_data(data) do + market_data = data["market_data"] + market_cap = market_data["market_cap"] + + %{ + market_cap: Decimal.new(to_string(market_cap["usd"])), + date: date(data["last_updated"]) + } + end +end diff --git a/apps/explorer/lib/explorer/market/history/source.ex b/apps/explorer/lib/explorer/market/history/source/price.ex similarity index 58% rename from apps/explorer/lib/explorer/market/history/source.ex rename to apps/explorer/lib/explorer/market/history/source/price.ex index af1c96e4bada..07924c1fca3a 100644 --- a/apps/explorer/lib/explorer/market/history/source.ex +++ b/apps/explorer/lib/explorer/market/history/source/price.ex @@ -1,6 +1,6 @@ -defmodule Explorer.Market.History.Source do +defmodule Explorer.Market.History.Source.Price do @moduledoc """ - Interface for a source that allows for fetching of market history. + Interface for a source that allows for fetching of coin price. """ @typedoc """ @@ -15,5 +15,5 @@ defmodule Explorer.Market.History.Source do @doc """ Fetch history for a specified amount of days in the past. """ - @callback fetch_history(previous_days :: non_neg_integer()) :: {:ok, [record()]} | :error + @callback fetch_price_history(previous_days :: non_neg_integer()) :: {:ok, [record()]} | :error end diff --git a/apps/explorer/lib/explorer/market/history/source/crypto_compare.ex b/apps/explorer/lib/explorer/market/history/source/price/crypto_compare.ex similarity index 86% rename from apps/explorer/lib/explorer/market/history/source/crypto_compare.ex rename to apps/explorer/lib/explorer/market/history/source/price/crypto_compare.ex index 6888d83b2b67..76693d37063d 100644 --- a/apps/explorer/lib/explorer/market/history/source/crypto_compare.ex +++ b/apps/explorer/lib/explorer/market/history/source/price/crypto_compare.ex @@ -1,4 +1,4 @@ -defmodule Explorer.Market.History.Source.CryptoCompare do +defmodule Explorer.Market.History.Source.Price.CryptoCompare do @moduledoc """ Adapter for fetching market history from https://cryptocompare.com. @@ -10,15 +10,15 @@ defmodule Explorer.Market.History.Source.CryptoCompare do """ - alias Explorer.Market.History.Source + alias Explorer.Market.History.Source.Price, as: SourcePrice alias HTTPoison.Response - @behaviour Source + @behaviour SourcePrice @typep unix_timestamp :: non_neg_integer() - @impl Source - def fetch_history(previous_days) do + @impl SourcePrice + def fetch_price_history(previous_days) do url = history_url(previous_days) headers = [{"Content-Type", "application/json"}] @@ -49,7 +49,7 @@ defmodule Explorer.Market.History.Source.CryptoCompare do |> DateTime.to_date() end - @spec format_data(String.t()) :: [Source.record()] + @spec format_data(String.t()) :: [SourcePrice.record()] defp format_data(data) do json = Jason.decode!(data) diff --git a/apps/explorer/lib/explorer/market/market.ex b/apps/explorer/lib/explorer/market/market.ex index 466c6c56170f..ef5b0b48cded 100644 --- a/apps/explorer/lib/explorer/market/market.ex +++ b/apps/explorer/lib/explorer/market/market.ex @@ -7,14 +7,6 @@ defmodule Explorer.Market do alias Explorer.Market.{MarketHistory, MarketHistoryCache} alias Explorer.{ExchangeRates, Repo} - @doc """ - Get most recent exchange rate for the given symbol. - """ - @spec get_exchange_rate(String.t()) :: Token.t() | nil - def get_exchange_rate(symbol) do - ExchangeRates.lookup(symbol) - end - @doc """ Retrieves the history for the recent specified amount of days. @@ -28,7 +20,7 @@ defmodule Explorer.Market do @doc """ Retrieves today's native coin exchange rate from the database. """ - @spec get_native_coin_exchange_rate_from_db() :: Token.t() | nil + @spec get_native_coin_exchange_rate_from_db() :: Token.t() def get_native_coin_exchange_rate_from_db do today = case fetch_recent_history() do @@ -37,12 +29,31 @@ defmodule Explorer.Market do end if today do - Map.get(today, :closing_price) + %Token{ + usd_value: Map.get(today, :closing_price), + market_cap_usd: Map.get(today, :market_cap), + available_supply: nil, + total_supply: nil, + btc_value: nil, + id: nil, + last_updated: nil, + name: nil, + symbol: nil, + volume_24h_usd: nil + } else - nil + Token.null() end end + @doc """ + Get most recent exchange rate for the given symbol from ETS or from DB. + """ + @spec get_coin_exchange_rate() :: Token.t() | nil + def get_coin_exchange_rate do + get_exchange_rate(Explorer.coin()) || get_native_coin_exchange_rate_from_db() || Token.null() + end + @doc false def bulk_insert_history(records) do records_without_zeroes = @@ -55,4 +66,9 @@ defmodule Explorer.Market do Repo.insert_all(MarketHistory, records_without_zeroes, on_conflict: :nothing, conflict_target: [:date]) end + + @spec get_exchange_rate(String.t()) :: Token.t() | nil + defp get_exchange_rate(symbol) do + ExchangeRates.lookup(symbol) + end end diff --git a/apps/explorer/lib/explorer/market/market_history.ex b/apps/explorer/lib/explorer/market/market_history.ex index 10850177b7c5..aca309062540 100644 --- a/apps/explorer/lib/explorer/market/market_history.ex +++ b/apps/explorer/lib/explorer/market/market_history.ex @@ -9,6 +9,7 @@ defmodule Explorer.Market.MarketHistory do field(:closing_price, :decimal) field(:date, :date) field(:opening_price, :decimal) + field(:market_cap, :decimal) end @typedoc """ @@ -17,10 +18,12 @@ defmodule Explorer.Market.MarketHistory do * `:closing_price` - Closing price in USD. * `:date` - The date in UTC. * `:opening_price` - Opening price in USD. + * `:market_cap` - Market cap in USD. """ @type t :: %__MODULE__{ closing_price: Decimal.t(), date: Date.t(), - opening_price: Decimal.t() + opening_price: Decimal.t(), + market_cap: Decimal.t() } end diff --git a/apps/explorer/priv/repo/migrations/20230530074105_market_history_add_market_cap.exs b/apps/explorer/priv/repo/migrations/20230530074105_market_history_add_market_cap.exs new file mode 100644 index 000000000000..b0ddaca92c21 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230530074105_market_history_add_market_cap.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Migrations.MarketHistoryAddMarketCap do + use Ecto.Migration + + def change do + alter table(:market_history) do + add(:market_cap, :decimal) + end + end +end diff --git a/apps/explorer/test/explorer/market/history/cataloger_test.exs b/apps/explorer/test/explorer/market/history/cataloger_test.exs index 433ad0fc1e5c..0acc04a7f24b 100644 --- a/apps/explorer/test/explorer/market/history/cataloger_test.exs +++ b/apps/explorer/test/explorer/market/history/cataloger_test.exs @@ -5,7 +5,7 @@ defmodule Explorer.Market.History.CatalogerTest do alias Explorer.Market.MarketHistory alias Explorer.Market.History.Cataloger - alias Explorer.Market.History.Source.TestSource + alias Explorer.Market.History.Source.Price.TestSource alias Explorer.Repo setup do @@ -15,27 +15,58 @@ defmodule Explorer.Market.History.CatalogerTest do test "init" do assert {:ok, %{}} == Cataloger.init(:ok) - assert_received {:fetch_history, 365} + assert_received {:fetch_price_history, 365} end - test "handle_info with `{:fetch_history, days}`" do + test "handle_info with `{:fetch_price_history, days}`" do records = [%{date: ~D[2018-04-01], closing_price: Decimal.new(10), opening_price: Decimal.new(5)}] - expect(TestSource, :fetch_history, fn 1 -> {:ok, records} end) + expect(TestSource, :fetch_price_history, fn 1 -> {:ok, records} end) set_mox_global() state = %{} - assert {:noreply, state} == Cataloger.handle_info({:fetch_history, 1}, state) - assert_receive {_ref, {1, 0, {:ok, ^records}}} + assert {:noreply, state} == Cataloger.handle_info({:fetch_price_history, 1}, state) + assert_receive {_ref, {:price_history, {1, 0, {:ok, ^records}}}} end - test "handle_info with successful task" do + test "handle_info with successful tasks (price and market cap)" do Application.put_env(:explorer, Cataloger, history_fetch_interval: 1) - record = %{date: ~D[2018-04-01], closing_price: Decimal.new(10), opening_price: Decimal.new(5)} - state = %{} + record_price = %{date: ~D[2018-04-01], closing_price: Decimal.new(10), opening_price: Decimal.new(5)} + record_market_cap = %{date: ~D[2018-04-01], market_cap: Decimal.new(100_500)} + + state = %{ + price_records: [ + record_price + ] + } + + assert {:noreply, state} == Cataloger.handle_info({nil, {:price_history, {1, 0, {:ok, [record_price]}}}}, state) + assert_receive :fetch_market_cap_history + + assert {:noreply, state} == + Cataloger.handle_info({nil, {:market_cap_history, {0, {:ok, record_market_cap}}}}, state) + + assert Repo.get_by(MarketHistory, date: record_price.date) + end + + test "handle_info with successful price task" do + Application.put_env(:explorer, Cataloger, history_fetch_interval: 1) + record_price = %{date: ~D[2018-04-01], closing_price: Decimal.new(10), opening_price: Decimal.new(5)} + record_market_cap = nil + + state = %{ + price_records: [ + record_price + ] + } + + assert {:noreply, state} == Cataloger.handle_info({nil, {:price_history, {1, 0, {:ok, [record_price]}}}}, state) + assert_receive :fetch_market_cap_history + + assert {:noreply, state} == + Cataloger.handle_info({nil, {:market_cap_history, {0, {:ok, record_market_cap}}}}, state) - assert {:noreply, state} == Cataloger.handle_info({nil, {1, 0, {:ok, [record]}}}, state) - assert_receive {:fetch_history, 1} - assert Repo.get_by(MarketHistory, date: record.date) + assert record = Repo.get_by(MarketHistory, date: record_price.date) + assert record.market_cap == nil end test "handle info for DOWN message" do diff --git a/apps/explorer/test/explorer/market/history/source/crypto_compare_test.exs b/apps/explorer/test/explorer/market/history/source/price/crypto_compare_test.exs similarity index 90% rename from apps/explorer/test/explorer/market/history/source/crypto_compare_test.exs rename to apps/explorer/test/explorer/market/history/source/price/crypto_compare_test.exs index 1fc4223f7262..2d8311b0ae1f 100644 --- a/apps/explorer/test/explorer/market/history/source/crypto_compare_test.exs +++ b/apps/explorer/test/explorer/market/history/source/price/crypto_compare_test.exs @@ -1,7 +1,7 @@ -defmodule Explorer.Market.History.Source.CryptoCompareTest do +defmodule Explorer.Market.History.Source.Price.CryptoCompareTest do use ExUnit.Case, async: false - alias Explorer.Market.History.Source.CryptoCompare + alias Explorer.Market.History.Source.Price.CryptoCompare alias Plug.Conn @json """ @@ -48,7 +48,7 @@ defmodule Explorer.Market.History.Source.CryptoCompareTest do } """ - describe "fetch_history/1" do + describe "fetch_price_history/1" do setup do bypass = Bypass.open() Application.put_env(:explorer, CryptoCompare, base_url: "http://localhost:#{bypass.port}") @@ -77,14 +77,14 @@ defmodule Explorer.Market.History.Source.CryptoCompareTest do } ] - assert {:ok, expected} == CryptoCompare.fetch_history(3) + assert {:ok, expected} == CryptoCompare.fetch_price_history(3) end test "with errored request", %{bypass: bypass} do error_text = ~S({"error": "server error"}) Bypass.expect(bypass, fn conn -> Conn.resp(conn, 500, error_text) end) - assert :error == CryptoCompare.fetch_history(3) + assert :error == CryptoCompare.fetch_price_history(3) end test "rejects empty prices", %{bypass: bypass} do @@ -138,7 +138,7 @@ defmodule Explorer.Market.History.Source.CryptoCompareTest do %{closing_price: Decimal.from_float(8804.32), date: ~D[2018-04-26], opening_price: Decimal.from_float(8873.57)} ] - assert {:ok, expected} == CryptoCompare.fetch_history(3) + assert {:ok, expected} == CryptoCompare.fetch_price_history(3) end end end diff --git a/apps/explorer/test/test_helper.exs b/apps/explorer/test/test_helper.exs index 849fa0b6ae12..5f07fdcc59e0 100644 --- a/apps/explorer/test/test_helper.exs +++ b/apps/explorer/test/test_helper.exs @@ -15,7 +15,7 @@ Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, :auto) Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Account, :auto) Mox.defmock(Explorer.ExchangeRates.Source.TestSource, for: Explorer.ExchangeRates.Source) -Mox.defmock(Explorer.Market.History.Source.TestSource, for: Explorer.Market.History.Source) +Mox.defmock(Explorer.Market.History.Source.Price.TestSource, for: Explorer.Market.History.Source.Price) Mox.defmock(Explorer.History.TestHistorian, for: Explorer.History.Historian) Mox.defmock(EthereumJSONRPC.Mox, for: EthereumJSONRPC.Transport) From eeabc44b8b3ac8ce66537eddb60172f416b4cc35 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Fri, 16 Jun 2023 13:07:36 +0300 Subject: [PATCH 2/8] Fix reviwer comments --- .../api/v2/transaction_controller_test.exs | 27 ------------------- .../history/source/market_cap/coin_gecko.ex | 4 +-- apps/explorer/lib/explorer/market/market.ex | 2 +- 3 files changed, 3 insertions(+), 30 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index 4f4f12f8ff57..14429b489b4f 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -964,33 +964,6 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do assert Address.checksum(transaction.to_address_hash) == json["to"]["hash"] end - defp compare_item(%InternalTransaction{} = internal_tx, json) do - assert internal_tx.block_number == json["block"] - assert to_string(internal_tx.gas) == json["gas_limit"] - assert internal_tx.index == json["index"] - assert to_string(internal_tx.transaction_hash) == json["transaction_hash"] - assert Address.checksum(internal_tx.from_address_hash) == json["from"]["hash"] - assert Address.checksum(internal_tx.to_address_hash) == json["to"]["hash"] - end - - defp compare_item(%Log{} = log, json) do - assert to_string(log.data) == json["data"] - assert log.index == json["index"] - assert Address.checksum(log.address_hash) == json["address"]["hash"] - assert to_string(log.transaction_hash) == json["tx_hash"] - end - - defp compare_item(%TokenTransfer{} = token_transfer, json) do - assert Address.checksum(token_transfer.from_address_hash) == json["from"]["hash"] - assert Address.checksum(token_transfer.to_address_hash) == json["to"]["hash"] - assert to_string(token_transfer.transaction_hash) == json["tx_hash"] - assert json["timestamp"] == nil - assert json["method"] == nil - assert to_string(token_transfer.block_hash) == json["block_hash"] - assert to_string(token_transfer.log_index) == json["log_index"] - assert check_total(Repo.preload(token_transfer, [{:token, :contract_address}]).token, json["total"], token_transfer) - end - defp compare_item(%Transaction{} = transaction, json, wl_names) do assert to_string(transaction.hash) == json["hash"] assert transaction.block_number == json["block"] diff --git a/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex index 1097e9430d9c..63ebc30c64a4 100644 --- a/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex +++ b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex @@ -1,8 +1,8 @@ defmodule Explorer.Market.History.Source.MarketCap.CoinGecko do @moduledoc """ - Adapter for fetching market history from CoinGecko. + Adapter for fetching current market from CoinGecko. - The history is fetched for the configured coin. You can specify a + The current market is fetched for the configured coin. You can specify a different coin by changing the targeted coin. # In config.exs diff --git a/apps/explorer/lib/explorer/market/market.ex b/apps/explorer/lib/explorer/market/market.ex index ef5b0b48cded..b2b9f1cb38ce 100644 --- a/apps/explorer/lib/explorer/market/market.ex +++ b/apps/explorer/lib/explorer/market/market.ex @@ -47,7 +47,7 @@ defmodule Explorer.Market do end @doc """ - Get most recent exchange rate for the given symbol from ETS or from DB. + Get most recent exchange rate for the native coin from ETS or from DB. """ @spec get_coin_exchange_rate() :: Token.t() | nil def get_coin_exchange_rate do From f5bc31fc47a4017cbb7c4eca592a98dcf176a0b1 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 19 Jun 2023 11:21:24 +0300 Subject: [PATCH 3/8] Fix reviewer comment --- .../block_scout_web/controllers/api/rpc/stats_controller.ex | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex index e8f829bbec14..19fcf8768c64 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex @@ -3,8 +3,7 @@ defmodule BlockScoutWeb.API.RPC.StatsController do use Explorer.Schema - alias Explorer - alias Explorer.{Chain, Etherscan, ExchangeRates} + alias Explorer.{Chain, Etherscan, Market} alias Explorer.Chain.Cache.{AddressSum, AddressSumMinusBurnt} alias Explorer.Chain.Wei @@ -61,8 +60,7 @@ defmodule BlockScoutWeb.API.RPC.StatsController do end def coinprice(conn, _params) do - symbol = Explorer.coin() - rates = ExchangeRates.lookup(symbol) + rates = Market.get_coin_exchange_rate() render(conn, "coinprice.json", rates: rates) end From 4ef68f14bfaa155f283de866133958849e3b93d9 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 19 Jun 2023 12:57:00 +0300 Subject: [PATCH 4/8] Fix reviewer comments --- .../lib/explorer/market/history/cataloger.ex | 54 +++++++++++-------- .../history/source/market_cap/coin_gecko.ex | 2 +- apps/explorer/lib/explorer/market/market.ex | 4 +- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/apps/explorer/lib/explorer/market/history/cataloger.ex b/apps/explorer/lib/explorer/market/history/cataloger.ex index 41e9468a6422..0672412d3311 100644 --- a/apps/explorer/lib/explorer/market/history/cataloger.ex +++ b/apps/explorer/lib/explorer/market/history/cataloger.ex @@ -28,6 +28,7 @@ defmodule Explorer.Market.History.Cataloger do @typep milliseconds :: non_neg_integer() + @price_failed_attempts 10 @market_cap_failed_attempts 3 @impl GenServer @@ -62,15 +63,15 @@ defmodule Explorer.Market.History.Cataloger do @impl GenServer # Record fetch successful. - def handle_info({_ref, {:market_cap_history, {_, {:ok, market_cap_records}}}}, state) do - records = compile_records(state.price_records, market_cap_records) - market_cap_history(records, state) + def handle_info({_ref, {:market_cap_history, {_, {:ok, nil}}}}, state) do + market_cap_history(state.price_records, state) end @impl GenServer # Record fetch successful. - def handle_info({:market_cap_history, {_, {:ok, nil}}}, state) do - market_cap_history(state.price_records, state) + def handle_info({_ref, {:market_cap_history, {_, {:ok, market_cap_record}}}}, state) do + records = compile_records(state.price_records, market_cap_record) + market_cap_history(records, state) end # Failed to get records. Try again. @@ -111,7 +112,7 @@ defmodule Explorer.Market.History.Cataloger do Market.bulk_insert_history(records) # Schedule next check for history - fetch_after = config_or_default(:history_fetch_interval, :timer.seconds(10)) + fetch_after = config_or_default(:history_fetch_interval, :timer.minutes(60)) Process.send_after(self(), {:fetch_price_history, 1}, fetch_after) {:noreply, state} @@ -141,7 +142,12 @@ defmodule Explorer.Market.History.Cataloger do defp fetch_price_history(day_count, failed_attempts \\ 0) do Task.Supervisor.async_nolink(Explorer.MarketTaskSupervisor, fn -> Process.sleep(delay(failed_attempts)) - {:price_history, {day_count, failed_attempts, source_price().fetch_price_history(day_count)}} + + if failed_attempts < @price_failed_attempts do + {:price_history, {day_count, failed_attempts, source_price().fetch_price_history(day_count)}} + else + {:price_history, {day_count, failed_attempts, {:ok, []}}} + end end) end @@ -158,22 +164,26 @@ defmodule Explorer.Market.History.Cataloger do end) end - defp compile_records(results_price, results_market_cap) do - if results_market_cap do - today_index = - Enum.find_index(results_price, fn price -> - price.date == results_market_cap.date - end) - - today = - results_price - |> Enum.at(today_index) - |> Map.put(:market_cap, results_market_cap.market_cap) - - results_price - |> List.replace_at(today_index, today) + defp compile_records(price_records, market_cap_record) do + if market_cap_record do + if Enum.empty?(price_records) do + [market_cap_record] + else + today_index = + Enum.find_index(price_records, fn price -> + price.date == market_cap_record.date + end) + + today = + price_records + |> Enum.at(today_index) + |> Map.put(:market_cap, market_cap_record.market_cap) + + price_records + |> List.replace_at(today_index, today) + end else - results_price + price_records end end diff --git a/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex index 63ebc30c64a4..9875f0d0a663 100644 --- a/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex +++ b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex @@ -45,7 +45,7 @@ defmodule Explorer.Market.History.Source.MarketCap.CoinGecko do end end - @spec format_data(term()) :: [SourceMarketCap.record()] | nil + @spec format_data(term()) :: SourceMarketCap.record() | nil defp format_data(nil), do: nil defp format_data(data) do diff --git a/apps/explorer/lib/explorer/market/market.ex b/apps/explorer/lib/explorer/market/market.ex index b2b9f1cb38ce..083f39403c94 100644 --- a/apps/explorer/lib/explorer/market/market.ex +++ b/apps/explorer/lib/explorer/market/market.ex @@ -59,7 +59,9 @@ defmodule Explorer.Market do records_without_zeroes = records |> Enum.reject(fn item -> - Decimal.equal?(item.closing_price, 0) && Decimal.equal?(item.opening_price, 0) + Map.has_key?(item, :opening_price) && Map.has_key?(item, :closing_price) && + Decimal.equal?(item.closing_price, 0) && + Decimal.equal?(item.opening_price, 0) end) # Enforce MarketHistory ShareLocks order (see docs: sharelocks.md) |> Enum.sort_by(& &1.date) From 60a0d1be0d3dfaa7db1475ca064373540cb0fabb Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 19 Jun 2023 15:38:39 +0300 Subject: [PATCH 5/8] Process reviwer comments --- .../assets/js/lib/history_chart.js | 29 ++++++------------- .../controllers/api/v2/stats_controller.ex | 12 +++++--- .../chain/market_history_chart_controller.ex | 26 ++++++++++++----- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/apps/block_scout_web/assets/js/lib/history_chart.js b/apps/block_scout_web/assets/js/lib/history_chart.js index b8f451d72cba..043e6c71f539 100644 --- a/apps/block_scout_web/assets/js/lib/history_chart.js +++ b/apps/block_scout_web/assets/js/lib/history_chart.js @@ -14,6 +14,7 @@ Chart.register(LineController, LineElement, PointElement, LinearScale, TimeScale // @ts-ignore const coinName = document.getElementById('js-coin-name').value +// @ts-ignore const chainId = document.getElementById('js-chain-id').value const priceDataKey = `priceData${coinName}` const txHistoryDataKey = `txHistoryData${coinName}${chainId}` @@ -188,15 +189,12 @@ function getTxHistoryData (transactionHistory) { return data } -function getMarketCapData (marketHistoryData, availableSupply) { +function getMarketCapData (marketHistoryData) { if (marketHistoryData.length === 0) { return getDataFromLocalStorage(marketCapDataKey) } - const data = marketHistoryData.map(({ date, closingPrice }) => { - const supply = (availableSupply !== null && typeof availableSupply === 'object') - ? availableSupply[date] - : availableSupply - return { x: date, y: closingPrice * supply } + const data = marketHistoryData.map(({ date, marketCap }) => { + return { x: date, y: marketCap } }) setDataToLocalStorage(marketCapDataKey, data) return data @@ -207,7 +205,7 @@ const priceLineColor = getPriceChartColor() const mcapLineColor = getMarketCapChartColor() class MarketHistoryChart { - constructor (el, availableSupply, _marketHistoryData, dataConfig) { + constructor (el, _marketHistoryData, dataConfig) { const axes = config.options.scales let priceActivated = true @@ -271,8 +269,6 @@ class MarketHistoryChart { axes.numTransactions.position = 'left' } - this.availableSupply = availableSupply - const txChartTitle = 'Daily transactions' const marketChartTitle = 'Daily price and market cap' let chartTitle = '' @@ -297,15 +293,9 @@ class MarketHistoryChart { this.chart = new Chart(el, config) } - updateMarketHistory (availableSupply, marketHistoryData) { + updateMarketHistory (marketHistoryData) { this.price.data = getPriceData(marketHistoryData) - if (this.availableSupply !== null && typeof this.availableSupply === 'object') { - const today = new Date().toJSON().slice(0, 10) - this.availableSupply[today] = availableSupply - this.marketCap.data = getMarketCapData(marketHistoryData, this.availableSupply) - } else { - this.marketCap.data = getMarketCapData(marketHistoryData, availableSupply) - } + this.marketCap.data = getMarketCapData(marketHistoryData) this.chart.update() } @@ -320,17 +310,16 @@ export function createMarketHistoryChart (el) { const dataConfig = $(el).data('history_chart_config') const $chartError = $('[data-chart-error-message]') - const chart = new MarketHistoryChart(el, 0, [], dataConfig) + const chart = new MarketHistoryChart(el, [], dataConfig) Object.keys(dataPaths).forEach(function (historySource) { $.getJSON(dataPaths[historySource], { type: 'JSON' }) .done(data => { switch (historySource) { case 'market': { - const availableSupply = JSON.parse(data.supply_data) const marketHistoryData = humps.camelizeKeys(JSON.parse(data.history_data)) $(el).show() - chart.updateMarketHistory(availableSupply, marketHistoryData) + chart.updateMarketHistory(marketHistoryData) break } case 'transaction': { diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex index 3d93c7b83d1b..801c75822d87 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do use Phoenix.Controller alias BlockScoutWeb.API.V2.Helper + alias BlockScoutWeb.Chain.MarketHistoryChartController alias Explorer.{Chain, Market} alias Explorer.Chain.Cache.Block, as: BlockCache alias Explorer.Chain.Cache.{GasPriceOracle, GasUsage} @@ -93,8 +94,9 @@ defmodule BlockScoutWeb.API.V2.StatsController do exchange_rate = Market.get_coin_exchange_rate() recent_market_history = Market.fetch_recent_history() + current_total_supply = available_supply(Chain.supply_for_days(), exchange_rate) - market_history_data = + price_history_data = recent_market_history |> case do [today | the_rest] -> @@ -109,11 +111,13 @@ defmodule BlockScoutWeb.API.V2.StatsController do data -> data end - |> Enum.map(fn day -> Map.take(day, [:closing_price, :date]) end) + |> Enum.map(fn day -> Map.take(day, [:closing_price, :market_cap, :date]) end) + + market_history_data = + MarketHistoryChartController.encode_market_history_data(price_history_data, current_total_supply) json(conn, %{ - chart_data: market_history_data, - available_supply: available_supply(Chain.supply_for_days(), exchange_rate) + chart_data: market_history_data }) end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex index 467a87426bfc..a871db7a63c5 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex @@ -8,19 +8,21 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do exchange_rate = Market.get_coin_exchange_rate() recent_market_history = Market.fetch_recent_history() + current_total_supply = available_supply(Chain.supply_for_days(), exchange_rate) - market_history_data = + price_history_data = case recent_market_history do [today | the_rest] -> - encode_market_history_data([%{today | closing_price: exchange_rate.usd_value} | the_rest]) + [%{today | closing_price: exchange_rate.usd_value} | the_rest] data -> - encode_market_history_data(data) + data end + market_history_data = encode_market_history_data(price_history_data, current_total_supply) + json(conn, %{ - history_data: market_history_data, - supply_data: available_supply(Chain.supply_for_days(), exchange_rate) + history_data: market_history_data }) else unprocessable_entity(conn) @@ -40,9 +42,19 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do end end - defp encode_market_history_data(market_history_data) do + def encode_market_history_data(market_history_data, current_total_supply) when is_binary(current_total_supply) do + encode_market_history_data(market_history_data, Decimal.new(current_total_supply)) + end + + def encode_market_history_data(market_history_data, current_total_supply) do market_history_data - |> Enum.map(fn day -> Map.take(day, [:closing_price, :date]) end) + |> Enum.map(fn day -> + market_cap = if day.market_cap, do: day.market_cap, else: Decimal.mult(current_total_supply, day.closing_price) + + day + |> Map.put(:market_cap, market_cap) + |> Map.take([:closing_price, :market_cap, :date]) + end) |> Jason.encode() |> case do {:ok, data} -> data From fd7f31c6e3cb8f8913add9ba1282f21e60eca924 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 19 Jun 2023 16:14:40 +0300 Subject: [PATCH 6/8] Fix failing incompatible test --- .../controllers/api/v2/stats_controller_test.exs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/stats_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/stats_controller_test.exs index 1c1659791fd5..52ef45f947c0 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/stats_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/stats_controller_test.exs @@ -41,8 +41,7 @@ defmodule BlockScoutWeb.API.V2.StatsControllerTest do request = get(conn, "/api/v2/stats/charts/market") assert response = json_response(request, 200) - assert response["chart_data"] == [] - assert response["available_supply"] == 0 + assert response["chart_data"] == "[]" end end From 2d5e01c3a8fb4db2ea90f4c2ee529413ee19db0b Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 19 Jun 2023 17:41:11 +0300 Subject: [PATCH 7/8] Fix test --- .../controllers/chain/market_history_chart_controller.ex | 2 +- .../controllers/api/v2/stats_controller_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex index a871db7a63c5..5ab997f4a91e 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex @@ -57,7 +57,7 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do end) |> Jason.encode() |> case do - {:ok, data} -> data + {:ok, data} -> Jason.decode!(data) _ -> [] end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/stats_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/stats_controller_test.exs index 52ef45f947c0..85ac41e1d14d 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/stats_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/stats_controller_test.exs @@ -41,7 +41,7 @@ defmodule BlockScoutWeb.API.V2.StatsControllerTest do request = get(conn, "/api/v2/stats/charts/market") assert response = json_response(request, 200) - assert response["chart_data"] == "[]" + assert response["chart_data"] == [] end end From a94f054f70536b797b7f15aa45661f4a2dd95292 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 19 Jun 2023 20:05:37 +0300 Subject: [PATCH 8/8] Fix history_chart.js Return available_supply for backward compatibility --- apps/block_scout_web/assets/js/lib/history_chart.js | 2 +- .../block_scout_web/controllers/api/v2/stats_controller.ex | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/assets/js/lib/history_chart.js b/apps/block_scout_web/assets/js/lib/history_chart.js index 043e6c71f539..d861602128aa 100644 --- a/apps/block_scout_web/assets/js/lib/history_chart.js +++ b/apps/block_scout_web/assets/js/lib/history_chart.js @@ -316,7 +316,7 @@ export function createMarketHistoryChart (el) { .done(data => { switch (historySource) { case 'market': { - const marketHistoryData = humps.camelizeKeys(JSON.parse(data.history_data)) + const marketHistoryData = humps.camelizeKeys(data.history_data) $(el).show() chart.updateMarketHistory(marketHistoryData) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex index 801c75822d87..6ffbd0c6bbfe 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -117,7 +117,9 @@ defmodule BlockScoutWeb.API.V2.StatsController do MarketHistoryChartController.encode_market_history_data(price_history_data, current_total_supply) json(conn, %{ - chart_data: market_history_data + chart_data: market_history_data, + # todo: remove when new frontend is ready to use data from chart_data property only + available_supply: current_total_supply }) end