Skip to content

feat(debridge): add tracker; never count treasury swap output as fees - #42

Merged
kaladinlight merged 6 commits into
mainfrom
feat/debridge-tracker
Oct 28, 2026
Merged

kaladinlight merged 6 commits into
mainfrom
feat/debridge-tracker

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Adds a deBridge tracker, and makes the shared treasury scanner never count the treasury's own swap or bridge output as fee revenue.

deBridge

  • web sends the affiliate fee to the sell chain's DAO treasury. It arrives from one of two contracts (same address on all 8 unchained chains):
    • DeBridgeRouter 0x663dc15d: same-chain swaps, paid in the buy token during the swap.
    • DlnSource 0xef4fb24a: cross-chain orders, paid in the order's input token when a solver unlocks the order.
  • deBridge's stats API can't attribute these. Orders only filter by referralCode (web sends none), and same-chain swaps have no list endpoint. So the tracker scans the treasuries for transfers from the two contracts, using the shared scanner.
  • Maps the deBridge swapper for partner reconciliation. swap-service doesn't verify deBridge swaps yet, so no deBridge swap is netted today; the mapping is there for later.
  • Audit (Feb → Oct 2026): 393 fees, about $4.1k at current prices (about $3.3k at swap-time prices).
    • All 352 same-chain ERC20 fees are 59–60 bps of the router's payout.
    • No other deBridge contract pays the treasuries.
    • Cross-chain fees misdirected before web#12270 (sent to the buy-chain treasury address on the sell chain): under $0.10 in total.
    • Untracked chains (no unchained host): about $0.09.
    • Native fees stuck in DlnSource (unclaimedAffiliateETHFees): 0.0006 ETH. They count once withdrawn.
  • 143 of the 144 rows with no USD price come from one bot swapping fake tokens with no liquidity. They stay at $0.

Treasury's own swaps (shared scanner: Across, CoW, ButterSwap, deBridge)

Swap output must never count as fee revenue. Fees on the treasury's own swaps are skipped along with the output.

  • Same-chain, any tracker: a transaction is skipped when the treasury spends in it, i.e. it sends the transaction, sends a nonzero token transfer, pays native coin to a tracked contract, or has a net native outflow. Native is judged on net flow because some Safes pass each incoming native payment on to their singleton (via delegatecall), and unchained lists that as an outgoing transfer of the same amount.
  • Cross-chain, per tracker: a new optional isOwnSwap(txid, client) check runs only on transactions that paid a fee.
    • Across uses it: it reads the fill's receipt (eth_getTransactionReceipt, no traces) and skips fills whose FilledRelay depositor is a DAO treasury.
    • It decodes both FilledRelay and the older FilledV3Relay. A fee transaction with no recognisable fill event is skipped with a warning, so a future Across event change costs a few fees instead of booking a bridge as revenue.
    • Across fee transactions so far: 201, about one a day.
    • Real fills record the original user as depositor: 110 distinct depositors, none a contract.
  • CoW needs no check: swap output comes from the settlement contract, which isn't tracked.
  • Known gaps:
    • deBridge cross-chain orders made by the treasury itself (not used for treasury swaps).
    • If a treasury bridge is batched into one relayer transaction with user fills, those user fees are dropped too.

Dashboard

  • Service colours now use each swapper's brand colour: the logo's, or its site's accent colour where the logo is too dark for the dashboard.

Verification

  • tsc --noEmit, eslint and bun test: 137 pass.
    • New: treasuryFees.test.ts, getTreasuryFees.test.ts (end to end over two mocked unchained pages, mutation-checked) and across.test.ts (encoded FilledRelay and FilledV3Relay events, plus the fail-safe).
  • Ran Across, CoW, deBridge and ButterSwap over 2025 → today, before and after the own-swap change: row-for-row identical (201 / 151 / 393 / 1,250).

Follow-ups

  • swap-service's SWAPPER_FEE_STRATEGY[Debridge] = 'sell_asset' is wrong for same-chain swaps, where the fee is in the buy token. Fix it before adding deBridge partner verification.

🤖 Generated with Claude Code

kaladinlight and others added 5 commits October 27, 2026 15:36
…nd DlnSource

deBridge pays our affiliate fee on the sell chain to the per-chain treasury:
same-chain swaps through DeBridgeRouter during the swap, cross-chain DLN orders
through DlnSource when the order is unlocked. Its stats API can't attribute
either (no affiliate-recipient filter, no same-chain list endpoint), so scan
the 8 unchained treasuries for transfers from those two contracts.

Maps the deBridge swapper for partner reconciliation and registers the
service in the dashboard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…vices

Same-chain deBridge fees arrive in the buy token (verified on-chain), not the
sell token. Service colors now come from each swapper's logo, or its site's
accent where the logo is too dark for the dashboard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… revenue

Same-chain: skip any tx in which the treasury spends (nonzero token out, net
native out, or sends the tx), since swap output can only land in the same tx
the treasury paid for it. Native is judged on net flow because some Safes
forward each receipt to their singleton.

Cross-chain: trackers can pass isOwnSwap, checked only on txs that pay a fee.
Across uses it to skip fills whose FilledRelay depositor is a DAO treasury,
reading the receipt through a new getReceiptLogs on the log client.

Verified against all four scanner trackers over full history (Across, CoW,
deBridge, ButterSwap): row-for-row identical to before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A native payment from the treasury to a sender is a spend even when an
  inflow in the same tx nets it out (WETH unwrap logs no Transfer).
