feat(swap-verification): Chainflip affiliate verification via explorer GraphQL#52
feat(swap-verification): Chainflip affiliate verification via explorer GraphQL#52kaladinlight wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughThis PR replaces Chainflip's REST-based swap verification with a GraphQL explorer query, adding new types and query constants for the ChangesChainflip GraphQL verification and affiliate USDC handling
Unrelated formatting change
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant SwapsService
participant SwapVerificationService
participant ChainflipExplorer
SwapsService->>SwapVerificationService: verifySwap(SwapperName.Chainflip)
SwapVerificationService->>ChainflipExplorer: POST GET_SWAP_BY_NATIVE_ID_QUERY
ChainflipExplorer-->>SwapVerificationService: swapRequest with beneficiaries/commissions
SwapVerificationService->>SwapVerificationService: resolve AFFILIATE beneficiary by idSs58
SwapVerificationService->>SwapVerificationService: validate single usdc commission group
SwapVerificationService-->>SwapsService: SwapVerificationResult (actualAffiliateAssetUsd, actualAffiliateFeeAssetId)
SwapsService->>SwapsService: persist affiliateAssetUsd
Possibly related PRs
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 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
apps/swap-service/src/verification/__tests__/fixtures/chainflip/response.json (1)
14-57: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueBeneficiary node order contradicts the query's
orderBy: TYPE_ASC.The
GET_SWAP_BY_NATIVE_ID_QUERYrequestsswapRequestBeneficiariesBySwapRequestId(orderBy: TYPE_ASC), which would sortAFFILIATEbeforeSUBMITTERalphabetically, but this fixture listsSUBMITTERfirst. Tests still pass because the suite uses.find()rather than relying on order, but the fixture misrepresents the real explorer response shape for anyone using it as a reference.Suggested reorder
"nodes": [ { - "type": "SUBMITTER", - "brokerCommissionRateBps": 5, + "type": "AFFILIATE", + "brokerCommissionRateBps": 60, ... }, { - "type": "AFFILIATE", - "brokerCommissionRateBps": 60, + "type": "SUBMITTER", + "brokerCommissionRateBps": 5, ... } ]🤖 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/__tests__/fixtures/chainflip/response.json` around lines 14 - 57, The beneficiary list in the chainflip response fixture is out of order relative to GET_SWAP_BY_NATIVE_ID_QUERY’s swapRequestBeneficiariesBySwapRequestId(orderBy: TYPE_ASC), so update the beneficiaries.nodes array to match the TYPE_ASC ordering used by the query. Reorder the existing node entries in response.json so the AFFILIATE entry comes before the SUBMITTER entry, keeping all fields unchanged, to align the fixture with the real explorer response shape.apps/swap-service/src/verification/swap-verification.service.ts (1)
414-421: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winGraphQL errors are logged but don't gate processing.
If the explorer returns
errorsalongside a non-nullswapRequest(partial success), the code proceeds to parse potentially incomplete data instead of treating it as PENDING/FAILED.🤖 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 414 - 421, The Chainflip explorer response handling in swap-verification.service.ts logs GraphQL errors but still continues into swapRequest parsing, which can process partial/incomplete data. Update the error handling around the swap verification flow so that when data.errors is present in this branch, the method returns a PENDING/FAILED result immediately instead of proceeding to inspect data.data.swapRequest. Keep the existing logger.error call in the same flow, and adjust the logic near the swapRequest extraction to gate processing on the presence of errors.
🤖 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 449-459: The Chainflip verification result is omitting the
realized affiliate USD amount, causing downstream fallback to an estimate.
Update the return object in swap-verification.service.ts’s verification path to
include actualAffiliateAssetUsd from the already queried commission aggregate
(commission.sum.valueUsd), alongside the existing actualAffiliateFeeAmount and
actualAffiliateFeeAssetId fields, so swaps.service.ts can persist the real USD
value.
In `@docs/superpowers/specs/2026-07-06-chainflip-swap-verification-design.md`:
- Around line 109-130: Update the Chainflip verification type contract in
verification/types.ts to match the aliased GraphQL response the verifier
actually consumes, not the old REST-shaped model. Replace
ChainflipSwapResponse/ChainflipSwapRequest fields like depositAmount,
destinationAddress, and totalBrokerCommissionBps with the nested
swapRequest/executedSwaps/egress/beneficiaries structure used by the query, and
adjust ChainflipExplorerResponse accordingly so the verifier’s parsing matches
the payload shape returned from the explorer.
- Around line 77-103: Update the Chainflip verification spec to match the
implemented verifier: in verifyChainflip/swap verification result mapping,
replace any use of depositAmount with the executed-swap sum used to exclude
refunds, and rename actualAffiliateFeeUsd to actualAffiliateAssetUsd. Also
adjust the commission description so it reflects the aggregated commission shape
validated by the verifier instead of taking only the first swapCommissions node,
while keeping the verifyChainflip(swap) result fields aligned with the
implemented mapping.
---
Nitpick comments:
In
`@apps/swap-service/src/verification/__tests__/fixtures/chainflip/response.json`:
- Around line 14-57: The beneficiary list in the chainflip response fixture is
out of order relative to GET_SWAP_BY_NATIVE_ID_QUERY’s
swapRequestBeneficiariesBySwapRequestId(orderBy: TYPE_ASC), so update the
beneficiaries.nodes array to match the TYPE_ASC ordering used by the query.
Reorder the existing node entries in response.json so the AFFILIATE entry comes
before the SUBMITTER entry, keeping all fields unchanged, to align the fixture
with the real explorer response shape.
In `@apps/swap-service/src/verification/swap-verification.service.ts`:
- Around line 414-421: The Chainflip explorer response handling in
swap-verification.service.ts logs GraphQL errors but still continues into
swapRequest parsing, which can process partial/incomplete data. Update the error
handling around the swap verification flow so that when data.errors is present
in this branch, the method returns a PENDING/FAILED result immediately instead
of proceeding to inspect data.data.swapRequest. Keep the existing logger.error
call in the same flow, and adjust the logic near the swapRequest extraction to
gate processing on the presence of errors.
🪄 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: 0e3e3b49-3e5b-4891-b1cd-cf25002fa9a7
📒 Files selected for processing (13)
apps/swap-service/src/env.tsapps/swap-service/src/swaps/swaps.service.tsapps/swap-service/src/swaps/utils.tsapps/swap-service/src/utils/affiliateFeeAsset.tsapps/swap-service/src/verification/__tests__/chainflip.test.tsapps/swap-service/src/verification/__tests__/fixtures/chainflip/response.jsonapps/swap-service/src/verification/__tests__/fixtures/chainflip/swap.tsapps/swap-service/src/verification/chainflip.query.tsapps/swap-service/src/verification/swap-verification.service.tsapps/swap-service/src/verification/types.tsdocs/superpowers/specs/2026-07-06-chainflip-swap-verification-design.mdpackages/shared-types/src/index.tsscripts/affiliate-payouts/utils.ts
…r GraphQL Verify Chainflip swaps against the Chainflip explorer GraphQL (GetSwapByNativeId / swapRequestByNativeId) rather than the partner-scoped broker status API, which carries no affiliate data and 404s for swaps this service didn't open. - verifyChainflip: match the ShapeShift AFFILIATE beneficiary by SS58; read the realized commission from groupedAggregates (sums DCA chunks); empty commission -> PENDING (BaaS-Confirmed-early vs explorer-indexing race / full refund), >1 groups or non-USDC -> FAILED to force inspection - verifiedSellAmount uses the executed swap input (excludes refunded portion on partial FoK fills), not the gross deposit - affiliateFeeAsset.ts + resolveActualFeeUsd: Chainflip fee asset is USDC, so the real on-chain fee is used instead of the bps-implied fallback - rename SwapVerificationResult.actualAffiliateFeeUsd -> actualAffiliateAssetUsd (it is a per-unit price mirroring affiliateAssetUsd, not a total) - trimmed GetSwapByNativeId to the consumed fields; real fixture from swap 1640208; 12 tests incl. DCA aggregation and partial/full refund flows Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8d0c59e to
2b47b8d
Compare
Description
Implements Chainflip affiliate-fee verification, bringing it to parity with the relay / thorchain / mayachain verifiers.
Chainflip's broker status API (
status-by-id) is partner-scoped — it only returns swaps opened under the caller's own apiKey and carries no affiliate/commission data, so it can't verify swaps this service didn't open. Instead we query the Chainflip explorer GraphQL (GetSwapByNativeId→swapRequestByNativeId), which is partner-agnostic and keyed by the swap-request nativeId we already store asmetadata.chainflipSwapId.AFFILIATEbeneficiary. We match that beneficiary by SS58 (cFMeDPtP…) — the submitter broker is shared across BaaS customers, so the affiliate account is the only trustworthy anchor.commissions.groupedAggregates(groupBy: ASSET)(which aggregates DCA chunks into one group).affiliateFeeAsset.tsmapsChainflip → usdcAssetIdandresolveActualFeeUsdgains a USDC branch, so the real on-chain fee is used instead of the bps-implied fallback.PENDING(self-heals the BaaS-Confirmed-early vs explorer-indexing race; also the full-refund shape, which never actually reaches verification);>1groups or a non-USDC asset →FAILEDto force inspection; GraphQL errors are logged.SwapVerificationResult.actualAffiliateFeeUsd→actualAffiliateAssetUsd— it's a per-unit price mirroringaffiliateAssetUsd, not a total (the old name invited that misread).GetSwapByNativeIdquery is trimmed to only the consumed fields.Testing
yarn workspace swap-service test— 70/70 pass (12 new Chainflip cases): success, DCA multi-chunk aggregation, partial FoK refund (executed vs deposit), case-insensitive SS58 match, wrong-broker, submitter-only, empty→PENDING, multi-group→FAILED, non-USDC→FAILED, missing-id→FAILED, null/HTTP-error→PENDING.1640208,1635648DCA,563102/744024refunds).tsc --noEmitclean.🤖 Generated with Claude Code