feat(scripts): affiliate monthly USDC payout generator - #48
Conversation
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Aggregates verified successful swaps by partnerCode for a month window and emits a Gnosis Safe CSV-airdrop file (USDC on Arbitrum) plus a JSON run record. Uses the on-chain verified affiliate fee, guarded by a deviation check against the bps-implied fee (volume x verifiedBps): swaps whose on-chain fee is wildly off (e.g. MayaChain rows that label the fee asset USDC but collect CACAO) are flagged and excluded rather than paid. Extends calculateFeeForSwap to expose actualFeeUsd + impliedFeeUsd (additive, backward-compatible) so the guard can compare them. Run: yarn affiliate-payouts generate [startDate] [endDate] Test: yarn affiliate-payouts:test Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Split scripts/affiliate-payouts.ts into scripts/affiliate-payouts/ with affiliate-payouts.ts (CLI), utils.ts (logic), types.ts, and tests. - resolveWindow takes a YYYY-MM month, defaulting to the previous calendar month; drops the start/end ISO date pair - Clearer excluded-swap category names (unpriceable / feeAnomaly / unresolvedFee) and a rewritten classification comment - buildPayouts takes a pre-lowercased affiliate map (caller owns citext normalization); normalizeRecipient renamed to normalizeAddress - swap-service calculateFeeForSwap: prefer the on-chain fee, fall back to the bps-implied fee, and skip only when neither is available (no longer defaults feeUsd to 0) - add scripts/tsconfig.json, move ts-jest isolatedModules into it, and update the yarn affiliate-payouts path Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 15 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 Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds a monthly affiliate USDC payout script ( ChangesAffiliate Monthly Payout Feature
Estimated code review effort: 3 (Moderate) | ~35 minutes Sequence Diagram(s)sequenceDiagram
participant CLI as main (CLI)
participant Generate as generate()
participant DB as Prisma/DB
participant Aggregate as aggregateByPartner
participant Artifacts as writeArtifacts
CLI->>Generate: resolveWindow(month)
Generate->>DB: query swaps + affiliates
DB-->>Generate: swap rows, affiliate rows
Generate->>Aggregate: aggregate swaps by partner
Aggregate-->>Generate: totals, anomalies, warnings
Generate->>Artifacts: buildPayouts + buildRecord
Artifacts-->>Generate: CSV + JSON written
Generate->>CLI: printSummary
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/jest.config.ts (1)
1-12: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winAlign
ts-jestwith Jest 30
ts-jest@29.2.5doesn’t support Jest 30. Upgradets-jestto a release that includespeerDependencies: jest "^29.0.0 || ^30.0.0"(or pin Jest to 29.x) to avoid install/runtime mismatches.🤖 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 `@scripts/jest.config.ts` around lines 1 - 12, The Jest config in config should be aligned with a ts-jest version that supports Jest 30; either upgrade the ts-jest dependency to a release with peer support for Jest 30 or pin Jest back to 29.x. Check the test setup around the Config export in jest.config.ts and update the package dependency versions so the transform using ts-jest matches the installed Jest major version.
🧹 Nitpick comments (2)
scripts/affiliate-payouts/types.ts (1)
20-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider deriving
FeeResultfromcalculateFeeForSwap's return type instead of duplicating it.
FeeResultmanually mirrors the shape returned byapps/swap-service/src/swaps/utils.ts'scalculateFeeForSwap. Since these two aren't linked, a future change to that function's return shape can silently drift out of sync with this type (no compile error would surface it). A type-only import (import type) doesn't pull in the ESM-only runtime deps the design doc is trying to avoid, so this is compile-time-only and safe for the "pure lib" goal.♻️ Suggested refactor
-import type { Swap as PrismaSwap } from '`@prisma/client`' +import type { Swap as PrismaSwap } from '`@prisma/client`' +import type { calculateFeeForSwap } from '../../apps/swap-service/src/swaps/utils' import type BigNumber from 'bignumber.js' -export type FeeResult = { - feeUsd: number - volumeUsd: number - verifiedBps: number - actualFeeUsd: number | null - impliedFeeUsd: number | null -} +export type FeeResult = NonNullable<ReturnType<typeof calculateFeeForSwap>>🤖 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 `@scripts/affiliate-payouts/types.ts` around lines 20 - 26, `FeeResult` is manually duplicating the return shape of `calculateFeeForSwap`, so update the type in `scripts/affiliate-payouts/types.ts` to derive from that function’s return type instead of maintaining a separate interface. Use a type-only reference to `calculateFeeForSwap` from `apps/swap-service/src/swaps/utils` so the pure-lib build still avoids runtime ESM dependencies, and keep any existing consumers of `FeeResult` working through the derived alias.docs/superpowers/specs/2026-06-30-affiliate-monthly-usdc-payout-design.md (1)
190-200: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDesign doc is stale vs. the final module structure/field names.
The JSON example totals use
skippedSwaps/anomalousSwaps, and the module-structure section references a singlescripts/affiliate-payouts-lib.tsfile with anoVerifiedFeefield. The actual implementation (perscripts/affiliate-payouts/types.ts) usesunpriceableSwaps/feeAnomalySwaps/unresolvedFeeand splits intoscripts/affiliate-payouts/{types.ts, utils.ts, affiliate-payouts.ts}. Since this design doc is the reference for the JSON payout-record schema, worth syncing it with the shipped names/layout to avoid confusing future readers of the settlement-tracking follow-up work.Also applies to: 227-247
🤖 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 `@docs/superpowers/specs/2026-06-30-affiliate-monthly-usdc-payout-design.md` around lines 190 - 200, Update the design doc’s payout schema and module-structure references to match the shipped affiliate payouts implementation: replace the stale totals field names like skippedSwaps and anomalousSwaps with the actual names used in scripts/affiliate-payouts/types.ts such as unpriceableSwaps, feeAnomalySwaps, and unresolvedFee, and revise the module layout to reflect scripts/affiliate-payouts/{types.ts, utils.ts, affiliate-payouts.ts} instead of a single scripts/affiliate-payouts-lib.ts file. Keep the JSON example and any schema prose consistent with the identifiers in the affiliate-payouts types so the reference matches the real record shape.
🤖 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 `@scripts/affiliate-payouts/utils.ts`:
- Around line 199-222: Zero/negative-fee accruals are being filtered out in
buildPayouts before they can appear in the payout record. Update buildPayouts so
accruals with feesEarnedUsd <= 0 are still emitted as PartnerPayout entries,
marked included: false with a clear excludedReason, or routed into a dedicated
review bucket, instead of being skipped. Keep the existing affiliate
lookup/normalizeAddress logic intact and make sure buildRecord can surface these
entries in the final audit trail alongside the other buckets.
---
Outside diff comments:
In `@scripts/jest.config.ts`:
- Around line 1-12: The Jest config in config should be aligned with a ts-jest
version that supports Jest 30; either upgrade the ts-jest dependency to a
release with peer support for Jest 30 or pin Jest back to 29.x. Check the test
setup around the Config export in jest.config.ts and update the package
dependency versions so the transform using ts-jest matches the installed Jest
major version.
---
Nitpick comments:
In `@docs/superpowers/specs/2026-06-30-affiliate-monthly-usdc-payout-design.md`:
- Around line 190-200: Update the design doc’s payout schema and
module-structure references to match the shipped affiliate payouts
implementation: replace the stale totals field names like skippedSwaps and
anomalousSwaps with the actual names used in scripts/affiliate-payouts/types.ts
such as unpriceableSwaps, feeAnomalySwaps, and unresolvedFee, and revise the
module layout to reflect scripts/affiliate-payouts/{types.ts, utils.ts,
affiliate-payouts.ts} instead of a single scripts/affiliate-payouts-lib.ts file.
Keep the JSON example and any schema prose consistent with the identifiers in
the affiliate-payouts types so the reference matches the real record shape.
In `@scripts/affiliate-payouts/types.ts`:
- Around line 20-26: `FeeResult` is manually duplicating the return shape of
`calculateFeeForSwap`, so update the type in
`scripts/affiliate-payouts/types.ts` to derive from that function’s return type
instead of maintaining a separate interface. Use a type-only reference to
`calculateFeeForSwap` from `apps/swap-service/src/swaps/utils` so the pure-lib
build still avoids runtime ESM dependencies, and keep any existing consumers of
`FeeResult` working through the derived alias.
🪄 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: 274f55f0-b64a-47e9-b021-58074e34557f
📒 Files selected for processing (11)
.gitignoreapps/swap-service/src/swaps/utils.tsdocs/superpowers/specs/2026-06-30-affiliate-monthly-usdc-payout-design.mdpackage.jsonscripts/affiliate-payouts/affiliate-payouts.test.tsscripts/affiliate-payouts/affiliate-payouts.tsscripts/affiliate-payouts/types.tsscripts/affiliate-payouts/utils.tsscripts/jest.config.tsscripts/tsconfig.jsontsconfig.json
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- add jest to root tsconfig types; remove scripts/tsconfig.json (scripts don't emit, so the per-workspace tsconfig split isn't needed — jest types belong at root) - remove dead root package.json jest block (matched no *.spec.ts and was never read; every jest call passes --config) - trim payouts gitignore + swap-service fee-calc comments Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Description
Adds a script that generates monthly affiliate USDC payouts from swap-fee attribution, plus a supporting swap-service fee-math tweak.
SUCCESSswaps with apartnerCodeover a calendar month, summing each partner's earned share of the verified on-chain affiliate fee (via the injected swap-service fee math) — never the bps-implied estimate.unverified,noAffiliateFee,unpriceable,unresolvedFee, feeanomaly,partnerBpsUnset) so the output explains why each swap was excluded.receiveAddress, falling back towalletAddress), checksums it, and excludes non-EVM/zero addresses. Partner-code matching is case-insensitive to match the DB citext column.yarn affiliate-payouts generate [YYYY-MM](defaults to the previous calendar month, UTC),--forceto overwrite existing artifacts.calculateFeeForSwap: prefer the on-chain fee, fall back to the bps-implied fee, and skip only when neither is available (previously defaultedfeeUsdto 0).Testing
yarn affiliate-payouts:test— 25 passing unit tests covering month-window resolution, per-partner aggregation/classification, the fee-anomaly guard, USDC formatting, address normalization, the Safe CSV output, and payout-record assembly.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests