diff --git a/packages/backend/src/partner/partner.service.ts b/packages/backend/src/partner/partner.service.ts index b81970f..4692fe6 100644 --- a/packages/backend/src/partner/partner.service.ts +++ b/packages/backend/src/partner/partner.service.ts @@ -3,12 +3,16 @@ import { PrismaService } from '@/database/prisma.service'; import { AccountReportDto } from './dto/account-report.dto'; import { getChainById } from '@polypay/shared'; +const BASE_CHAIN_IDS = [8453, 84532]; + @Injectable() export class PartnerService { constructor(private readonly prisma: PrismaService) {} async generateAccountReport(dto: AccountReportDto): Promise { - const where: Record = {}; + const where: Record = { + chainId: { notIn: BASE_CHAIN_IDS }, + }; if (dto.startDate || dto.endDate) { where.createdAt = {}; @@ -26,38 +30,70 @@ export class PartnerService { const accounts = await this.prisma.account.findMany({ where, - select: { address: true, createdAt: true, chainId: true }, + include: { + signers: { + include: { user: true }, + orderBy: { createdAt: 'asc' }, + }, + }, orderBy: { createdAt: 'desc' }, }); - return this.generateCSV(accounts, dto.includeChainId); + // Get creator (or first signer as fallback) commitment for each account + const commitments = accounts + .map((a) => { + const creator = a.signers.find((s) => s.isCreator); + return (creator || a.signers[0])?.user.commitment; + }) + .filter((c): c is string => !!c); + const addressMap = await this.buildCommitmentToAddressMap(commitments); + + return this.generateCSV(accounts, addressMap, dto.includeChainId); } - private getExplorerAddressUrl(chainId: number, address: string): string { - try { - const chain = getChainById(chainId); - return `${chain.blockExplorers!.default.url}/address/${address}`; - } catch { - return address; - } + private async buildCommitmentToAddressMap( + commitments: string[], + ): Promise> { + if (commitments.length === 0) return new Map(); + + const uniqueCommitments = [...new Set(commitments)]; + const loginHistories = await this.prisma.loginHistory.findMany({ + where: { commitment: { in: uniqueCommitments } }, + orderBy: { createdAt: 'desc' }, + distinct: ['commitment'], + }); + + return new Map( + loginHistories.map((lh) => [lh.commitment, lh.walletAddress]), + ); } private generateCSV( - accounts: { address: string; createdAt: Date; chainId: number }[], + accounts: { + address: string; + createdAt: Date; + chainId: number; + signers: { + isCreator: boolean; + user: { commitment: string }; + }[]; + }[], + addressMap: Map, includeChainId?: boolean, ): string { const totalLine = [`Total Accounts,${accounts.length}`, '']; const header = includeChainId - ? 'Address,Explorer Address,Created At,Chain ID,Chain Name' - : 'Address,Explorer Address,Created At'; + ? 'Multisig Account Address,EOA,Created At,Chain ID,Chain Name' + : 'Multisig Account Address,EOA,Created At'; const rows = accounts.map((account) => { - const addressLink = this.getExplorerAddressUrl( - account.chainId, - account.address, - ); - const base = `${account.address},${addressLink},${account.createdAt.toISOString()}`; + const creator = account.signers.find((s) => s.isCreator); + const commitment = (creator || account.signers[0])?.user.commitment; + const eoaWallet = commitment + ? addressMap.get(commitment) || 'UNKNOWN' + : 'UNKNOWN'; + const base = `${account.address},${eoaWallet},${account.createdAt.toISOString()}`; if (includeChainId) { let chainName: string; try {