Merge pull request #8966 from blockscout/np-hf-account-watchlist

Add ACCOUNT_WATCHLIST_NOTIFICATIONS_LIMIT_FOR_30_DAYS
vb-remove-stg-suffix
nikitosing 11 months ago committed by GitHub
commit b7dac2a217
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 10
      apps/explorer/lib/explorer/account/notifier/notify.ex
  3. 25
      apps/explorer/lib/explorer/account/watchlist_notification.ex
  4. 23
      apps/explorer/priv/account/migrations/20231207201701_add_watchlist_id_column.exs
  5. 56
      apps/explorer/test/explorer/account/notifier/notify_test.exs
  6. 4
      config/runtime.exs
  7. 3
      cspell.json

@ -4,6 +4,8 @@
### Features
- [#8966](https://github.com/blockscout/blockscout/pull/8966) - Add `ACCOUNT_WATCHLIST_NOTIFICATIONS_LIMIT_FOR_30_DAYS`
### Fixes
### Chore

@ -55,7 +55,8 @@ defmodule Explorer.Account.Notifier.Notify do
defp notify_watchlists(nil), do: nil
defp notify_watchlist(%WatchlistAddress{} = address, summary, direction) do
case ForbiddenAddress.check(address.address_hash) do
case !WatchlistNotification.limit_reached_for_watchlist_id?(address.watchlist_id) &&
ForbiddenAddress.check(address.address_hash) do
{:ok, _address_hash} ->
with %WatchlistNotification{} = notification <-
build_watchlist_notification(
@ -74,6 +75,9 @@ defmodule Explorer.Account.Notifier.Notify do
{:error, _message} ->
nil
false ->
nil
end
end
@ -106,9 +110,6 @@ defmodule Explorer.Account.Notifier.Notify do
Logger.info("--- email delivery response: FAILED", fetcher: :account)
Logger.info(error, fetcher: :account)
end
else
Logger.info("--- email delivery response: FAILED", fetcher: :account)
Logger.info("Email is not composed (is nil)", fetcher: :account)
end
end
@ -119,6 +120,7 @@ defmodule Explorer.Account.Notifier.Notify do
if is_watched(address, summary, direction) do
%WatchlistNotification{
watchlist_address_id: address.id,
watchlist_id: address.watchlist_id,
transaction_hash: summary.transaction_hash,
from_address_hash: summary.from_address_hash,
to_address_hash: summary.to_address_hash,

@ -1,6 +1,6 @@
defmodule Explorer.Account.WatchlistNotification do
@moduledoc """
Stored notification about event
Stored notification about event
related to WatchlistAddress
"""
@ -9,7 +9,8 @@ defmodule Explorer.Account.WatchlistNotification do
import Ecto.Changeset
import Explorer.Chain, only: [hash_to_lower_case_string: 1]
alias Explorer.Account.WatchlistAddress
alias Explorer.Repo
alias Explorer.Account.{Watchlist, WatchlistAddress}
schema "account_watchlist_notifications" do
field(:amount, :decimal)
@ -24,6 +25,7 @@ defmodule Explorer.Account.WatchlistNotification do
field(:subject_hash, Cloak.Ecto.SHA256)
belongs_to(:watchlist_address, WatchlistAddress)
belongs_to(:watchlist, Watchlist)
field(:from_address_hash, Explorer.Encrypted.AddressHash)
field(:to_address_hash, Explorer.Encrypted.AddressHash)
@ -62,4 +64,23 @@ defmodule Explorer.Account.WatchlistNotification do
|> put_change(:transaction_hash_hash, hash_to_lower_case_string(get_field(changeset, :transaction_hash)))
|> put_change(:subject_hash, get_field(changeset, :subject))
end
@doc """
Check if amount of watchlist notifications for the last 30 days is less than ACCOUNT_WATCHLIST_NOTIFICATIONS_LIMIT_FOR_30_DAYS
"""
@spec limit_reached_for_watchlist_id?(integer) :: boolean
def limit_reached_for_watchlist_id?(watchlist_id) do
__MODULE__
|> where(
[wn],
wn.watchlist_id == ^watchlist_id and
fragment("NOW() - ? at time zone 'UTC' <= interval '30 days'", wn.inserted_at)
)
|> limit(^watchlist_notification_30_days_limit())
|> Repo.account_repo().aggregate(:count) == watchlist_notification_30_days_limit()
end
defp watchlist_notification_30_days_limit do
Application.get_env(:explorer, Explorer.Account)[:notifications_limit_for_30_days]
end
end

@ -0,0 +1,23 @@
defmodule Explorer.Repo.Account.Migrations.AddWatchlistIdColumn do
use Ecto.Migration
def change do
execute("""
ALTER TABLE public.account_watchlist_notifications
DROP CONSTRAINT account_watchlist_notifications_watchlist_address_id_fkey;
""")
alter table(:account_watchlist_notifications) do
add(:watchlist_id, :bigserial)
end
create(index(:account_watchlist_notifications, [:watchlist_id]))
execute("""
UPDATE account_watchlist_notifications awn
SET watchlist_id = awa.watchlist_id
FROM account_watchlist_addresses awa
WHERE awa.id = awn.watchlist_address_id
""")
end
end

@ -86,5 +86,61 @@ defmodule Explorer.Account.Notifier.NotifyTest do
assert wn.tx_fee == fee
assert wn.type == "COIN"
end
test "ignore new notification when limit is reached" do
old_envs = Application.get_env(:explorer, Explorer.Account)
Application.put_env(:explorer, Explorer.Account, Keyword.put(old_envs, :notifications_limit_for_30_days, 1))
wa =
%WatchlistAddress{address_hash: address_hash} =
build(:account_watchlist_address, watch_coin_input: true)
|> Repo.account_repo().insert!()
_watchlist_address = Repo.preload(wa, watchlist: :identity)
tx =
%Transaction{
from_address: _from_address,
to_address: _to_address,
block_number: _block_number,
hash: _tx_hash
} = with_block(insert(:transaction, to_address: %Chain.Address{hash: address_hash}))
{_, fee} = Chain.fee(tx, :gwei)
amount = Wei.to(tx.value, :ether)
notify = Notify.call([tx])
wn =
WatchlistNotification
|> first
|> Repo.account_repo().one()
assert notify == [[:ok]]
assert wn.amount == amount
assert wn.direction == "incoming"
assert wn.method == "transfer"
assert wn.subject == "Coin transaction"
assert wn.tx_fee == fee
assert wn.type == "COIN"
address = Repo.get(Chain.Address, address_hash)
tx =
%Transaction{
from_address: _from_address,
to_address: _to_address,
block_number: _block_number,
hash: _tx_hash
} = with_block(insert(:transaction, to_address: address))
Notify.call([tx])
WatchlistNotification
|> first
|> Repo.account_repo().one!()
Application.put_env(:explorer, Explorer.Account, old_envs)
end
end
end

@ -407,7 +407,9 @@ config :explorer, Explorer.Account,
],
resend_interval: ConfigHelper.parse_time_env_var("ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL", "5m"),
private_tags_limit: ConfigHelper.parse_integer_env_var("ACCOUNT_PRIVATE_TAGS_LIMIT", 2000),
watchlist_addresses_limit: ConfigHelper.parse_integer_env_var("ACCOUNT_WATCHLIST_ADDRESSES_LIMIT", 15)
watchlist_addresses_limit: ConfigHelper.parse_integer_env_var("ACCOUNT_WATCHLIST_ADDRESSES_LIMIT", 15),
notifications_limit_for_30_days:
ConfigHelper.parse_integer_env_var("ACCOUNT_WATCHLIST_NOTIFICATIONS_LIMIT_FOR_30_DAYS", 1000)
config :explorer, :token_id_migration,
first_block: ConfigHelper.parse_integer_env_var("TOKEN_ID_MIGRATION_FIRST_BLOCK", 0),

@ -545,7 +545,8 @@
"qitmeer",
"meer",
"DefiLlama",
"SOLIDITYSCAN"
"SOLIDITYSCAN",
"fkey"
],
"enableFiletypes": [
"dotenv",

Loading…
Cancel
Save