Skip to content

refactor(swap-service): per-unit sellAssetUsd + verified affiliate fee USD - #35

Merged
kaladinlight merged 3 commits into
developfrom
refactor/sell-asset-usd-pricing
May 1, 2026
Merged

refactor(swap-service): per-unit sellAssetUsd + verified affiliate fee USD#35
kaladinlight merged 3 commits into
developfrom
refactor/sell-asset-usd-pricing

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Apr 30, 2026

Copy link
Copy Markdown
Member

Description

Reshapes USD pricing on swaps so all three asset prices (sell / buy / affiliate fee) are stored per-unit and resolved consistently across the create and verification paths.

  • Renames sellAmountUsdsellAssetUsd on the Swap model and stores the per-unit price (sellAmountUsd / sellAmount) so it matches buyAssetUsd / affiliateAssetUsd. Drops the back-derivation in resolveActualFeeUsd.
  • calculateFeeForSwap now derives volume from the verifier's on-chain sell amount rather than the user-declared amount, and getAffiliateStats reuses calculateFeeForSwap + getAffiliateFeeRate so it honors the verified fee when present and applies ShapeShift's cut consistently with the other fee paths.
  • Resolves actualAffiliateFeeUsd in the Relay verifier (was a TODO) and persists it into affiliateAssetUsd during reconcileSwap. Drops the fixed_base strategy in affiliateFeeAsset.ts and the hardcoded USDC-on-Base address — Relay's fee asset is determined post-verification, so it's none at create time.
  • Adds a one-shot scripts/migrate-sell-asset-usd.ts helper that exports legacy rows via raw SQL and re-imports them under the new schema, computing sellAssetUsd from the legacy sellAmountUsd.

Testing

  • Run the export → DB reset → import flow against a snapshot and confirm row counts and sellAssetUsd values match sellAmountUsd / sellAmount.
  • Create a Relay swap end-to-end and verify affiliateAssetUsd is populated after reconciliation (sell-asset, buy-asset, and unrelated fee-asset cases).
  • Hit /swaps/affiliate-fees/:address and /swaps/referral-fees/:code and confirm the totals match the verified-fee path (no regression vs. the volume-derived path when verification is present).
  • getAffiliateStats returns numbers consistent with the other fee endpoints for the same affiliate.

Summary by CodeRabbit

  • New Features

    • Added USD value tracking for affiliate fees in swap verification results.
  • Improvements

    • Refactored affiliate stats calculation to support flexible date range queries.
    • Enhanced swap pricing accuracy by separating asset price from computed volume calculations.
    • Improved affiliate reward calculation logic.
  • Bug Fixes

    • Removed unsupported fixed-base fee asset strategy.

kaladinlight and others added 2 commits April 30, 2026 15:18
…cing

The legacy sellAmountUsd column held a total (sellAmount × price), while
buyAssetUsd and affiliateAssetUsd held per-unit prices — forcing back-derivation
of the sell-side price in fee math. Renaming the column to sellAssetUsd and
storing the per-unit price drops the asymmetry and simplifies resolveActualFeeUsd.

calculateFeeForSwap now derives volume from the verified on-chain sell amount
rather than the user-declared sell amount, and getAffiliateStats reuses
calculateFeeForSwap + getAffiliateFeeRate so it honors the actual verified fee
when available and applies shapeshift's cut consistently with the rest of the
fee paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bridges old-schema dumps (sellAmountUsd) to the new per-unit semantics
(sellAssetUsd = sellAmountUsd / sellAmount) so existing rows can be reapplied
after a DB reset. Two phases: export reads via raw SQL to bypass the now-stale
generated client; import re-inserts via the new client.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 30, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@kaladinlight has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 47 minutes before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a199ed57-667d-4aa4-b190-0bd56f8021b8

📥 Commits

Reviewing files that changed from the base of the PR and between 6d7c68e and 601db2e.

📒 Files selected for processing (4)
  • apps/swap-service/src/swaps/swaps.service.ts
  • apps/swap-service/src/utils/affiliateFeeAsset.ts
  • apps/swap-service/src/verification/swap-verification.service.ts
  • packages/shared-types/src/index.ts
📝 Walkthrough

Walkthrough

The PR refactors USD pricing calculations for swaps by decoupling sellAssetUsd (asset price, stored in database) from sellAmountUsd (computed sell quantity value). Affiliate stats retrieval now uses a DTO object instead of separate parameters, affiliate fee USD tracking is added to swap verification, and the 'fixed_base' affiliate fee asset strategy is removed.

Changes

Cohort / File(s) Summary
Affiliate Stats & API Refactoring
apps/swap-service/src/affiliate/affiliate.controller.ts, apps/swap-service/src/affiliate/affiliate.service.ts
Controller now passes entire AffiliateStatsQueryDto to service instead of individual date parameters; service method signature changed to accept options object and refactored to support open-ended date ranges and fee-aware volume/fee computation using helper functions.
Swap USD Pricing Refactoring
apps/swap-service/src/swaps/types.ts, apps/swap-service/src/swaps/utils.ts, apps/swap-service/src/swaps/swaps.service.ts
Type renamed sellAmountUsd to sellAssetUsd to represent asset price; new computeSellAmountUsd helper derives sell quantity in USD on-demand using base units and asset precision; createSwap and reconcileSwap updated to use new field and calculation flow.
Affiliate Fee Asset Strategy
apps/swap-service/src/utils/affiliateFeeAsset.ts
Removed 'fixed_base' from FeeAssetStrategy type; Relay swapper changed to use 'none' strategy instead, causing fixed-base resolution to return null.
Test Fixtures
apps/swap-service/src/verification/__tests__/fixtures/near/swap.ts, apps/swap-service/src/verification/__tests__/fixtures/relay/swap.ts
Updated fixture properties: replaced sellAmountUsd with sellAssetUsd and adjusted corresponding numeric values.
Swap Verification & Shared Types
apps/swap-service/src/verification/swap-verification.service.ts, packages/shared-types/src/index.ts
Relay verification now populates actualAffiliateFeeUsd in results using asset prices from sell/buy asset matches or via asset price lookup; SwapVerificationResult interface adds optional actualAffiliateFeeUsd field.
Database Schema
prisma/schema/swap-service.prisma, prisma/migrations/0_init_swap_service/migration.sql
Renamed column sellAmountUsd to sellAssetUsd in Swap model and corresponding migration file.
Referral Rewards Script
scripts/referral-rewards.ts
Updated swap query to fetch sellAssetUsd, sellAmountCryptoBaseUnit, and sellAsset; reward calculation now derives sell amount USD on-demand using asset precision; eligibility guard checks sellAssetUsd instead of sellAmountUsd.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Twitches whiskers at prices refined,
sellAssetUsd and sellAmountUsd, realigned!
Affiliate stats hop through DTOs so neat,
Fee calculations twice as sweet,
Our warren's pricing is redesigned!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 accurately summarizes the main refactoring: renaming sellAmountUsd to sellAssetUsd and implementing verified affiliate fee USD calculation across the codebase.
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 refactor/sell-asset-usd-pricing

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
Review rate limit: 0/1 reviews remaining, refill in 47 minutes.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/swap-service/src/swaps/utils.ts (1)

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

Third-asset fee routes still fall back to estimated USD.

Relay can now persist affiliateAssetUsd for a fee asset that's neither the sell nor buy asset, but this branch still hard-returns null because it has no precision for swap.affiliateFeeAssetId. That means calculateFeeForSwap() reverts to the bps-based estimate instead of the verified fee for those swaps.

Please persist or resolve the fee asset precision alongside affiliateFeeAssetId and use it here before merging; otherwise the verified-fee stats stay inaccurate for those routes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/swap-service/src/swaps/utils.ts` around lines 124 - 138, The branch that
handles a third-party fee asset must use a persisted or resolved precision
instead of forcing precision = null; update calculateFeeForSwap() (the block
referencing swap.affiliateFeeAssetId, affiliateAssetUsd and
bnOrZero(...).div(...).times(...)) to first look for a stored precision field
(e.g., swap.affiliateAssetPrecision) and use it when present, otherwise resolve
the asset precision via the existing asset registry/metadata lookup (e.g.,
getAssetPrecision(assetId) or the same source used for sellAsset/buyAsset
precision); only return null if priceUsd or the resolved precision cannot be
obtained after these steps. Ensure the division uses the resolved precision
before multiplying by priceUsd so verified fees are computed for third-asset
routes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/swap-service/src/verification/swap-verification.service.ts`:
- Around line 203-209: The async block computing actualAffiliateFeeUsd should
not call getAssetPriceUsd when actualAffiliateFeeAssetId is missing; update the
IIFE so after checking equality with swap.sellAsset.assetId and
swap.buyAsset.assetId you explicitly bail out if actualAffiliateFeeAssetId is
falsy (e.g., null/undefined/empty) and return undefined (or null) instead of
calling getAssetPriceUsd(''), otherwise call
getAssetPriceUsd(actualAffiliateFeeAssetId) as before; reference
variables/functions: actualAffiliateFeeAssetId, actualAffiliateFeeUsd,
getAssetPriceUsd, swap.sellAsset.assetId, swap.buyAsset.assetId.

In `@scripts/referral-rewards.ts`:
- Around line 84-86: The current math uses parseFloat on
swap.sellAmountCryptoBaseUnit which can overflow JS number precision; replace
this with the big-number utility computeSellAmountUsd() from
apps/swap-service/src/swaps/utils.ts (or replicate its BigNumber-based logic) to
compute volumeUsd safely. Locate the usage around the sellAsset/sellAmount
calculation in scripts/referral-rewards.ts (references: swap.sellAsset,
swap.sellAmountCryptoBaseUnit, sellAmount, volumeUsd) and call
computeSellAmountUsd(swap) (or the same BigNumber path) to obtain a precise USD
volume instead of dividing by 10**precision and using parseFloat. Ensure
types/returns are handled where volumeUsd is consumed.

---

Outside diff comments:
In `@apps/swap-service/src/swaps/utils.ts`:
- Around line 124-138: The branch that handles a third-party fee asset must use
a persisted or resolved precision instead of forcing precision = null; update
calculateFeeForSwap() (the block referencing swap.affiliateFeeAssetId,
affiliateAssetUsd and bnOrZero(...).div(...).times(...)) to first look for a
stored precision field (e.g., swap.affiliateAssetPrecision) and use it when
present, otherwise resolve the asset precision via the existing asset
registry/metadata lookup (e.g., getAssetPrecision(assetId) or the same source
used for sellAsset/buyAsset precision); only return null if priceUsd or the
resolved precision cannot be obtained after these steps. Ensure the division
uses the resolved precision before multiplying by priceUsd so verified fees are
computed for third-asset routes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 889dfaff-f580-4775-a800-3da85d13bd90

📥 Commits

Reviewing files that changed from the base of the PR and between f35cfb8 and 6d7c68e.

📒 Files selected for processing (13)
  • apps/swap-service/src/affiliate/affiliate.controller.ts
  • apps/swap-service/src/affiliate/affiliate.service.ts
  • apps/swap-service/src/swaps/swaps.service.ts
  • apps/swap-service/src/swaps/types.ts
  • apps/swap-service/src/swaps/utils.ts
  • apps/swap-service/src/utils/affiliateFeeAsset.ts
  • apps/swap-service/src/verification/__tests__/fixtures/near/swap.ts
  • apps/swap-service/src/verification/__tests__/fixtures/relay/swap.ts
  • apps/swap-service/src/verification/swap-verification.service.ts
  • packages/shared-types/src/index.ts
  • prisma/migrations/0_init_swap_service/migration.sql
  • prisma/schema/swap-service.prisma
  • scripts/referral-rewards.ts

Comment thread apps/swap-service/src/verification/swap-verification.service.ts
Comment thread scripts/referral-rewards.ts
The Relay verifier left actualAffiliateFeeUsd as a TODO and the create
path papered over it by hardcoding USDC-on-Base as Relay's fee asset
(fixed_base strategy). Now the Relay verifier resolves the fee asset's
USD price using sellAssetUsd / buyAssetUsd when they match, otherwise
fetches it, and reconcileSwap persists the result into affiliateAssetUsd
so fee aggregation can use it.

- shared-types: add actualAffiliateFeeUsd to SwapVerificationResult.
- verification: compute and emit actualAffiliateFeeUsd in the Relay
  verifier path.
- swaps.service: write verificationResult.actualAffiliateFeeUsd to
  affiliateAssetUsd during reconcile.
- affiliateFeeAsset: drop the fixed_base strategy and the hardcoded
  USDC-on-Base address; Relay now uses 'none' since the fee asset is
  determined post-verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kaladinlight
kaladinlight force-pushed the refactor/sell-asset-usd-pricing branch from 6d7c68e to 601db2e Compare May 1, 2026 00:07
@kaladinlight
kaladinlight merged commit 1cc25b5 into develop May 1, 2026
1 check passed
@kaladinlight
kaladinlight deleted the refactor/sell-asset-usd-pricing branch May 6, 2026 21:58
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