Skip to content

🦺 server: filter notifications to known tokens only#974

Merged
cruzdanilo merged 1 commit intomainfrom
filter-notifications
May 7, 2026
Merged

🦺 server: filter notifications to known tokens only#974
cruzdanilo merged 1 commit intomainfrom
filter-notifications

Conversation

@aguxez
Copy link
Copy Markdown
Contributor

@aguxez aguxez commented Apr 24, 2026

closes #906

Summary by CodeRabbit

  • Bug Fixes

    • "Funds received" notifications now filter to recognized tokens only, significantly reducing spam from unknown or unverified assets
    • Enhanced error handling and logging for notification delivery to better track and diagnose failures
  • Tests

    • Added comprehensive test coverage for token filtering logic, including caching behavior, multi-chain scenarios, and various error handling edge cases

@aguxez aguxez force-pushed the filter-notifications branch from 391e906 to b5ee87a Compare April 24, 2026 11:28
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 24, 2026

🦋 Changeset detected

Latest commit: e736d6f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@exactly/server Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR adds token filtering to the "Funds received" OneSignal notification system. A new async isKnownToken function checks Redis for cached token addresses per chain, fetches from the LiFi API on cache miss, and silently fails open by continuing to notify on any error while capturing the exception.

Changes

Token Filtering for Notifications

Layer / File(s) Summary
Notification Gating
server/hooks/activity.ts
The ADDRESS_ACTIVITY handler now gates "Funds received" notifications on isKnownToken(chain.id, underlying) in addition to the existing marketsByAsset check.
Token Verification Logic
server/hooks/activity.ts
New isKnownToken(chainId, address) helper uses Redis-backed per-chain token caching, fetches from li.quest/v1/tokens on cache miss with 5s timeout, and returns true on any error (fail-open).
Error Reporting
server/hooks/activity.ts
Notification errors and token-fetch errors are reported to Sentry with { level: "error" } context.
Infrastructure & Tests
server/test/hooks/activity.test.ts
Redis cleanup added per test; existing error-handling assertion updated; comprehensive test suite validates cache miss/hit, cache isolation across chains, suppression for unknown tokens, and fail-open behavior on LiFi/Redis errors.
Release Metadata
.changeset/sharp-sails-guess.md
Patch-version bump announced for @exactly/server.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant ActivityHook as Activity Hook
    participant Redis as Redis Cache
    participant LiFi as LiFi API
    participant Sentry as Sentry
    participant OneSignal as OneSignal

    Client->>ActivityHook: ADDRESS_ACTIVITY event
    ActivityHook->>ActivityHook: Check marketsByAsset
    alt Token in marketsByAsset
        ActivityHook->>OneSignal: Send notification
    else Token not in marketsByAsset
        ActivityHook->>Redis: Check isKnownToken (SISMEMBER + SCARD)
        alt Cache hit (non-empty, token found)
            Redis-->>ActivityHook: true
            ActivityHook->>OneSignal: Send notification
        else Cache miss (empty set)
            ActivityHook->>LiFi: Fetch tokens for chain
            LiFi-->>ActivityHook: Token list
            ActivityHook->>Redis: Populate cache with tokens
            alt Token in fetched list
                ActivityHook->>OneSignal: Send notification
            else Token not in fetched list
                ActivityHook-->>Client: Skip notification
            end
        else LiFi or Redis error
            ActivityHook->>Sentry: Capture error { level: "error" }
            ActivityHook->>OneSignal: Send notification (fail-open)
        end
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

Suggested Reviewers

  • nfmelendez
  • cruzdanilo
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main change: filtering notifications to known tokens only, which is the core objective across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch filter-notifications

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

gemini-code-assist[bot]

This comment was marked as resolved.

@sentry
Copy link
Copy Markdown

sentry Bot commented Apr 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.80%. Comparing base (8c2785c) to head (e736d6f).
⚠️ Report is 5 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #974      +/-   ##
==========================================
+ Coverage   72.35%   72.80%   +0.45%     
==========================================
  Files         231      231              
  Lines        8884     9020     +136     
  Branches     2874     2943      +69     
==========================================
+ Hits         6428     6567     +139     
- Misses       2199     2200       +1     
+ Partials      257      253       -4     
Flag Coverage Δ
e2e 72.68% <84.84%> (+3.17%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aguxez aguxez force-pushed the filter-notifications branch 2 times, most recently from ecdf4f2 to 4d6172b Compare April 24, 2026 14:04
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
server/hooks/activity.ts (1)

143-143: ⚠️ Potential issue | 🟠 Major

Blocking webhook response on a per-transfer LiFi fetch.

await isKnownToken(chain.id, underlying) runs serially inside the for (const ... of transfers) loop before c.json({}) returns. On cache miss, this adds up to 5s (plus Redis RTTs) per unknown-token transfer, serialized across transfers in the payload. Alchemy may retry slow webhooks, which can amplify downstream work (poking, autoCredit, duplicate sends).

Consider resolving the known-token check off the response path — e.g. kick off the LiFi/Redis checks in parallel and gate only the sendPushNotification(...) call on an already-resolved decision, so the handler acks quickly regardless of LiFi latency.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3560abdd-a0de-493c-8582-db8f20162cb6

📥 Commits

Reviewing files that changed from the base of the PR and between b5ee87a and 4d6172b.

📒 Files selected for processing (4)
  • .changeset/sharp-sails-guess.md
  • cspell.json
  • server/hooks/activity.ts
  • server/test/hooks/activity.test.ts

@aguxez aguxez marked this pull request as ready for review April 27, 2026 08:03
chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@aguxez aguxez force-pushed the filter-notifications branch from 4d6172b to 53dadc1 Compare April 27, 2026 11:27
chatgpt-codex-connector[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@aguxez aguxez force-pushed the filter-notifications branch 3 times, most recently from 8e731f9 to 2705d5f Compare May 4, 2026 09:23
chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
server/hooks/activity.ts (1)

143-163: ⚠️ Potential issue | 🟠 Major

Still blocking the webhook on isKnownToken.

This await runs inside the transfer loop before the handler reaches c.json({}), so a cold LiFi/Redis miss still holds the webhook open and serializes that delay across unknown transfers in the same payload. Move the allowlist check off the response path or resolve it in parallel with the rest of the async work.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5a008653-b200-47f8-a977-8b35a899f3cf

📥 Commits

Reviewing files that changed from the base of the PR and between 53dadc1 and 2705d5f.

📒 Files selected for processing (4)
  • .changeset/sharp-sails-guess.md
  • cspell.json
  • server/hooks/activity.ts
  • server/test/hooks/activity.test.ts

Comment thread cspell.json
Comment thread server/hooks/activity.ts
@aguxez aguxez force-pushed the filter-notifications branch from 2705d5f to e92aef2 Compare May 4, 2026 12:12
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
server/hooks/activity.ts (1)

143-143: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Take the token check off the webhook ack path and dedupe cold refreshes.

await isKnownToken(...) keeps the handler waiting on Redis and a potential 5s LiFi fetch before c.json({}), and every concurrent cold miss on the same chainId still performs its own refresh because nothing coordinates the count === 0 path. That makes the slow path both serialized per transfer and stampede-prone. Move the notification gating into the existing fire-and-forget work and add a short per-chain refresh lock/sentinel so one miss produces one upstream fetch.

Also applies to: 338-363


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8c348a5a-b475-4041-b12f-73a07cb88538

📥 Commits

Reviewing files that changed from the base of the PR and between 2705d5f and e92aef2.

📒 Files selected for processing (3)
  • .changeset/sharp-sails-guess.md
  • server/hooks/activity.ts
  • server/test/hooks/activity.test.ts

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e92aef2445

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/hooks/activity.ts Outdated
Comment thread server/hooks/activity.ts
@aguxez aguxez marked this pull request as draft May 4, 2026 14:19
@aguxez aguxez marked this pull request as draft May 4, 2026 14:19
@cruzdanilo cruzdanilo marked this pull request as ready for review May 5, 2026 19:07
@aguxez aguxez marked this pull request as draft May 6, 2026 10:58
@aguxez aguxez force-pushed the filter-notifications branch from e92aef2 to f3acb2c Compare May 7, 2026 12:49
@aguxez aguxez marked this pull request as ready for review May 7, 2026 13:57
@aguxez aguxez force-pushed the filter-notifications branch 2 times, most recently from 8bfb559 to 3e8a975 Compare May 7, 2026 14:44
@aguxez aguxez force-pushed the filter-notifications branch from 3e8a975 to e736d6f Compare May 7, 2026 17:35
@cruzdanilo cruzdanilo merged commit e736d6f into main May 7, 2026
14 checks passed
@cruzdanilo cruzdanilo deleted the filter-notifications branch May 7, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

server: filter spam notifications

2 participants