Skip to content

Transaction History (Strict Ledger)

The SDK provides access to the user's immutable transaction ledger. This is scoped per walletId and uses a strict accounting schema to ensure full audit compliance.

Fetching Transactions

You must provide the walletId. You can also paginate and filter by transaction type.

javascript
const transactions = await sdk.wallet.getTransactions('wallet_usd', {
  limit: 20,
  offset: 0,
  type: 'DEPOSIT' // Optional: Filter by DEPOSIT, WITHDRAWAL, BET, WIN, etc.
});

transactions.forEach(tx => {
  console.log(`${tx.direction} ${tx.amount} ${tx.currency} - ${tx.status}`);
});

Pagination uses limit + offset and always returns newest first.

Strict Ledger Schema

Every financial movement in the platform uses this exact schema. Your backend database must implement this structure. BET and WIN entries are written by game rounds under a strict hold-then-settle contract; a PENDING-status BET entry holds the stake at round start, and resolution flips its status and writes the conditional WIN (or REFUND); see Round History & Ledger Contract.

typescript
interface Transaction {
  id: string;
  walletId: string;
  type: 'DEPOSIT' | 'WITHDRAWAL' | 'BET' | 'WIN' | 'BONUS_CREDIT' | 'BONUS_EXPIRY' | 'BONUS_FORFEIT' | 'BONUS_CONVERSION' | 'REFUND' | 'ADJUSTMENT' | 'TOURNAMENT_PRIZE';
  
  // Accounting Logic: Amount is ALWAYS a positive number.
  // Direction dictates whether money was added (CREDIT) or removed (DEBIT).
  direction: 'CREDIT' | 'DEBIT'; 
  amount: number; 
  
  currency: string;
  status: 'PENDING' | 'COMPLETED' | 'FAILED' | 'REJECTED';
  
  // The real balance of the wallet immediately AFTER this transaction was applied.
  // Important for casino auditors to trace the exact flow of funds.
  // For entries that move bonusBalance (BONUS_CREDIT, BONUS_EXPIRY, BONUS_FORFEIT),
  // this is the UNCHANGED real balance; see bonusBalanceAfter below.
  balanceAfter: number; 
  
  // Present ONLY on entries that change bonusBalance (BONUS_CREDIT, BONUS_EXPIRY,
  // BONUS_FORFEIT, BONUS_CONVERSION): the wallet's bonusBalance immediately after
  // this entry. Absent on all other types.
  bonusBalanceAfter?: number;
  
  reference?: string; // e.g., Game Round ID or Provider Transaction ID
  metadata?: Record<string, string | number | boolean>; // operator/system annotations; primitive passthrough
  createdAt: string;   // ISO 8601 Date string
}

Pagination

getTransactions uses limit + offset pagination:

  • Default limit: 20 · maximum limit: 100 (values above 100 are clamped to 100).
  • Deterministic ordering: newest first; sorted by createdAt descending, with id as the stable tiebreaker, so pages are reproducible and no row is skipped or duplicated across pages.
javascript
const page1 = await sdk.wallet.getTransactions('wallet_usd', { limit: 20, offset: 0 });
const page2 = await sdk.wallet.getTransactions('wallet_usd', { limit: 20, offset: 20 });

Audit Trail Integrity

Even rejected or failed transactions (like a withdrawal denied due to insufficient funds) are recorded in the ledger with a REJECTED or FAILED status. This ensures a complete audit trail for casino compliance.

Append-only: ledger entries are never modified or deleted. Only the status field ever transitions (from PENDING to a terminal value); type, amounts, currency, and balances are immutable from creation.

Tournament prize settlement

Tournament prizes settle automatically; the player never claims them:

text
GIVEN a tournament with opted-in players whose status flips to COMPLETED
      (the flip itself is date-driven: endDate passed and final scores were fixed)
WHEN prize settlement runs; exactly once per tournament, triggered by the
      status flip to COMPLETED
THEN every ranked player with a non-zero prize receives exactly ONE CREDIT
     ledger entry of type TOURNAMENT_PRIZE, amount = the player's prize in the
     tournament's currency, into the player's wallet in that currency;
     creating the wallet first if the player has none in that currency
     (wallet creation stays platform-controlled; players never create wallets).

GIVEN tournament prize settlement has already run for a tournament
WHEN any process attempts to settle it again
THEN nothing happens; settlement is exactly-once per tournament.
     A repeated settlement would double-credit an append-only ledger,
     which is forbidden.

GIVEN a player whose prize was settled
WHEN they look for the payout
THEN it appears via getTransactions (type TOURNAMENT_PRIZE), and the
     tournament-completed / prize-settled notification arrives on the
     engagement event channel (see the Tournament Engine documentation);
     polling getTransactions is the always-available fallback.

Prize amounts are determined by the tournament's prize structure; a typed, closed discriminated union beginning with CASH (a share of the prizePool). Non-cash variants (e.g., free spins granted via the Bonus Engine) are not defined; the structure is simply ready for them.