Skip to content
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { PrismaClient } from '@prisma/client';
import { decimalToBigInt } from '../../../shared/database/index.js';
import type { AnalyticsReader } from '../domain/index.js';

/**
Expand All @@ -18,10 +19,7 @@ export function createPrismaAnalyticsReader(prisma: PrismaClient): AnalyticsRead
});
return grouped.map((row) => ({
token: row.token,
// See src/modules/escrow/infrastructure/prisma-escrow-repository.ts's
// comment: Decimal.toString() switches to exponential notation past
// 21 digits, which BigInt() can't parse — .toFixed() can't.
releasedAmount: BigInt(row._sum.amount?.toFixed() ?? '0'),
releasedAmount: row._sum.amount === null ? 0n : decimalToBigInt(row._sum.amount),
releasedCount: row._count._all,
}));
},
Expand Down
9 changes: 3 additions & 6 deletions src/modules/escrow/infrastructure/prisma-escrow-repository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Escrow as PrismaEscrow, PrismaClient } from '@prisma/client';
import { decimalToBigInt } from '../../../shared/database/index.js';
import type { Escrow, EscrowRepository } from '../domain/index.js';

function toDomain(record: PrismaEscrow): Escrow {
Expand All @@ -9,12 +10,8 @@ function toDomain(record: PrismaEscrow): Escrow {
recipientAddress: record.recipientAddress,
driverAddress: record.driverAddress,
token: record.token,
// Prisma's Decimal (decimal.js) switches to exponential notation past 21
// digits by default — `.toString()` on an i128::MAX-sized value (39
// digits) yields "1.7...e+38", which BigInt() can't parse. `.toFixed()`
// always returns a plain fixed-point string regardless of magnitude.
amount: BigInt(record.amount.toFixed()),
platformFee: record.platformFee === null ? null : BigInt(record.platformFee.toFixed()),
amount: decimalToBigInt(record.amount),
platformFee: record.platformFee === null ? null : decimalToBigInt(record.platformFee),
status: record.status,
disputedBy: record.disputedBy,
disputedAt: record.disputedAt,
Expand Down
24 changes: 24 additions & 0 deletions src/shared/database/decimal.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Prisma } from '@prisma/client';
import { describe, expect, it } from 'vitest';
import { decimalToBigInt } from './decimal.js';

describe('decimalToBigInt', () => {
it('converts a small integer Decimal', () => {
expect(decimalToBigInt(new Prisma.Decimal('42'))).toBe(42n);
});

it('converts zero', () => {
expect(decimalToBigInt(new Prisma.Decimal(0))).toBe(0n);
});

it('converts a negative value', () => {
expect(decimalToBigInt(new Prisma.Decimal('-123456789'))).toBe(-123456789n);
});

it('converts an i128::MAX-sized value that .toString() would render exponentially', () => {
// The bug this helper guards against: Decimal.toString() on a 39-digit
// value yields "1.7014118346046923e+38", which BigInt() can't parse.
const i128Max = '170141183460469231731687303715884105727';
expect(decimalToBigInt(new Prisma.Decimal(i128Max))).toBe(BigInt(i128Max));
});
});
15 changes: 15 additions & 0 deletions src/shared/database/decimal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Prisma } from '@prisma/client';

/**
* Converts a Prisma `Decimal` to a `BigInt`.
*
* Prisma's Decimal (decimal.js) switches to exponential notation past 21
* significant digits by default — `.toString()` on an i128::MAX-sized amount
* (39 digits; the columns are `Decimal(39, 0)`, see prisma/schema.prisma)
* yields "1.7...e+38", which `BigInt()` can't parse. `.toFixed()` always
* returns a plain fixed-point string regardless of magnitude, so Decimal
* amounts must be converted to BigInt via `.toFixed()`, never `.toString()`.
*/
export function decimalToBigInt(value: Prisma.Decimal): bigint {
return BigInt(value.toFixed());
}
1 change: 1 addition & 0 deletions src/shared/database/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { getPrismaClient, disconnectPrisma } from './prisma-client.js';
export { decimalToBigInt } from './decimal.js';
Loading