diff --git a/apps/swap-service/src/affiliate/__tests__/affiliate.service.test.ts b/apps/swap-service/src/affiliate/__tests__/affiliate.service.test.ts index 3785ea7..26ac7a1 100644 --- a/apps/swap-service/src/affiliate/__tests__/affiliate.service.test.ts +++ b/apps/swap-service/src/affiliate/__tests__/affiliate.service.test.ts @@ -90,8 +90,6 @@ describe('AffiliateService.createAffiliate', () => { }) describe('AffiliateService attribution reads', () => { - const address = '0x1111111111111111111111111111111111111111' - // Pull the `where` from the first prisma.swap.findMany call as a typed object so the // assertions below don't trip the no-unsafe-any lint rules on jest's `any`-typed calls. const whereOfFirstCall = (findMany: jest.Mock): Record => { @@ -99,37 +97,11 @@ describe('AffiliateService attribution reads', () => { return args.where } - it('getAffiliateStatsByAddress filters through the affiliate relation, never partnerAddress', async () => { - const findMany = jest.fn().mockResolvedValue([]) - const service = new AffiliateService(makePrismaMock(undefined, findMany)) - - await service.getAffiliateStatsByAddress(address, {}) - - const where = whereOfFirstCall(findMany) - expect(where).toMatchObject({ - affiliate: { OR: [{ walletAddress: address }, { receiveAddress: address }] }, - }) - expect(where).not.toHaveProperty('partnerAddress') - }) - - it('getAffiliateSwapsByAddress filters through the affiliate relation, never partnerAddress', async () => { - const findMany = jest.fn().mockResolvedValue([]) - const service = new AffiliateService(makePrismaMock(undefined, findMany)) - - await service.getAffiliateSwapsByAddress(address, { limit: 50 }) - - const where = whereOfFirstCall(findMany) - expect(where).toMatchObject({ - affiliate: { OR: [{ walletAddress: address }, { receiveAddress: address }] }, - }) - expect(where).not.toHaveProperty('partnerAddress') - }) - - it('getAffiliateSwapsByPartnerCode filters directly on partnerCode, with no join or address', async () => { + it('getAffiliateSwaps filters directly on partnerCode, with no join or address', async () => { const findMany = jest.fn().mockResolvedValue([]) const service = new AffiliateService(makePrismaMock(undefined, findMany)) - await service.getAffiliateSwapsByPartnerCode('goodcode', { limit: 50 }) + await service.getAffiliateSwaps('goodcode', { limit: 50 }) const where = whereOfFirstCall(findMany) expect(where).toMatchObject({ partnerCode: 'goodcode' }) @@ -137,11 +109,11 @@ describe('AffiliateService attribution reads', () => { expect(where).not.toHaveProperty('partnerAddress') }) - it('getAffiliateStatsByPartnerCode filters directly on partnerCode, with no join or address', async () => { + it('getAffiliateStats filters directly on partnerCode, with no join or address', async () => { const findMany = jest.fn().mockResolvedValue([]) const service = new AffiliateService(makePrismaMock(undefined, findMany)) - const result = await service.getAffiliateStatsByPartnerCode('goodcode', {}) + const result = await service.getAffiliateStats('goodcode', {}) const where = whereOfFirstCall(findMany) expect(where).toMatchObject({ partnerCode: 'goodcode', status: 'SUCCESS', isAffiliateVerified: true }) diff --git a/apps/swap-service/src/affiliate/affiliate.controller.ts b/apps/swap-service/src/affiliate/affiliate.controller.ts index cc4922f..f6b1bcf 100644 --- a/apps/swap-service/src/affiliate/affiliate.controller.ts +++ b/apps/swap-service/src/affiliate/affiliate.controller.ts @@ -32,16 +32,12 @@ export class AffiliateController { @Get('swaps') async getSwaps(@Query() query: AffiliateSwapsQueryDto) { - if (query.partnerCode) return this.affiliateService.getAffiliateSwapsByPartnerCode(query.partnerCode, query) - if (query.address) return this.affiliateService.getAffiliateSwapsByAddress(query.address, query) - throw new BadRequestException('partnerCode or address is required') + return this.affiliateService.getAffiliateSwaps(query.partnerCode, query) } @Get('stats') async getStats(@Query() query: AffiliateStatsQueryDto) { - if (query.partnerCode) return this.affiliateService.getAffiliateStatsByPartnerCode(query.partnerCode, query) - if (query.address) return this.affiliateService.getAffiliateStatsByAddress(query.address, query) - throw new BadRequestException('partnerCode or address is required') + return this.affiliateService.getAffiliateStats(query.partnerCode, query) } @Get(':address') diff --git a/apps/swap-service/src/affiliate/affiliate.service.ts b/apps/swap-service/src/affiliate/affiliate.service.ts index 722e708..c3cb4b5 100644 --- a/apps/swap-service/src/affiliate/affiliate.service.ts +++ b/apps/swap-service/src/affiliate/affiliate.service.ts @@ -7,13 +7,7 @@ import { PaginatedSwaps } from '../swaps/types' import { calculateFeeForSwap, getPartnerFeeRate, toSwap } from '../swaps/utils' import { getNextCursor, swapCursorArgs } from '../utils/pagination' -import type { - AffiliateStatsQueryDto, - AffiliateStatsResult, - AffiliateSwapsQueryDto, - CreateAffiliateDto, - UpdateAffiliateDto, -} from './types' +import type { AffiliateStatsResult, CreateAffiliateDto, UpdateAffiliateDto } from './types' import { isReservedPartnerCode } from './utils' @Injectable() @@ -64,50 +58,7 @@ export class AffiliateService { return this.prisma.affiliate.update({ where: { walletAddress }, data: updateData }) } - async getAffiliateStatsByAddress(address: string, options: AffiliateStatsQueryDto): Promise { - const { startDate, endDate } = options - - const items = await this.prisma.swap.findMany({ - where: { - affiliate: { OR: [{ walletAddress: address }, { receiveAddress: address }] }, - status: 'SUCCESS', - isAffiliateVerified: true, - ...(startDate || endDate - ? { - createdAt: { - ...(startDate && { gte: startDate }), - ...(endDate && { lte: endDate }), - }, - } - : {}), - }, - }) - - let totalSwaps = 0 - let totalVolumeUsd = 0 - let totalFeesEarnedUsd = 0 - - for (const item of items) { - const swap = toSwap(item) - - const fee = calculateFeeForSwap(swap) - if (!fee) continue - - const rate = getPartnerFeeRate(fee.verifiedBps, swap.partnerBps) - - totalSwaps++ - totalVolumeUsd += fee.volumeUsd - totalFeesEarnedUsd += fee.feeUsd * rate - } - - return { - totalSwaps, - totalVolumeUsd: totalVolumeUsd.toFixed(2), - totalFeesEarnedUsd: totalFeesEarnedUsd.toFixed(2), - } - } - - async getAffiliateStatsByPartnerCode( + async getAffiliateStats( partnerCode: string, options: { startDate?: Date; endDate?: Date }, ): Promise { @@ -153,31 +104,7 @@ export class AffiliateService { } } - async getAffiliateSwapsByAddress(address: string, options: AffiliateSwapsQueryDto): Promise { - const { startDate, endDate, limit, cursor } = options - - const items = await this.prisma.swap.findMany({ - ...swapCursorArgs(limit, cursor), - where: { - affiliate: { OR: [{ walletAddress: address }, { receiveAddress: address }] }, - ...(startDate || endDate - ? { - createdAt: { - ...(startDate && { gte: startDate }), - ...(endDate && { lte: endDate }), - }, - } - : {}), - }, - }) - - return { - swaps: items.map(toSwap), - nextCursor: getNextCursor(items, limit), - } - } - - async getAffiliateSwapsByPartnerCode( + async getAffiliateSwaps( partnerCode: string, options: { startDate?: Date; endDate?: Date; limit: number; cursor?: string }, ): Promise { diff --git a/apps/swap-service/src/affiliate/types.ts b/apps/swap-service/src/affiliate/types.ts index e517d15..ef8a945 100644 --- a/apps/swap-service/src/affiliate/types.ts +++ b/apps/swap-service/src/affiliate/types.ts @@ -14,13 +14,8 @@ export interface AffiliateStatsResult { } export class AffiliateStatsQueryDto { - @IsOptional() - @IsEthereumAddress() - address?: string - - @IsOptional() @Matches(PARTNER_CODE_REGEX, { message: PARTNER_CODE_MESSAGE }) - partnerCode?: string + partnerCode: string @IsOptional() @Type(() => Date) @@ -34,13 +29,8 @@ export class AffiliateStatsQueryDto { } export class AffiliateSwapsQueryDto extends PaginationQueryDto { - @IsOptional() - @IsEthereumAddress() - address?: string - - @IsOptional() @Matches(PARTNER_CODE_REGEX, { message: PARTNER_CODE_MESSAGE }) - partnerCode?: string + partnerCode: string @IsOptional() @Type(() => Date) diff --git a/apps/swap-service/src/swaps/swaps.controller.ts b/apps/swap-service/src/swaps/swaps.controller.ts index adc9902..cab0cfd 100644 --- a/apps/swap-service/src/swaps/swaps.controller.ts +++ b/apps/swap-service/src/swaps/swaps.controller.ts @@ -48,6 +48,6 @@ export class SwapsController { @Query('startDate', OptionalDatePipe) startDate?: Date, @Query('endDate', OptionalDatePipe) endDate?: Date, ) { - return this.swapsService.calculateAffiliateFeesByPartnerCode(partnerCode, startDate, endDate) + return this.swapsService.calculateAffiliateFees(partnerCode, startDate, endDate) } } diff --git a/apps/swap-service/src/swaps/swaps.service.ts b/apps/swap-service/src/swaps/swaps.service.ts index 85b3ab5..9d98521 100644 --- a/apps/swap-service/src/swaps/swaps.service.ts +++ b/apps/swap-service/src/swaps/swaps.service.ts @@ -311,7 +311,7 @@ export class SwapsService { } } - async calculateAffiliateFeesByPartnerCode(partnerCode: string, startDate?: Date, endDate?: Date): Promise { + async calculateAffiliateFees(partnerCode: string, startDate?: Date, endDate?: Date): Promise { logger.log( `Calculating affiliate fees for ${partnerCode}, period: ${startDate?.toISOString()} - ${endDate?.toISOString()}`, )