Skip to content

fix(swap-service): correct Mayachain affiliate fee asset (CACAO) and value by native precision#47

Open
kaladinlight wants to merge 5 commits into
developfrom
fix/mayachain-affiliate-fee-asset
Open

fix(swap-service): correct Mayachain affiliate fee asset (CACAO) and value by native precision#47
kaladinlight wants to merge 5 commits into
developfrom
fix/mayachain-affiliate-fee-asset

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Jun 30, 2026

Copy link
Copy Markdown
Member

Description

MayaChain collects the affiliate fee in its native asset CACAO (like THORChain collects RUNE), but the fee-asset strategy mapped Mayachain to 'sell_asset'. So swaps stored affiliateFeeAssetId = the sell asset while actualAffiliateFeeAmountCryptoBaseUnit held a CACAO amount. resolveActualFeeUsd then priced that CACAO amount with the sell asset's price/precision, producing wildly wrong USD fees — observed $39,020 on a $100 swap (and a $57,712 row), which fed straight into the affiliate /stats totals and would have driven an erroneous payout.

Changes:

  • Fee asset: map Mayachain → mayachainAssetId (CACAO), mirroring Thorchain → thorchainAssetId (RUNE).
  • Read path: resolveActualFeeUsd now has explicit cases for the native fee assets — RUNE (precision 8) and CACAO (precision 10) — pricing the on-chain amount via affiliateAssetUsd (previously a fetched-but-unused column). Implemented as a switch on affiliateFeeAssetId.
  • Verifier: the shared Midgard verifier reports amounts in 1e8, so it now scales the stored affiliate fee to the fee asset's native precision (RUNE 8 = no-op, CACAO 10 = ×100), keeping storage consistent with the read path.
  • Historical data: the existing mislabeled rows on the develop DB were corrected by a one-off backfill (relabel → CACAO, native-precision amount, and historical CACAO/USD derived from the Maya ETH.USDC pool at each swap's block height). The script was applied and then removed.

Note: THORChain now values its actual RUNE fee instead of falling back to bps-implied — more accurate, but existing THORChain /stats numbers will shift slightly.

Linear: https://linear.app/shapeshift-dao/issue/SS-5709/mayachain-fee-inconsistent (SS-5709)

Closes #46

Testing

  • Full swap-service Jest suite green (58 tests); updated the maya verifier fee assertion to native precision (423777900000).
  • Validated end-to-end against the develop DB through the real calculateFeeForSwap: the $39,020 and $57,712 garbage rows collapse to ~$0.40 / ~$0.60, and every backfilled actual fee lands within <0.25% of its bps-implied value (well inside the payout deviation guard).
  • Backfill applied to the develop DB; re-run reports 0 mislabeled MayaChain swaps remaining (11 relabeled, 6 priced).

Summary by CodeRabbit

  • Bug Fixes
    • Improved affiliate fee handling for swaps on Thorchain and Mayachain so fee amounts are calculated with the correct native asset precision.
    • Fixed Mayachain fee reporting to use the chain’s native fee asset instead of treating it like a standard sell-asset swap.
    • Adjusted verification so affiliate fee values are only shown when fee data is available, reducing incorrect or missing fee calculations.

kaladinlight and others added 4 commits June 30, 2026 15:47
…sset

MayaChain, like Thorchain, collects the affiliate fee in its native asset
(CACAO), reported by the shared Midgard verifier as feeOut.coins[0].amount.
The fee-asset strategy mapped Mayachain to 'sell_asset', so the stored
affiliateFeeAssetId was the sell asset while the amount was CACAO base units.
resolveActualFeeUsd then priced the CACAO amount with the sell asset's price
and precision, producing wildly wrong USD fees (observed $39,020 on a $100
swap, which flowed into /stats and would have driven a ~$29k erroneous payout).

Map Mayachain to mayachainAssetId, mirroring Thorchain's thorchainAssetId. New
Maya swaps now fall back to the bps-implied fee (CACAO is neither sell nor buy,
so precision is unknown) exactly like Thorchain — bounded and correct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drafted one-off backfill to correct pre-fix MayaChain swaps whose
affiliateFeeAssetId was set to the sell asset instead of CACAO. For each swap
it reads the Midgard action (block height + CACAO affiliate fee out), fetches
the ETH.USDC pool at that height for historical CACAO/USD, and writes:
  affiliateFeeAssetId                    = CACAO
  actualAffiliateFeeAmountCryptoBaseUnit = Midgard 1e8 amount → CACAO native 1e10
  affiliateAssetUsd                      = historical CACAO/USD

Dry-run by default; --apply to write. Not yet applied. The backfilled
affiliateAssetUsd/amount are correct but remain unused until resolveActualFeeUsd
resolves the native fee-asset precision (deferred); until then Maya fees fall
back to the bps-implied value, which is bounded and correct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
resolveActualFeeUsd previously returned null when the affiliate fee asset was
neither the sell nor buy asset, so the actual on-chain fee for Thorchain (RUNE)
and Maya (CACAO) was never valued — affiliateAssetUsd was a fetched-but-dead
column. Add explicit cases for thorchainAssetId (precision 8) and mayachainAssetId
(precision 10), pricing the stored amount with affiliateAssetUsd.

The stored amount must be in native base units for those precisions. Midgard
reports in 1e8, so the shared verifier now scales the affiliate fee to the fee
asset's native precision (RUNE 8 = no-op, CACAO 10 = x100). Historical Maya rows
are corrected by scripts/backfill-maya-fee-asset.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Maya fee-asset backfill has been applied; the one-off script (and its yarn
entry / tsconfig include) is no longer needed. Also reformats verifyThorchain
config formatting in the Midgard verifier.

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

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes inconsistent Mayachain affiliate fee reporting by changing the Mayachain fee asset strategy to a fixed native asset ID, adding native-asset precision handling (RUNE/CACAO) in fee USD resolution, and applying chain-specific precision conversion when computing affiliate fee amounts from Midgard data, with one test expectation updated accordingly.

Changes

Affiliate fee precision fix

Layer / File(s) Summary
Mayachain affiliate fee asset strategy
apps/swap-service/src/utils/affiliateFeeAsset.ts
SWAPPER_FEE_STRATEGY for Mayachain now resolves to mayachainAssetId directly instead of using the 'sell_asset' strategy.
Fee USD resolution for native chain assets
apps/swap-service/src/swaps/utils.ts
Adds thorchainAssetId/mayachainAssetId imports and RUNE_PRECISION/CACAO_PRECISION constants; replaces the sell/buy if/else in resolveActualFeeUsd with a switch that handles native THORChain/Mayachain affiliate fee assets, defaulting to null precision otherwise.
Midgard fee amount precision conversion
apps/swap-service/src/verification/swap-verification.service.ts, apps/swap-service/src/verification/__tests__/maya.test.ts
Passes feeAssetPrecision (8 for Thorchain, 10 for Maya) into verifyMidgardSwap; actualAffiliateFeeAmountCryptoBaseUnit is now computed via thorchainToNativePrecision guarded by affiliate/fee-output presence; updates the corresponding Maya test expectation.

Sequence Diagram(s)

sequenceDiagram
  participant SwapVerificationService
  participant verifyMidgardSwap
  participant thorchainToNativePrecision
  participant resolveActualFeeUsd

  SwapVerificationService->>verifyMidgardSwap: verify swap (feeAssetPrecision=8/10)
  verifyMidgardSwap->>thorchainToNativePrecision: convert feeOut coin amount
  thorchainToNativePrecision-->>verifyMidgardSwap: actualAffiliateFeeAmountCryptoBaseUnit
  verifyMidgardSwap-->>SwapVerificationService: verification result with fee amount

  SwapVerificationService->>resolveActualFeeUsd: resolve affiliate fee USD
  resolveActualFeeUsd->>resolveActualFeeUsd: switch on affiliateFeeAssetId
  resolveActualFeeUsd-->>SwapVerificationService: fee USD (with correct precision)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

A rabbit hopped through chains so wide,
RUNE and CACAO, side by side 🐇
No more fees that swing askew,
Now precision sees them through.
Hop, hop, hooray — the math rings true! 🥕

🚥 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: fixing Mayachain affiliate fee handling with native CACAO precision.
Linked Issues check ✅ Passed The changes address #46 by mapping Mayachain to CACAO and converting fee values using native precision end-to-end.
Out of Scope Changes check ✅ Passed No clearly unrelated code changes are evident; the modified files all support the affiliate-fee fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mayachain-affiliate-fee-asset

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@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: 1

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

Inline comments:
In `@apps/swap-service/src/verification/swap-verification.service.ts`:
- Around line 393-396: `verifySwap()` in `swap-verification.service.ts` should
preserve the missing-coin guard when computing
`actualAffiliateFeeAmountCryptoBaseUnit`; the current `feeOut.coins[0].amount`
access can throw if `feeOut` has no first coin. Update the affiliate-fee mapping
so it only calls `thorchainToNativePrecision` when `hasAffiliate`, `feeOut`, and
`feeOut.coins[0]` are all present, otherwise leave the field `undefined`. Use
the existing `verifySwap` affiliate-fee block and
`feeOut`/`thorchainToNativePrecision` symbols to locate the change.
🪄 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: 1174e7b6-9fb6-4197-a1b9-5cfd74a46e5d

📥 Commits

Reviewing files that changed from the base of the PR and between 0422a84 and 972069e.

📒 Files selected for processing (4)
  • apps/swap-service/src/swaps/utils.ts
  • apps/swap-service/src/utils/affiliateFeeAsset.ts
  • apps/swap-service/src/verification/__tests__/maya.test.ts
  • apps/swap-service/src/verification/swap-verification.service.ts

Comment on lines +393 to +396
actualAffiliateFeeAmountCryptoBaseUnit:
hasAffiliate && feeOut
? thorchainToNativePrecision(feeOut.coins[0].amount, config.feeAssetPrecision)
: undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Keep the missing-coin guard when converting the affiliate fee.

Line 395 now dereferences feeOut.coins[0].amount unconditionally. If Midgard returns an affiliate out entry with no first coin, this throws and verifySwap() falls back to PENDING instead of leaving actualAffiliateFeeAmountCryptoBaseUnit undefined like before.

Suggested fix
       actualAffiliateFeeAmountCryptoBaseUnit:
-        hasAffiliate && feeOut
-          ? thorchainToNativePrecision(feeOut.coins[0].amount, config.feeAssetPrecision)
+        hasAffiliate && feeOut?.coins?.[0]?.amount
+          ? thorchainToNativePrecision(feeOut.coins[0].amount, config.feeAssetPrecision)
           : undefined,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
actualAffiliateFeeAmountCryptoBaseUnit:
hasAffiliate && feeOut
? thorchainToNativePrecision(feeOut.coins[0].amount, config.feeAssetPrecision)
: undefined,
actualAffiliateFeeAmountCryptoBaseUnit:
hasAffiliate && feeOut?.coins?.[0]?.amount
? thorchainToNativePrecision(feeOut.coins[0].amount, config.feeAssetPrecision)
: undefined,
🤖 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/verification/swap-verification.service.ts` around lines
393 - 396, `verifySwap()` in `swap-verification.service.ts` should preserve the
missing-coin guard when computing `actualAffiliateFeeAmountCryptoBaseUnit`; the
current `feeOut.coins[0].amount` access can throw if `feeOut` has no first coin.
Update the affiliate-fee mapping so it only calls `thorchainToNativePrecision`
when `hasAffiliate`, `feeOut`, and `feeOut.coins[0]` are all present, otherwise
leave the field `undefined`. Use the existing `verifySwap` affiliate-fee block
and `feeOut`/`thorchainToNativePrecision` symbols to locate the change.

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.

Mayachain Fee inconsistent

1 participant