Skip to content

fix(swap-verification): value MAYAChain affiliate fee with raw CACAO amount - #53

Merged
kaladinlight merged 3 commits into
developfrom
fix/maya-affiliate-fee-precision
Jul 8, 2026
Merged

fix(swap-verification): value MAYAChain affiliate fee with raw CACAO amount#53
kaladinlight merged 3 commits into
developfrom
fix/maya-affiliate-fee-precision

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Jul 8, 2026

Copy link
Copy Markdown
Member

Description

MAYAChain Midgard reports the native CACAO amount in its native 1e10 precision, unlike pool/external assets which are normalized to 1e8 (Maya docs: "CACAO is the only asset which is expressed in 1e10 format"). The shared Midgard verifier was running the affiliate feeOut amount through thorchainToNativePrecision(..., feeAssetPrecision=10), multiplying it by 100. Since the downstream read path (resolveActualFeeUsd) then divides by 1e10 and prices with the real CACAO price, this inflated on-chain MAYAChain affiliate fee USD by 100×.

Example (swap d16d8a13, ~70 bps on a ~$5 swap): stored 329870380000$3.76, vs. correct raw 3298703800$0.038.

Confirmed three ways: the Maya docs, an empirical mayanode-vs-Midgard pool-depth cross-check (both report CACAO at ~6.1e16 = 1e10, not 100× apart), and the tx's own numbers reconciling to 70 bps only under the 1e10 interpretation.

Changes:

  • Store feeOut.coins[0].amount raw — the affiliate fee is always the chain's native asset (RUNE/CACAO), which Midgard already reports in native precision. Removed the feeAssetPrecision config. Thorchain is unaffected (RUNE native = 8 = THORCHAIN_PRECISION, previously a no-op).
  • Attribute verified affiliates that collect no fee: hasAffiliate now keys off the affiliate-address match alone (dropping the && !!feeOut gate), reporting affiliateBps with a '0' fee amount for genuine 0-bps swaps.
  • calculateFeeForSwap guards verifiedBps with === undefined so real 0-bps affiliate swaps aren't skipped.
  • Naming consistency (no behavior change): verifyMayaverifyMayachain, maya.test.tsmayachain.test.ts, fixtures/maya/fixtures/mayachain/, mayaResponse/mayaSwapmayachainResponse/mayachainSwap.

Testing

  • yarn test verification/__tests__ — 50/50 pass, including updated MAYAChain expectations (raw 4237779000; '0' for the affiliate-with-no-payout case) and unchanged Thorchain expectations.

Summary by CodeRabbit

  • Bug Fixes
    • Improved swap verification so affiliate activity is now recognized even when no affiliate payout is present, showing a zero fee instead of omitting affiliate details.
    • Fixed fee validation to correctly accept a zero value where appropriate.
    • Updated verification coverage for Mayachain and Thorchain scenarios to better reflect expected swap results.

kaladinlight and others added 2 commits July 8, 2026 10:06
Maya Midgard reports the native CACAO fee in its native 1e10 precision
(pool/external assets are 1e8), so the shared Midgard verifier must store
feeOut.coins[0].amount raw. It was running the amount through
thorchainToNativePrecision (×100 for Maya), inflating on-chain fee USD 100×
(e.g. $3.76 vs correct $0.038 for a ~70bps fee on a ~$5 swap). Removed the
feeAssetPrecision config; Thorchain is unaffected (RUNE native = 8).

Also attribute verified affiliates that collect no fee: hasAffiliate now keys
off the affiliate-address match alone (dropping the && feeOut monkey patch),
reporting affiliateBps with a '0' fee amount for 0-bps swaps. calculateFeeForSwap
guards verifiedBps with === undefined so real 0-bps affiliate swaps aren't skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ency

Align the MAYAChain verifier naming with the fully-spelled Thorchain
convention: verifyMaya -> verifyMayachain, test fixtures maya/ -> mayachain/
(mayaResponse/mayaSwap -> mayachainResponse/mayachainSwap), maya.test.ts ->
mayachain.test.ts, and the RUNE/CACAO precision comment. No behavior change.

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

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kaladinlight, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab4a0eea-f99a-4e9b-b7b7-d34bfc464968

📥 Commits

Reviewing files that changed from the base of the PR and between 74ba50c and 52fd954.

📒 Files selected for processing (2)
  • apps/swap-service/src/swaps/__tests__/utils.test.ts
  • apps/swap-service/src/swaps/utils.ts
📝 Walkthrough

Walkthrough

This PR modifies swap affiliate verification logic in swap-verification.service.ts: hasAffiliate now depends solely on memo-matched affiliate address (not requiring a discovered feeOut), and actualAffiliateFeeAmountCryptoBaseUnit always defaults to '0' without precision conversion. Maya verification is renamed to Mayachain, verifiedBps check tightened, and tests updated accordingly.

Changes

Affiliate verification and fee logic update

