Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 54 additions & 18 deletions packages/backend/src/partner/partner.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> {
const where: Record<string, unknown> = {};
const where: Record<string, unknown> = {
chainId: { notIn: BASE_CHAIN_IDS },
};

if (dto.startDate || dto.endDate) {
where.createdAt = {};
Expand All @@ -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<Map<string, string>> {
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<string, string>,
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 {
Expand Down
Loading