Skip to content

feat(swap-verification): Chainflip affiliate verification via explorer GraphQL#52

Open
kaladinlight wants to merge 1 commit into
developfrom
feat/swap-chainflip-verification
Open

feat(swap-verification): Chainflip affiliate verification via explorer GraphQL#52
kaladinlight wants to merge 1 commit into
developfrom
feat/swap-chainflip-verification

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Jul 7, 2026

Copy link
Copy Markdown
Member

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 (GetSwapByNativeIdswapRequestByNativeId), which is partner-agnostic and keyed by the swap-request nativeId we already store as metadata.chainflipSwapId.

  • Attribution: ShapeShift routes via Chainflip's shared "Broker as a Service" and is attached as an AFFILIATE beneficiary. We match that beneficiary by SS58 (cFMeDPtP…) — the submitter broker is shared across BaaS customers, so the affiliate account is the only trustworthy anchor.
  • Fee: commission is always realized in USDC, read from commissions.groupedAggregates(groupBy: ASSET) (which aggregates DCA chunks into one group). affiliateFeeAsset.ts maps Chainflip → usdcAssetId and resolveActualFeeUsd gains a USDC branch, so the real on-chain fee is used instead of the bps-implied fallback.
  • Sell amount: uses the executed swap input (excludes any refunded portion on partial FoK fills), not the gross deposit.
  • Edge cases: empty commission → PENDING (self-heals the BaaS-Confirmed-early vs explorer-indexing race; also the full-refund shape, which never actually reaches verification); >1 groups or a non-USDC asset → FAILED to force inspection; GraphQL errors are logged.
  • Rename: SwapVerificationResult.actualAffiliateFeeUsdactualAffiliateAssetUsd — it's a per-unit price mirroring affiliateAssetUsd, not a total (the old name invited that misread).
  • The GetSwapByNativeId query is trimmed to only the consumed fields.

Note: the test fixture is derived from a real explorer response for swap 1640208. A fixture from a swap initiated through the swap widget will be added as a follow-up.

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.
  • Fixture + all amounts/addresses validated against the live Chainflip explorer (swaps 1640208, 1635648 DCA, 563102/744024 refunds).
  • tsc --noEmit clean.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR replaces Chainflip's REST-based swap verification with a GraphQL explorer query, adding new types and query constants for the GetSwapByNativeId operation. It introduces USDC-specific affiliate fee handling, renames actualAffiliateFeeUsd to actualAffiliateAssetUsd throughout, renames verifyMaya to verifyMayachain, and adds a new environment variable, tests, fixtures, and a design doc.

Changes

Chainflip GraphQL verification and affiliate USDC handling

Layer / File(s) Summary
Env config and shared type rename
apps/swap-service/src/env.ts, packages/shared-types/src/index.ts
Adds VITE_CHAINFLIP_EXPLORER_URL env var and renames SwapVerificationResult.actualAffiliateFeeUsd to actualAffiliateAssetUsd.
USDC affiliate fee asset resolution
apps/swap-service/src/utils/affiliateFeeAsset.ts, apps/swap-service/src/swaps/utils.ts
Chainflip's fee strategy now resolves to usdcAssetId directly, and resolveActualFeeUsd adds a USDC precision branch.
Chainflip GraphQL query and types
apps/swap-service/src/verification/chainflip.query.ts, apps/swap-service/src/verification/types.ts
Adds GetSwapByNativeId query constants and explorer-specific types (ChainflipExplorerResponse, beneficiary, commission aggregate), removing the old ChainflipSwapResponse type.
verifyChainflip rewrite and field renames
apps/swap-service/src/verification/swap-verification.service.ts, apps/swap-service/src/swaps/swaps.service.ts
Rewrites verifyChainflip to query the GraphQL explorer, resolve the affiliate beneficiary and commissions, renames verifyMaya to verifyMayachain, and updates verifyRelay/persistence to use actualAffiliateAssetUsd.
Fixtures and tests
apps/swap-service/src/verification/__tests__/chainflip.test.ts, apps/swap-service/src/verification/__tests__/fixtures/chainflip/*
Adds Chainflip swap and explorer response fixtures plus a full test suite covering success, DCA aggregation, partial refunds, mismatched broker, missing affiliate, pending, and failure scenarios.
Design doc
docs/superpowers/specs/2026-07-06-chainflip-swap-verification-design.md
Documents the Chainflip verification design, data source, and test plan.

Unrelated formatting change

Layer / File(s) Summary
Script reformatting
scripts/affiliate-payouts/utils.ts
Reformats a multi-line statement with no behavior 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
Loading

Possibly related PRs

  • shapeshift/microservices#37: Both PRs modify swap-verification.service.ts, refactoring the verifier's return shape and affiliate USD/asset fields used by verification results.
  • shapeshift/microservices#40: Both PRs touch verifyRelay in the same service, renaming the affiliate USD field and deriving actualAffiliateFeeAssetId.
  • shapeshift/microservices#47: Both PRs modify resolveActualFeeUsd in swaps/utils.ts to add asset-specific native precision mappings.

Poem

A rabbit hops through GraphQL fields so bright,
USDC commissions counted just right 🥕
No more REST calls, no bearer token quest,
Just query the explorer and let it rest.
Chainflip verified, affiliates in sight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: Chainflip affiliate verification implemented via explorer GraphQL.
✨ 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 feat/swap-chainflip-verification

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: 3

🧹 Nitpick comments (2)
apps/swap-service/src/verification/__tests__/fixtures/chainflip/response.json (1)

14-57: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Beneficiary node order contradicts the query's orderBy: TYPE_ASC.

The GET_SWAP_BY_NATIVE_ID_QUERY requests swapRequestBeneficiariesBySwapRequestId(orderBy: TYPE_ASC), which would sort AFFILIATE before SUBMITTER alphabetically, but this fixture lists SUBMITTER first. 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 win

GraphQL errors are logged but don't gate processing.

If the explorer returns errors alongside a non-null swapRequest (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

📥 Commits

Reviewing files that changed from the base of the PR and between bdc8da0 and 8d0c59e.

📒 Files selected for processing (13)
  • apps/swap-service/src/env.ts
  • apps/swap-service/src/swaps/swaps.service.ts
  • apps/swap-service/src/swaps/utils.ts
  • apps/swap-service/src/utils/affiliateFeeAsset.ts
  • apps/swap-service/src/verification/__tests__/chainflip.test.ts
  • apps/swap-service/src/verification/__tests__/fixtures/chainflip/response.json
  • apps/swap-service/src/verification/__tests__/fixtures/chainflip/swap.ts
  • apps/swap-service/src/verification/chainflip.query.ts
  • apps/swap-service/src/verification/swap-verification.service.ts
  • apps/swap-service/src/verification/types.ts
  • docs/superpowers/specs/2026-07-06-chainflip-swap-verification-design.md
  • packages/shared-types/src/index.ts
  • scripts/affiliate-payouts/utils.ts

Comment thread apps/swap-service/src/verification/swap-verification.service.ts
Comment thread docs/superpowers/specs/2026-07-06-chainflip-swap-verification-design.md Outdated
Comment thread docs/superpowers/specs/2026-07-06-chainflip-swap-verification-design.md Outdated
…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>
@kaladinlight kaladinlight force-pushed the feat/swap-chainflip-verification branch from 8d0c59e to 2b47b8d Compare July 7, 2026 16:36
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