- Compare token values as strings so a malformed NFT value can't throw.
- Ignore undecodable logs that share the FilledRelay topic.
- List every DAO treasury constant in DAO_TREASURIES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p check

Mocks unchained history across two pages and the Alchemy client, and checks
that isOwnSwap runs only on fee txs, receives the scanner's client, and drops
exactly the txs it flags, alongside the same-chain spend rule and the
status/window filters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
revenue-dashboard Ready Ready Preview Oct 27, 2026 10:31pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 49 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: caa5096b-81ea-4003-adce-f554f88dd4c7
Commits

Reviewing files that changed from the base of the PR and between e1bb02f and ac84fd0.

Files selected for processing: 2
  • apps/revenue-api/src/affiliateRevenue/across/across.test.ts
  • apps/revenue-api/src/affiliateRevenue/across/across.ts

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2c8e3ee9-94ac-452b-9ea1-f2a10ebff31a
Commits

Reviewing files that changed from the base of the PR and between fc4b98c and e1bb02f.

Files selected for processing: 16
  • README.md
  • apps/revenue-api/src/affiliateRevenue/across/across.test.ts
  • apps/revenue-api/src/affiliateRevenue/across/across.ts
  • apps/revenue-api/src/affiliateRevenue/constants.ts
  • apps/revenue-api/src/affiliateRevenue/debridge/constants.ts
  • apps/revenue-api/src/affiliateRevenue/debridge/debridge.ts
  • apps/revenue-api/src/affiliateRevenue/debridge/index.ts
  • apps/revenue-api/src/affiliateRevenue/index.ts
  • apps/revenue-api/src/affiliateRevenue/reconcileRevenue.test.ts
  • apps/revenue-api/src/affiliateRevenue/reconcileRevenue.ts
  • apps/revenue-api/src/affiliateRevenue/utils/evmLogClient.ts
  • apps/revenue-api/src/affiliateRevenue/utils/getTreasuryFees.test.ts
  • apps/revenue-api/src/affiliateRevenue/utils/treasuryFees.test.ts
  • apps/revenue-api/src/affiliateRevenue/utils/treasuryFees.ts
  • apps/revenue-api/src/types.ts
  • apps/revenue-dashboard/src/constants/services.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The API adds deBridge fee collection and reconciliation across configured chains. The shared treasury scanner adds transaction-spend filtering and an optional own-swap check, which Across uses to detect treasury deposits. The README and dashboard now include deBridge.

Changes: Affiliate revenue collection

Layer / File(s) Summary
Treasury fee classification
apps/revenue-api/src/affiliateRevenue/utils/treasuryFees.ts, apps/revenue-api/src/affiliateRevenue/utils/evmLogClient.ts, apps/revenue-api/src/affiliateRevenue/across/*, apps/revenue-api/src/affiliateRevenue/constants.ts, apps/revenue-api/src/affiliateRevenue/utils/*test.ts
The shared scanner identifies treasury-funded transactions and supports an optional isOwnSwap callback. Across checks receipt logs for FilledRelay events whose depositor matches a DAO treasury. Tests cover fee extraction, spend filtering, and Across fill detection.
deBridge API collection and reconciliation
apps/revenue-api/src/affiliateRevenue/debridge/*, apps/revenue-api/src/affiliateRevenue/index.ts, apps/revenue-api/src/affiliateRevenue/reconcileRevenue*, apps/revenue-api/src/types.ts
The API fetches and enriches fees for configured deBridge chains and senders. The provider registry and service type include deBridge. Reconciliation uses the configured chain coverage, with tests for payable deductions and an unsupported chain.
deBridge dashboard and provider listing
README.md, apps/revenue-dashboard/src/constants/services.ts
The README lists deBridge as a DEX provider. The dashboard adds its service label, color, and stack order.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant providers
  participant getFees
  participant getCachedFees
  participant USDPriceEnrichment
  providers->>getFees: Request deBridge fees
  getFees->>getCachedFees: Fetch fees for configured chains and senders
  getCachedFees-->>getFees: Return per-chain fees
  getFees->>USDPriceEnrichment: Enrich combined fees
  USDPriceEnrichment-->>getFees: Return fees with USD prices
Loading

Possibly related PRs

Merge Risk: Minimal · previous review e1bb0

The change is mergeable after normal checks. Mixed Across batches can still omit some user fees, but no broader failure was established.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly summarizes the two primary changes: adding the deBridge tracker and excluding treasury swap output from fee calculations.
Docstring Coverage Passed No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within survi…
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.
Full details: Docstring Coverage

Explanation

No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 15 files. (1 skipped: 1 unsupported.)

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

A rabbit checks the treasury flow,
Across logs reveal which fills to know.
DeBridge fees join the chain-wide sweep,
While dashboard labels mark their keep.
I nibble greens and hop away,
With tidy totals for the day.

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

…ised fill

Every handler payout happens inside a SpokePool fill, so a fee tx whose
receipt has no fill event we can decode means Across changed its event. Skip
it with a warning rather than risk counting a treasury bridge's output as a
fee. The current SpokePool ABI still declares the address-typed FilledV3Relay,
so decode it alongside FilledRelay.

Full-history Across run: 201 -> 201 fees, no warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kaladinlight
kaladinlight merged commit 7026d32 into main Oct 28, 2026
4 checks passed
@kaladinlight
kaladinlight deleted the feat/debridge-tracker branch October 28, 2026 14:20

This branch was successfully deployed

1 active deployment
Preview — ac84fd02 Deployed Oct 27, 2026 by vercel[bot]
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.

1 participant