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.