Skip to content

Stub verifiers report SUCCESS with hasAffiliate from the swap's own claim #64

Description

@kaladinlight

Description

Five verifiers in apps/swap-service/src/verification/swap-verification.service.ts are stubs — each carries a // TODO: Implement on-chain/API verification and makes no network call — but they return verificationStatus: 'SUCCESS' with hasAffiliate derived from the row's own claimed bps:

// TODO: Implement on-chain/API verification for Cetus
const affiliateBps = swap.affiliateBps
const hasAffiliate = affiliateBps > 0

return { verificationStatus: 'SUCCESS', hasAffiliate, ... }

Affected: verifyCetus, verifySunio, verifyAvnu, verifyStonfi, verifyAcross.

That feeds straight into the payout gate:

// swaps.service.ts:467
const isAffiliateVerified = verificationResult.verificationStatus === 'SUCCESS' && verificationResult.hasAffiliate
// scripts/affiliate-payouts/utils.ts:122
if (!row.isAffiliateVerified) { ... }   // excluded, otherwise payable

So a swap on one of these five is marked affiliate-verified on the strength of its own payload, and becomes payable, with nothing checked. The gate that exists to keep unverified swaps out of revenue reports them as verified.

Contrast with the honest no-ops in the same switch — BobGateway, Debridge, Test return noAffiliateResult('SUCCESS', 'Verification not implemented'), and verifyArbitrumBridge returns hasAffiliate: false. All four yield isAffiliateVerified = false and are correctly excluded. That is the behaviour the five stubs should have.

Not currently exploitable: none of the five is in public-api's ENABLED_SWAPPER_NAMES, so no quote can be minted for them today. But swap-service will verify such a row if one arrives by any route, and enabling any of these swappers would open the path silently.

Suggested fix

Short term, make the five behave like the other unimplemented ones — return hasAffiliate: false (or noAffiliateResult('SUCCESS', 'Verification not implemented')) so they cannot set isAffiliateVerified. One line each.

Longer term, implement real verification per swapper, or add a compile-time guard so a new SwapperName cannot default into a self-certifying branch.

Related

  • 0x (verifyZrx) calls /trade-analytics/swap?txHash=… assuming it filters. It does not — the endpoint returns a paginated feed of ~200 recent trades across all chains plus a nextCursor, which the verifier never follows. So a 0x swap verifies only while it remains on the first page; older ones hit the not-found branch, which returns PENDING, leaving the swap re-polled every 30s indefinitely. Separate bug, same file.
  • CowSwap, Portals, Bebop, ButterSwap do make real calls and are not part of this issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions