You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Build a PaymentAggregator that maintains a live, incrementally-updated payment summary for an invoice. It must handle out-of-order payment events (from blockchain reorgs or delayed delivery), resolve conflicts via ledger sequence number, produce periodic rollup snapshots to avoid recomputing from scratch, and expose a reactive subscription API.
Acceptance Criteria
Create src/paymentAggregator.ts exporting PaymentAggregator class
PaymentAggregator accepts an initial Invoice and maintains internal state: totalFunded: bigint, percentFunded: number, payerBreakdown: Map<string, bigint>, paymentCount: number, lastLedger: number
applyPayment(payment: Payment & { ledger: number }): void — applies a payment; ignores duplicates (same payer + same ledger); if a payment with lower ledger arrives after a higher one, re-sorts and recomputes (conflict resolution)
subscribe(cb: (summary: PaymentSummary) => void): () => void — fires callback synchronously on every applyPayment call; returns unsubscribe
snapshot(): PaymentSnapshot — returns current state serializable to JSON; restore(snapshot: PaymentSnapshot): void — restores state without reprocessing all payments
getTopPayers(n: number): { address: string, amount: bigint }[] — returns top N payers sorted descending by amount
percentFunded must never exceed 100 even if totalFunded > invoiceTotal
All exported from src/index.ts with full types
Tests: out-of-order payment conflict resolution, duplicate payment ignored, snapshot/restore round-trip, subscriber fires on each apply, unsubscribe works, top payers sorted correctly with tie-breaking by address alphabetically
Context
Invoice and Payment types are in src/types.ts — Payment may need a ledger: number field added
src/snapshot.ts already exists — align snapshot format conventions
bigint arithmetic only — no Number() conversion for amounts
Description
Build a
PaymentAggregatorthat maintains a live, incrementally-updated payment summary for an invoice. It must handle out-of-order payment events (from blockchain reorgs or delayed delivery), resolve conflicts via ledger sequence number, produce periodic rollup snapshots to avoid recomputing from scratch, and expose a reactive subscription API.Acceptance Criteria
src/paymentAggregator.tsexportingPaymentAggregatorclassPaymentAggregatoraccepts an initialInvoiceand maintains internal state:totalFunded: bigint,percentFunded: number,payerBreakdown: Map<string, bigint>,paymentCount: number,lastLedger: numberapplyPayment(payment: Payment & { ledger: number }): void— applies a payment; ignores duplicates (same payer + same ledger); if a payment with lower ledger arrives after a higher one, re-sorts and recomputes (conflict resolution)subscribe(cb: (summary: PaymentSummary) => void): () => void— fires callback synchronously on everyapplyPaymentcall; returns unsubscribesnapshot(): PaymentSnapshot— returns current state serializable to JSON;restore(snapshot: PaymentSnapshot): void— restores state without reprocessing all paymentsgetTopPayers(n: number): { address: string, amount: bigint }[]— returns top N payers sorted descending by amountpercentFundedmust never exceed 100 even iftotalFunded > invoiceTotalsrc/index.tswith full typesContext
InvoiceandPaymenttypes are insrc/types.ts—Paymentmay need aledger: numberfield addedsrc/snapshot.tsalready exists — align snapshot format conventionsbigintarithmetic only — noNumber()conversion for amounts