Add token balances info to watchlist address response (#7888)

* Add token balances info to watchlist address response

* Changelog

* Handle nil fiat_value
pull/7939/head
nikitosing 1 year ago committed by GitHub
parent 3ff97afbc4
commit 3a6c07cea0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 28
      apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex
  3. 5
      apps/block_scout_web/lib/block_scout_web/views/account/api/v1/user_view.ex
  4. 106
      apps/block_scout_web/test/block_scout_web/controllers/account/api/v1/user_controller_test.exs
  5. 3
      apps/explorer/lib/explorer/account/watchlist_address.ex
  6. 2
      apps/explorer/test/support/factory.ex

@ -4,6 +4,7 @@
### Features ### Features
- [#7888](https://github.com/blockscout/blockscout/pull/7888) - Add token balances info to watchlist address response
- [#7898](https://github.com/blockscout/blockscout/pull/7898) - Add possibility to add extra headers with JSON RPC URL - [#7898](https://github.com/blockscout/blockscout/pull/7898) - Add possibility to add extra headers with JSON RPC URL
- [#7836](https://github.com/blockscout/blockscout/pull/7836) - Improve unverified email flow - [#7836](https://github.com/blockscout/blockscout/pull/7836) - Improve unverified email flow
- [#7784](https://github.com/blockscout/blockscout/pull/7784) - Search improvements: Add new fields, light refactoring - [#7784](https://github.com/blockscout/blockscout/pull/7784) - Search improvements: Add new fields, light refactoring

@ -8,12 +8,13 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do
alias Explorer.Account.Api.Key, as: ApiKey alias Explorer.Account.Api.Key, as: ApiKey
alias Explorer.Account.CustomABI alias Explorer.Account.CustomABI
alias Explorer.Account.{Identity, PublicTagsRequest, TagAddress, TagTransaction, WatchlistAddress} alias Explorer.Account.{Identity, PublicTagsRequest, TagAddress, TagTransaction, WatchlistAddress}
alias Explorer.{Market, Repo} alias Explorer.{Chain, Market, PagingOptions, Repo}
alias Plug.CSRFProtection alias Plug.CSRFProtection
action_fallback(BlockScoutWeb.Account.Api.V1.FallbackController) action_fallback(BlockScoutWeb.Account.Api.V1.FallbackController)
@ok_message "OK" @ok_message "OK"
@token_balances_amount 150
def info(conn, _params) do def info(conn, _params) do
with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, with {:auth, %{id: uid}} <- {:auth, current_user(conn)},
@ -30,11 +31,34 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do
{:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, %{watchlists: [watchlist | _]}} <-
{:watchlist, Repo.account_repo().preload(identity, :watchlists)}, {:watchlist, Repo.account_repo().preload(identity, :watchlists)},
watchlist_with_addresses <- preload_watchlist_addresses(watchlist) do watchlist_with_addresses <- preload_watchlist_addresses(watchlist) do
watchlist_addresses =
Enum.map(watchlist_with_addresses.watchlist_addresses, fn wa ->
balances =
Chain.fetch_paginated_last_token_balances(wa.address_hash,
paging_options: %PagingOptions{page_size: @token_balances_amount + 1}
)
count = Enum.count(balances)
overflow? = count > @token_balances_amount
fiat_sum =
balances
|> Enum.take(@token_balances_amount)
|> Enum.reduce(Decimal.new(0), fn tb, acc -> Decimal.add(acc, tb.fiat_value || 0) end)
%WatchlistAddress{
wa
| tokens_fiat_value: fiat_sum,
tokens_count: min(count, @token_balances_amount),
tokens_overflow: overflow?
}
end)
conn conn
|> put_status(200) |> put_status(200)
|> render(:watchlist_addresses, %{ |> render(:watchlist_addresses, %{
exchange_rate: Market.get_coin_exchange_rate(), exchange_rate: Market.get_coin_exchange_rate(),
watchlist_addresses: watchlist_with_addresses.watchlist_addresses watchlist_addresses: watchlist_addresses
}) })
end end
end end

@ -102,7 +102,10 @@ defmodule BlockScoutWeb.Account.Api.V1.UserView do
}, },
"notification_methods" => %{ "notification_methods" => %{
"email" => watchlist.notify_email "email" => watchlist.notify_email
} },
"tokens_fiat_value" => watchlist.tokens_fiat_value,
"tokens_count" => watchlist.tokens_count,
"tokens_overflow" => watchlist.tokens_overflow
} }
end end

@ -1,6 +1,7 @@
defmodule BlockScoutWeb.Account.Api.V1.UserControllerTest do defmodule BlockScoutWeb.Account.Api.V1.UserControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
alias Explorer.Repo
alias Explorer.Chain.Address alias Explorer.Chain.Address
alias BlockScoutWeb.Models.UserFromAuth alias BlockScoutWeb.Models.UserFromAuth
@ -573,6 +574,111 @@ defmodule BlockScoutWeb.Account.Api.V1.UserControllerTest do
|> json_response(422) == %{"errors" => %{"watchlist_id" => ["Address already added to the watch list"]}} |> json_response(422) == %{"errors" => %{"watchlist_id" => ["Address already added to the watch list"]}}
end end
test "watchlist address returns with token balances info", %{conn: conn} do
watchlist_address_map = build(:watchlist_address)
conn
|> post(
"/api/account/v1/user/watchlist",
watchlist_address_map
)
|> json_response(200)
watchlist_address_map_1 = build(:watchlist_address)
conn
|> post(
"/api/account/v1/user/watchlist",
watchlist_address_map_1
)
|> json_response(200)
values =
for _i <- 0..149 do
ctb =
insert(:address_current_token_balance_with_token_id,
address: Repo.get_by(Address, hash: watchlist_address_map["address_hash"])
)
|> Repo.preload([:token])
Decimal.div(
Decimal.mult(ctb.value, ctb.token.fiat_value),
Decimal.new(10 ** Decimal.to_integer(ctb.token.decimals))
)
end
values_1 =
for _i <- 0..200 do
ctb =
insert(:address_current_token_balance_with_token_id,
address: Repo.get_by(Address, hash: watchlist_address_map_1["address_hash"])
)
|> Repo.preload([:token])
Decimal.div(
Decimal.mult(ctb.value, ctb.token.fiat_value),
Decimal.new(10 ** Decimal.to_integer(ctb.token.decimals))
)
end
|> Enum.sort(fn x1, x2 -> Decimal.compare(x1, x2) in [:gt, :eq] end)
|> Enum.take(150)
[wa2, wa1] = conn |> get("/api/account/v1/user/watchlist") |> json_response(200)
assert wa1["tokens_fiat_value"] |> Decimal.new() |> Decimal.round(14) ==
values |> Enum.reduce(Decimal.new(0), fn x, acc -> Decimal.add(x, acc) end) |> Decimal.round(14)
assert wa1["tokens_count"] == 150
assert wa1["tokens_overflow"] == false
assert wa2["tokens_fiat_value"] |> Decimal.new() |> Decimal.round(14) ==
values_1 |> Enum.reduce(Decimal.new(0), fn x, acc -> Decimal.add(x, acc) end) |> Decimal.round(14)
assert wa2["tokens_count"] == 150
assert wa2["tokens_overflow"] == true
end
test "watchlist address returns with token balances info + handle nil fiat values", %{conn: conn} do
watchlist_address_map = build(:watchlist_address)
conn
|> post(
"/api/account/v1/user/watchlist",
watchlist_address_map
)
|> json_response(200)
values =
for _i <- 0..148 do
ctb =
insert(:address_current_token_balance_with_token_id,
address: Repo.get_by(Address, hash: watchlist_address_map["address_hash"])
)
|> Repo.preload([:token])
Decimal.div(
Decimal.mult(ctb.value, ctb.token.fiat_value),
Decimal.new(10 ** Decimal.to_integer(ctb.token.decimals))
)
end
token = insert(:token, fiat_value: nil)
insert(:address_current_token_balance_with_token_id,
address: Repo.get_by(Address, hash: watchlist_address_map["address_hash"]),
token: token,
token_contract_address_hash: token.contract_address_hash
)
[wa1] = conn |> get("/api/account/v1/user/watchlist") |> json_response(200)
assert wa1["tokens_fiat_value"] |> Decimal.new() |> Decimal.round(14) ==
values |> Enum.reduce(Decimal.new(0), fn x, acc -> Decimal.add(x, acc) end) |> Decimal.round(14)
assert wa1["tokens_count"] == 150
assert wa1["tokens_overflow"] == false
end
test "post api key", %{conn: conn} do test "post api key", %{conn: conn} do
post_api_key_response = post_api_key_response =
conn conn

@ -38,6 +38,9 @@ defmodule Explorer.Account.WatchlistAddress do
field(:notify_inapp, :boolean) field(:notify_inapp, :boolean)
field(:fetched_coin_balance, Wei, virtual: true) field(:fetched_coin_balance, Wei, virtual: true)
field(:tokens_fiat_value, :decimal, virtual: true)
field(:tokens_count, :integer, virtual: true)
field(:tokens_overflow, :boolean, virtual: true)
timestamps() timestamps()
end end

@ -895,7 +895,7 @@ defmodule Explorer.Factory do
address: build(:address), address: build(:address),
token_contract_address_hash: insert(:token).contract_address_hash, token_contract_address_hash: insert(:token).contract_address_hash,
block_number: block_number(), block_number: block_number(),
value: Enum.random(1..100_000), value: Enum.random(1_000_000_000_000_000_000..10_000_000_000_000_000_000),
value_fetched_at: DateTime.utc_now(), value_fetched_at: DateTime.utc_now(),
token_id: token_id, token_id: token_id,
token_type: token_type token_type: token_type

Loading…
Cancel
Save