@@ -506,21 +506,9 @@ export async function getNftOwner(
506506
507507// ─── Account summary helpers ──────────────────────────────────────────────────
508508
509- /**
510- * Incrementally update materialized aggregates for every address touched by
511- * `records`. Called inside the same logical write as upsertTransfers so the
512- * two tables never diverge.
513- *
514- * Strategy:
515- * 1. Accumulate per-(address, contractId) deltas in memory.
516- * 2. Emit one raw UPSERT per unique pair — O(unique addresses) DB round-trips.
517- *
518- * Using raw SQL because Prisma cannot do arithmetic on string-typed NUMERIC columns.
519- */
520509export async function upsertAccountSummaries ( records : TransferRecord [ ] ) : Promise < void > {
521510 if ( records . length === 0 ) return ;
522511
523- // Accumulate deltas keyed by "address|contractId"
524512 const deltas = new Map <
525513 string ,
526514 { address : string ; contractId : string ; sent : bigint ; received : bigint ; count : number ; lastAt : Date }
@@ -567,10 +555,6 @@ export async function upsertAccountSummaries(records: TransferRecord[]): Promise
567555 }
568556}
569557
570- /**
571- * Return all asset rows for a given address, optionally filtered to one contract.
572- * O(1) — reads directly from the materialized AccountSummary table.
573- */
574558export async function getAccountSummary ( address : string , contractId ?: string ) {
575559 return prisma . accountSummary . findMany ( {
576560 where : {
@@ -653,6 +637,36 @@ export async function queryAccountSummaries(params: AccountSummaryQueryParams) {
653637 } ;
654638}
655639
640+ // ─── Balance aggregate query ──────────────────────────────────────────────────
641+ export type BalanceRow = {
642+ contractId : string ;
643+ balance : string ;
644+ } ;
645+
646+ export async function queryBalances ( address : string ) : Promise < BalanceRow [ ] > {
647+ const end = dbQueryDurationSeconds . startTimer ( { operation : "queryBalances" } ) ;
648+
649+ const rows = await prisma . $queryRaw < BalanceRow [ ] > `
650+ SELECT
651+ "contractId",
652+ (
653+ COALESCE(SUM(CASE WHEN "toAddress" = ${ address } THEN CAST("amount" AS NUMERIC) ELSE 0 END), 0) -
654+ COALESCE(SUM(CASE WHEN "fromAddress" = ${ address } THEN CAST("amount" AS NUMERIC) ELSE 0 END), 0)
655+ )::TEXT AS "balance"
656+ FROM "TokenTransfer"
657+ WHERE "toAddress" = ${ address } OR "fromAddress" = ${ address }
658+ GROUP BY "contractId"
659+ HAVING (
660+ COALESCE(SUM(CASE WHEN "toAddress" = ${ address } THEN CAST("amount" AS NUMERIC) ELSE 0 END), 0) -
661+ COALESCE(SUM(CASE WHEN "fromAddress" = ${ address } THEN CAST("amount" AS NUMERIC) ELSE 0 END), 0)
662+ ) != 0
663+ ORDER BY "contractId"
664+ ` ;
665+
666+ end ( ) ;
667+ return rows ;
668+ }
669+
656670// ─── Combined address query ───────────────────────────────────────────────────
657671export type AllTransfersQueryParams = {
658672 address : string ;
0 commit comments