fix(mayachain): rewrite tracker on midgard, replacing unchained affiliate indexer - #32
Conversation
…iate indexer The unchained affiliate fee indexer underreported drastically (124 fees all-time vs midgard's 379): it required the affiliate outbound and swap event in the same block (missing streaming swaps), its history was capped by the node's block-search index, and websocket gaps had no backfill. Fetch ssmaya actions from midgard instead, paging descending under the timestamp param only — mayachain midgard's fromTimestamp expects nanoseconds (unlike thorchain's seconds) and pages unstably when combined with timestamp, so the window's lower bound is enforced client-side. Price fees at historical CACAO spot derived from the USDC pool depth history (assetPriceUSD / assetPrice), replacing current-price enrichment — consistent with the thorchain tracker's /history/rune approach. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 41 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughMayachain fee retrieval now uses Midgard actions and depth history. The implementation paginates affiliate actions, derives CACAO USD prices from the USDC pool, and returns transformed fee records with USD values. ChangesMayachain Midgard fee retrieval
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant FeeService
participant MidgardDepthHistory
participant MidgardActions
FeeService->>MidgardDepthHistory: Load USDC pool depth history
MidgardDepthHistory-->>FeeService: Return CACAO USD prices
FeeService->>MidgardActions: Load paginated ssmaya affiliate actions
MidgardActions-->>FeeService: Return filtered actions
FeeService->>FeeService: Transform actions into Fees records
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
… bound Midgard's timestamp param cuts at exactly endTimestamp*1e9 nanoseconds, so fees landing in the last second of a cacheable day were excluded and cached as absent. Pass endTimestamp+1 and trim to the window client-side, matching the mayachain tracker. Verified live on both midgards: timestamp=S excludes an action at S.xxx, timestamp=S+1 includes it, fromTimestamp is inclusive. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/revenue-api/src/affiliateRevenue/mayachain/mayachain.ts (1)
61-92: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd an iteration safety cap to the pagination loop.
The
while (true)loop infetchMidgardActionshas no maximum bound on iterations. It relies entirely on the API returning a partial batch (batch.length < MIDGARD_PAGE_LIMIT) or an old-enough action to terminate. If Midgard ever returns a full page repeatedly without surfacing an action older thanstartTimestamp(for example due to an API-side pagination bug or an unexpectedly large affiliate action count), the loop runs indefinitely inside a request handler, tying up resources.Add a maximum page count (or a maximum offset) as a defensive bound, and log/throw when it is exceeded.
♻️ Proposed safety cap
+const MAX_MIDGARD_PAGES = 1000 // defensive cap; ~50k actions at MIDGARD_PAGE_LIMIT=50 + const fetchMidgardActions = async (startTimestamp: number, endTimestamp: number): Promise<MidgardAction[]> => { const actions: MidgardAction[] = [] let offset = 0 - while (true) { + for (let page = 0; page < MAX_MIDGARD_PAGES; page++) { const { data } = await withRetry('mayachain/midgard-actions', () => axios.get<MidgardActionsResponse>(`${MIDGARD_BASE_URL}/actions`, { params: { affiliate: MIDGARD_AFFILIATE, timestamp: endTimestamp + 1, limit: MIDGARD_PAGE_LIMIT, offset, }, }) ) const batch = data.actions ?? [] actions.push(...batch) if (batch.length < MIDGARD_PAGE_LIMIT) break const oldest = batch[batch.length - 1] if (actionTimestamp(oldest) < startTimestamp) break offset += MIDGARD_PAGE_LIMIT }🤖 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/revenue-api/src/affiliateRevenue/mayachain/mayachain.ts` around lines 61 - 92, Update fetchMidgardActions to enforce a maximum pagination page count or offset in addition to the existing termination checks. When the cap is exceeded, log an appropriate diagnostic or throw an error, then prevent further requests while preserving the existing filtering and normal pagination behavior.
🤖 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/revenue-api/src/affiliateRevenue/mayachain/mayachain.ts`:
- Around line 103-114: Update the affiliate payout reduction around affiliateOut
and cacaoAmount to require affiliateOut.coins[0].asset to match MAYA.CACAO
before applying CACAO precision, pricing, and asset serialization; skip
non-CACAO payouts unless they are handled using their own asset-specific fields.
---
Nitpick comments:
In `@apps/revenue-api/src/affiliateRevenue/mayachain/mayachain.ts`:
- Around line 61-92: Update fetchMidgardActions to enforce a maximum pagination
page count or offset in addition to the existing termination checks. When the
cap is exceeded, log an appropriate diagnostic or throw an error, then prevent
further requests while preserving the existing filtering and normal pagination
behavior.
🪄 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 Plus
Run ID: 1017d65f-142b-4c5d-8e16-a7feae619468
📒 Files selected for processing (3)
apps/revenue-api/src/affiliateRevenue/mayachain/constants.tsapps/revenue-api/src/affiliateRevenue/mayachain/mayachain.tsapps/revenue-api/src/affiliateRevenue/mayachain/types.ts
…O pricing The fee record hardcodes CACAO decimals (1e10), the CACAO spot price, and the CACAO assetId, so a non-CACAO affiliate out would be serialized as a mispriced CACAO fee. Skip (and warn on) any non-CACAO payout instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pricing Same latent issue as the mayachain tracker: the fee record hardcodes RUNE decimals (1e8), the RUNE spot price, and the RUNE assetId, so a non-RUNE affiliate out would be serialized as a mispriced RUNE fee. Skip (and warn on) any non-RUNE payout instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Description
Rewrites the mayachain affiliate revenue tracker to source fees from Maya Midgard (
/v2/actions?affiliate=ssmayavia our unchained proxy) instead of the unchained affiliate fee indexer endpoint, which is being removed (shapeshift/unchained#1285). Also patches the thorchain tracker for the same midgard timestamp boundary bug found in review.Motivation
The unchained indexer drastically underreported: 124 fees all-time vs 379 actually on-chain (~$21.6k total). It required the affiliate outbound and swap event in the same block (missing streaming swaps — including the largest fees on record), its history was capped by the node's block-search index (nothing before 2025-07-04), and websocket gaps had no backfill.
Design notes (all verified against the live API)
timestamp(upper bound, unix seconds) param only, stopping once a page's oldest action predates the window, with a client-side[start, end]filter. Maya Midgard'sfromTimestampexpects nanoseconds (unlike THORChain's seconds — seconds are read as ~1970 and return from genesis) and pages unstably combined withtimestamp, so it's avoided entirely.timestampparam cuts at exactlyend*1e9ns, silently dropping fees in the final second of each cacheable day (and permanently caching them as absent). Both trackers now passend+1with a client-side trim. Confirmed live on both midgards:timestamp=Sexcludes an action atS.xxxns,timestamp=S+1includes it,fromTimestampis inclusive atS.affiliate: trueout entry's CACAO amount (1e10 base units),in[0].txIDas txHash,date(ns) → seconds.assetPriceUSD / assetPriceper interval), replacing current-price enrichment — consistent with the thorchain tracker's/history/runeapproach. Cross-checked against Midgard's own per-swap USD prices (~1-2% agreement at daily granularity) and validated at the earliest ssmaya fee (2025-06-26).Validation
Follow-ups (inherited from the shared tracker pattern, not introduced here)
nextPageToken)metadata.swap.inPriceUSD/outPriceUSDcould eventually replace the depths call with exact per-swap pricing🤖 Generated with Claude Code