Skip to content

fix(swap-service): value fees paid in a third asset and report only what the payout pays - #78

Merged
kaladinlight merged 4 commits into
developfrom
fix/fee-asset-precision
Oct 28, 2026
Merged

kaladinlight merged 4 commits into
developfrom
fix/fee-asset-precision

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Description

A partner fee collected in neither swap asset (Relay routes some fees to USDC on Base, for example) had no known precision, so its on-chain value was never computed. The payout report listed those swaps as "no verified on-chain fee" and left them unpaid, while the affiliate endpoints fell back to the bps-implied estimate for them. On October's data this left 19 partner swaps unpaid.

Fee asset precision

  • Precision for a fee asset outside the swap's own assets is looked up in the web app's hosted asset list (generatedAssetData.json). An asset's precision never changes, so nothing is stored: no migration, no backfill, no API change.
  • The list is cached in memory and refreshed in the background at most hourly, downloading the full file only when the manifest hash changes. Only the first load is waited on, and a failed refresh keeps the cached list.
  • The payout script fails if the list can't be loaded at all, rather than leaving those fees unpaid as if they had no verified fee.

One set of payout rules

  • The payout script's per-swap rules (attribution accepted, verified, an on-chain fee within the deviation guard, a partner share configured) move to swaps/partner-fee.ts, shared by the payout script and the affiliate endpoints.
  • /v1/affiliate/stats totals and the feeUsd / partnerFeeUsd on /v1/affiliate/swaps now count only swaps the payout would pay. Anything else reports no fee, never the bps-implied estimate, so the affiliate dashboard total, the revenue dashboard and the payout agree.
  • The payout report describes a held-back fee from the deviation the shared rules computed, so every excluded swap lands in a report section. A non-finite deviation counts as an anomaly.

Consumers of /v1/affiliate/swaps: the revenue dashboard keys off partnerFeeUsd and needs no change. A follow-up in web switches the affiliate dashboard to partnerFeeUsd and should deploy after this.

Testing

  • yarn lint and yarn build (7/7) pass; swap-service tests (182) and affiliate payout tests (33) pass
  • New unit tests cover third-asset fee valuation, the shared payout rules in order (including a non-finite deviation), affiliate stats and swaps reporting only payable fees, and the asset list's background refresh, reload on a new manifest hash and fallback to the cached list
  • October payout against a production snapshot: the 19 swaps are valued and 16 are paid; the other 3 are now listed as fee anomalies (a reported on-chain fee of 0)
  • The payout report is identical before and after moving the rules into the shared module
  • After deploy, check /v1/affiliate/stats totals for a partner against the payout report for the same month

Summary by CodeRabbit

  • Bug Fixes
    • Affiliate fees and earnings now reflect recorded on-chain payouts, with fees excluded when attribution, verification, or fee validation requirements are not met.
    • Fee amounts can now be valued for additional supported assets when precision data is available; unresolved fees remain unreported rather than being estimated.
    • Payout reports now identify fee deviations, including cases where a fee cannot be validated.

kaladinlight and others added 4 commits October 28, 2026 11:29
…n-chain fees

- Look up fee asset precision in the web app's hosted asset list (cached,
  reloaded when its manifest hash changes) so a fee in neither swap asset,
  such as Relay's USDC on Base, gets its on-chain value
- Affiliate stats and swaps report feeUsd and partnerFeeUsd from the on-chain
  fee only, null when it can't be valued, instead of the bps-implied estimate
- The payout script fails if the asset list can't load rather than leaving
  those fees unpaid; referral fees use the lookup and keep their fallback

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

- Share the payout's per-swap rules (attribution accepted, verified, an
  on-chain fee within the deviation guard, a partner share configured)
  so the affiliate endpoints and the payout script can't drift apart
- Affiliate stats totals and the fee fields on affiliate swaps count only
  payable swaps; everything else reports no fee
- The payout script's report buckets are unchanged

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t anomalies from the shared rules

- Serve the cached asset list while a due refresh runs; only the first
  load is waited on, and only the refresh logs a failure
- resolvePartnerFee returns the fee deviation it held a swap back on, and
  the payout report describes that anomaly instead of recomputing it, so
  an excluded swap can't drop out of every report bucket
- A non-finite deviation counts as an anomaly rather than passing the guard
- Affiliate stats narrow the query to rows that can be payable
- Tests for the shared payout rules and the asset list refresh

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

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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: 9c427451-d1f1-44c7-90fa-6bf9957c0357
Commits

Reviewing files that changed from the base of the PR and between 4098349 and 31244fb.

Files selected for processing: 12
  • apps/swap-service/src/affiliate/__tests__/affiliate.service.test.ts
  • apps/swap-service/src/affiliate/affiliate.service.ts
  • apps/swap-service/src/swaps/__tests__/partner-fee.test.ts
  • apps/swap-service/src/swaps/__tests__/utils.test.ts
  • apps/swap-service/src/swaps/partner-fee.ts
  • apps/swap-service/src/swaps/utils.ts
  • apps/swap-service/src/utils/__tests__/assets.test.ts
  • apps/swap-service/src/utils/assets.ts
  • scripts/affiliate-payouts/affiliate-payouts.test.ts
  • scripts/affiliate-payouts/affiliate-payouts.ts
  • scripts/affiliate-payouts/types.ts
  • scripts/affiliate-payouts/utils.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 swap service now values affiliate fees with asset precision data and applies shared payout eligibility checks. Affiliate statistics, swap fee fields, and payout aggregation use the resolved outcomes. The payout script also uses the shared fee types and deviation tolerance.

Changes: Affiliate fee eligibility and reporting

Layer / File(s) Summary
Asset precision loading and fee calculation
apps/swap-service/src/utils/assets.ts, apps/swap-service/src/utils/__tests__/assets.test.ts, apps/swap-service/src/swaps/utils.ts, apps/swap-service/src/swaps/__tests__/utils.test.ts
The swap service loads and caches asset precisions. Fee calculation uses those precisions to value fees paid in assets outside the swap pair and known assets. Tests cover loading, refresh behavior, and fee valuation.
Shared partner-fee resolution
apps/swap-service/src/swaps/partner-fee.ts, apps/swap-service/src/swaps/__tests__/partner-fee.test.ts
The resolver checks attribution, verification, affiliate status, fee availability, fee deviation, and positive partner basis points. Tests cover payable, excluded, and anomalous fee outcomes.
Affiliate statistics and swap fee fields
apps/swap-service/src/affiliate/affiliate.service.ts, apps/swap-service/src/affiliate/__tests__/affiliate.service.test.ts
Statistics queries require successful verification, accepted attribution, and positive partner basis points. Statistics count only payable swaps and use actual fees for earned amounts. Swap fee fields are null for non-payable outcomes; volume can still use the calculated fee.
Affiliate payout aggregation
scripts/affiliate-payouts/affiliate-payouts.ts, scripts/affiliate-payouts/types.ts, scripts/affiliate-payouts/utils.ts, scripts/affiliate-payouts/affiliate-payouts.test.ts
Payout aggregation supplies asset precisions to fee calculation and maps resolver exclusions to existing report buckets. Fee anomaly descriptions include deviation details or a reason when deviation cannot be validated.

Priority: Normal

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

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant PayoutScript as affiliate-payouts.ts
  participant AssetPrecisions as getAssetPrecisions
  participant Aggregation as aggregateByPartner
  participant FeeCalculator as calculateFeeForSwap
  participant FeeResolver as resolvePartnerFee
  PayoutScript->>AssetPrecisions: Load asset precision map
  PayoutScript->>Aggregation: Start aggregation with precision-aware fee callback
  Aggregation->>FeeCalculator: Calculate swap fee using precision map
  Aggregation->>FeeResolver: Resolve fee eligibility
  FeeResolver-->>Aggregation: Return payable or excluded outcome
  Aggregation-->>PayoutScript: Return payout aggregation
Loading

Possibly related PRs

Merge Risk: Minimal · up to 31244

No merge-blocking issue was established. Affiliate statistics and monthly payouts use different date-boundary conventions, so comparisons should use matching intervals.

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 names the two main changes: valuing partner fees paid in a third asset, and reporting only what the payout pays. The changeset adds precision lookup for assets outside the swap's two assets …
Docstring Coverage Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentati…
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: Title check

Explanation

The title names the two main changes: valuing partner fees paid in a third asset, and reporting only what the payout pays. The changeset adds precision lookup for assets outside the swap's two assets (assets.ts, utils.ts) and moves payout eligibility into a shared resolvePartnerFee module, which matches the 'report only what the payout pays' part. The title is specific and uses the conventional commit format. It is slightly long at 88 characters, but this does not reduce its relevance.

Full details: Docstring Coverage

Explanation

Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 5 functions across 12 files.

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

I’m a rabbit with a ledger, and the fee figures align.
I hop through cached precisions to check each token’s sign.
Accepted swaps may pass the gate; excluded ones are shown.
A deviation gets a reason when its value can’t be known.
I tuck the payout report away, then nibble clover green.

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

@kaladinlight
kaladinlight merged commit ce93ce3 into develop Oct 28, 2026
2 checks passed
@kaladinlight
kaladinlight deleted the fix/fee-asset-precision branch October 28, 2026 20:16
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