diff --git a/src/utils/ownershipConsistency.test.ts b/src/utils/ownershipConsistency.test.ts new file mode 100644 index 0000000..51cac67 --- /dev/null +++ b/src/utils/ownershipConsistency.test.ts @@ -0,0 +1,40 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { checkOwnershipReadModelConsistency } from './ownershipConsistency'; + +test('emits a structured error log when holder balances differ from stored supply', () => { + const events: Array> = []; + + const mismatchDetected = checkOwnershipReadModelConsistency({ + creatorId: 'creator-1', + storedSupply: 100, + holderBalances: [40, 20], + detectedAt: new Date('2026-01-01T00:00:00.000Z'), + logError: entry => events.push(entry), + }); + + assert.equal(mismatchDetected, true); + assert.equal(events.length, 1); + assert.deepEqual(events[0], { + creator_id: 'creator-1', + stored_supply: 100, + computed_holder_sum: 60, + discrepancy: -40, + detected_at: '2026-01-01T00:00:00.000Z', + }); +}); + +test('does not emit a log when holder balances match stored supply', () => { + const events: Array> = []; + + const mismatchDetected = checkOwnershipReadModelConsistency({ + creatorId: 'creator-2', + storedSupply: 100, + holderBalances: [50, 50], + detectedAt: new Date('2026-01-02T00:00:00.000Z'), + logError: entry => events.push(entry), + }); + + assert.equal(mismatchDetected, false); + assert.equal(events.length, 0); +}); diff --git a/src/utils/ownershipConsistency.ts b/src/utils/ownershipConsistency.ts new file mode 100644 index 0000000..12659de --- /dev/null +++ b/src/utils/ownershipConsistency.ts @@ -0,0 +1,32 @@ +export interface OwnershipConsistencyCheckParams { + creatorId: string; + storedSupply: number; + holderBalances: number[]; + detectedAt: Date; + logError: (entry: Record) => void; +} + +export function checkOwnershipReadModelConsistency({ + creatorId, + storedSupply, + holderBalances, + detectedAt, + logError, +}: OwnershipConsistencyCheckParams): boolean { + const computedHolderSum = holderBalances.reduce((sum, balance) => sum + balance, 0); + const discrepancy = computedHolderSum - storedSupply; + + if (discrepancy === 0) { + return false; + } + + logError({ + creator_id: creatorId, + stored_supply: storedSupply, + computed_holder_sum: computedHolderSum, + discrepancy, + detected_at: detectedAt.toISOString(), + }); + + return true; +}