From 252453d46f02305c5841164c8d44cc7da5b7c22c Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:21:37 -0600 Subject: [PATCH 1/3] fix(swaps): skip fee calc quietly for unverified swaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The partner swaps listing (getAffiliateSwaps) passes every partnerCode swap through calculateFeeForSwap, including failed/unverified ones with no affiliateVerificationDetails — which logged a misleading "Verified swap ... missing affiliate bps" WARN on every fetch. Add an early return on !isAffiliateVerified: those legitimately have no fee, so return null silently. The WARN now fires only for the genuinely unexpected case (a verified swap missing bps/sell amount). Co-Authored-By: Claude Opus 4.8 --- apps/swap-service/src/swaps/__tests__/utils.test.ts | 9 +++++++++ apps/swap-service/src/swaps/utils.ts | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/apps/swap-service/src/swaps/__tests__/utils.test.ts b/apps/swap-service/src/swaps/__tests__/utils.test.ts index 1fa17a6..9d0d7c9 100644 --- a/apps/swap-service/src/swaps/__tests__/utils.test.ts +++ b/apps/swap-service/src/swaps/__tests__/utils.test.ts @@ -10,6 +10,7 @@ import { calculateFeeForSwap } from '../utils' const makeSwap = (overrides: Partial = {}): Swap => ({ swapId: 'test-swap', + isAffiliateVerified: true, sellAsset: { assetId: 'eip155:1/slip44:60', precision: 18 }, buyAsset: { assetId: 'eip155:1/erc20:0xusdc', precision: 6 }, sellAssetUsd: '2000', @@ -30,6 +31,14 @@ const makeSwap = (overrides: Partial = {}): Swap => describe('calculateFeeForSwap volume reconstruction', () => { afterEach(() => jest.restoreAllMocks()) + it('returns null without warning for a swap that is not affiliate-verified', () => { + const warn = jest.spyOn(Logger.prototype, 'warn').mockImplementation(() => undefined) + + // e.g. a failed / unverified swap surfaced by the partner swaps listing + expect(calculateFeeForSwap(makeSwap({ isAffiliateVerified: false, affiliateVerificationDetails: null }))).toBeNull() + expect(warn).not.toHaveBeenCalled() + }) + it('uses the sell-side USD as volume for a 0-bps swap when the sell price is present', () => { const result = calculateFeeForSwap(makeSwap()) diff --git a/apps/swap-service/src/swaps/utils.ts b/apps/swap-service/src/swaps/utils.ts index f23361b..2b9a0ed 100644 --- a/apps/swap-service/src/swaps/utils.ts +++ b/apps/swap-service/src/swaps/utils.ts @@ -168,6 +168,11 @@ export const calculateFeeForSwap = ( actualFeeUsd: number | null impliedFeeUsd: number | null } | null => { + // Only affiliate-verified swaps can carry a fee. Failed / pending / no-affiliate swaps + // legitimately have none, and callers like the swaps listing pass them through — return + // quietly without warning. Past this point a missing field is genuinely unexpected. + if (!swap.isAffiliateVerified) return null + const verifiedBps = swap.affiliateVerificationDetails?.affiliateBps if (verifiedBps === undefined) { logger.warn(`Verified swap ${swap.swapId} missing affiliate bps in verification details, skipping`) From ed896edf2eae3594d9210a22efc9a58c7c2f216c Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:53:37 -0600 Subject: [PATCH 2/3] fix(swaps): clear affiliate details on failed verification The FAILED short-circuit in verifySwap set isAffiliateVerified=false but left any prior affiliateVerificationDetails intact, so a previously-verified swap re-processed while FAILED could carry stale details. Null them out so the invariant holds: isAffiliateVerified=false always implies null details. This lets calculateFeeForSwap rely solely on the !isAffiliateVerified early return. Co-Authored-By: Claude Opus 4.8 --- apps/swap-service/src/swaps/swaps.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/swap-service/src/swaps/swaps.service.ts b/apps/swap-service/src/swaps/swaps.service.ts index 79358fc..2d757f8 100644 --- a/apps/swap-service/src/swaps/swaps.service.ts +++ b/apps/swap-service/src/swaps/swaps.service.ts @@ -399,7 +399,8 @@ export class SwapsService { return toSwap( await this.prisma.swap.update({ where: { swapId: swap.swapId }, - data: { verificationStatus: 'FAILED', isAffiliateVerified: false }, + // Clear any stale details so isAffiliateVerified=false always implies null details. + data: { verificationStatus: 'FAILED', isAffiliateVerified: false, affiliateVerificationDetails: Prisma.DbNull }, }), ) } From 9677de2f2de6a75a2f465d4c783b976e8b5b7dd1 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:54:58 -0600 Subject: [PATCH 3/3] style(swaps): format failed-verification data block, trim comments Co-Authored-By: Claude Opus 4.8 --- apps/swap-service/src/swaps/swaps.service.ts | 7 +++++-- apps/swap-service/src/swaps/utils.ts | 3 --- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/swap-service/src/swaps/swaps.service.ts b/apps/swap-service/src/swaps/swaps.service.ts index 2d757f8..24c3ac4 100644 --- a/apps/swap-service/src/swaps/swaps.service.ts +++ b/apps/swap-service/src/swaps/swaps.service.ts @@ -399,8 +399,11 @@ export class SwapsService { return toSwap( await this.prisma.swap.update({ where: { swapId: swap.swapId }, - // Clear any stale details so isAffiliateVerified=false always implies null details. - data: { verificationStatus: 'FAILED', isAffiliateVerified: false, affiliateVerificationDetails: Prisma.DbNull }, + data: { + verificationStatus: 'FAILED', + isAffiliateVerified: false, + affiliateVerificationDetails: Prisma.DbNull, + }, }), ) } diff --git a/apps/swap-service/src/swaps/utils.ts b/apps/swap-service/src/swaps/utils.ts index 2b9a0ed..77be2d0 100644 --- a/apps/swap-service/src/swaps/utils.ts +++ b/apps/swap-service/src/swaps/utils.ts @@ -168,9 +168,6 @@ export const calculateFeeForSwap = ( actualFeeUsd: number | null impliedFeeUsd: number | null } | null => { - // Only affiliate-verified swaps can carry a fee. Failed / pending / no-affiliate swaps - // legitimately have none, and callers like the swaps listing pass them through — return - // quietly without warning. Past this point a missing field is genuinely unexpected. if (!swap.isAffiliateVerified) return null const verifiedBps = swap.affiliateVerificationDetails?.affiliateBps