Layer / File(s) Summary
verifiedBps missing-value check
apps/swap-service/src/swaps/utils.ts
Missing-verifiedBps guard now checks explicitly for undefined instead of falsy, so 0 is no longer treated as missing; comment wording updated to “THORChain/MAYAChain”.
Mayachain routing and affiliate/fee logic
apps/swap-service/src/verification/swap-verification.service.ts
verifySwap routes Mayachain swaps to new verifyMayachain, which wraps verifyMidgardSwap; feeAssetPrecision removed from config; hasAffiliate now based only on memo-matched address; actualAffiliateFeeAmountCryptoBaseUnit always set (defaulting to '0') without precision conversion.
Thorchain affiliate test expectations
apps/swap-service/src/verification/__tests__/thorchain.test.ts
Test for no-fee-paid-out scenario updated to expect hasAffiliate: true, affiliateBps: 60, actualAffiliateFeeAmountCryptoBaseUnit: '0' instead of undefined affiliate fields.
Mayachain test fixture rename and scenario updates
apps/swap-service/src/verification/__tests__/mayachain.test.ts
Test suite renamed from verifyMaya to verifyMayachain, fixtures switched to mayachainResponse/Mayachain swap JSON across all scenarios; no-payout affiliate case now expects hasAffiliate: true with affiliateBps: 60 and '0' fee amount.

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

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant SwapVerificationService
  participant verifyMayachain
  participant verifyMidgardSwap
  Caller->>SwapVerificationService: verifySwap(swap)
  SwapVerificationService->>verifyMayachain: verifyMayachain(swap)
  verifyMayachain->>verifyMidgardSwap: verifyMidgardSwap(swap, config)
  verifyMidgardSwap->>verifyMidgardSwap: hasAffiliate = affiliateAddress === config.affiliate
  verifyMidgardSwap->>verifyMidgardSwap: actualAffiliateFeeAmountCryptoBaseUnit = feeOut?.coins[0]?.amount ?? '0'
  verifyMidgardSwap-->>SwapVerificationService: verification result
Loading

Possibly related PRs

  • shapeshift/microservices#39: Both PRs modify the shared verifyMidgardSwap affiliate detection and actualAffiliateFeeAmountCryptoBaseUnit logic plus corresponding Mayachain/Thorchain tests.
  • shapeshift/microservices#47: Both PRs modify verifyMidgardSwap affiliate detection and fee-amount computation, including precision/feeOut handling, with matching test updates.
  • shapeshift/microservices#48: Both PRs modify swaps/utils.ts's calculateFeeForSwap fee-math logic around verifiedBps and related fee handling.

Poem

A hop, a fix, a bps set right,
Zero fees now stand in the light 🐇
Maya renamed to Mayachain's call,
Affiliates counted, one and all,
Thump thump — the tests all pass tonight! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: storing MAYAChain affiliate fees using raw CACAO amount.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/maya-affiliate-fee-precision

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.

@kaladinlight

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

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)

171-201: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle the 0-bps case in the volumeUsd fallback

affiliateBps can now be 0, and if sellAmountUsd is null this div(verifiedBps) path produces NaN/Infinity, which then poisons volumeUsd and the aggregated affiliate stats. Guard verifiedBps === 0 explicitly here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/swap-service/src/swaps/utils.ts` around lines 171 - 201, The volume
fallback in utils.ts can divide by zero when
affiliateVerificationDetails.affiliateBps is 0 and sellAmountUsd is null,
causing NaN/Infinity in the affiliate stats path. Update the logic in the swap
fee calculation block around verifiedBps, computeSellAmountUsd, and volumeUsd to
explicitly handle verifiedBps === 0 before the div(verifiedBps) fallback, and
return a safe null/skip behavior instead of producing an invalid volume.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/swap-service/src/swaps/utils.ts`:
- Around line 171-201: The volume fallback in utils.ts can divide by zero when
affiliateVerificationDetails.affiliateBps is 0 and sellAmountUsd is null,
causing NaN/Infinity in the affiliate stats path. Update the logic in the swap
fee calculation block around verifiedBps, computeSellAmountUsd, and volumeUsd to
explicitly handle verifiedBps === 0 before the div(verifiedBps) fallback, and
return a safe null/skip behavior instead of producing an invalid volume.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d45bd13-1bfc-45b0-ac72-097c8a152fc6

📥 Commits

Reviewing files that changed from the base of the PR and between bdc8da0 and 74ba50c.

📒 Files selected for processing (6)
  • apps/swap-service/src/swaps/utils.ts
  • apps/swap-service/src/verification/__tests__/fixtures/mayachain/response.json
  • apps/swap-service/src/verification/__tests__/fixtures/mayachain/swap.ts
  • apps/swap-service/src/verification/__tests__/mayachain.test.ts
  • apps/swap-service/src/verification/__tests__/thorchain.test.ts
  • apps/swap-service/src/verification/swap-verification.service.ts

calculateFeeForSwap now lets verifiedBps === 0 through so real 0-bps affiliate
swaps aren't skipped. The volumeUsd fallback reconstructs volume from the fee
(fee × 10000 ÷ bps), which is NaN/Infinity at 0 bps and poisons aggregated
affiliate stats. When the sell price is missing at 0 bps volume is genuinely
unknown — record 0 and warn instead. Adds unit tests for the volume branches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kaladinlight
kaladinlight force-pushed the fix/maya-affiliate-fee-precision branch from ce1b05c to 52fd954 Compare July 8, 2026 17:45
@kaladinlight
kaladinlight enabled auto-merge (squash) July 8, 2026 17:47
@kaladinlight
kaladinlight merged commit 0f6ad85 into develop Jul 8, 2026
2 checks passed
@kaladinlight
kaladinlight deleted the fix/maya-affiliate-fee-precision branch July 8, 2026 17:47
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