Skip to content

Bet & Accounting ERD

This diagram covers the money-flow core: bets, transactions, balances, deposits, withdrawals, tips, rakeback, and the game catalogue. All tables live in the public Postgres schema (libs/_prisma/src/schema/api.prisma).

Every balance change goes through Transaction (api.prisma:712-752) — it is the single ledger table. Bet records game-round outcomes; Deposit/Withdraw track fiat/crypto movement. UserBalance holds the per-currency wallet and vault amounts.

erDiagram
    User {
        int id PK
        string username
    }

    Bet {
        string id PK
        datetime createdAt
        datetime settledAt
        int userId FK
        enum status
        decimal amount
        decimal payout
        float multiplier
        enum currencyId
        decimal usdAmount
        decimal usdPayout
        decimal commissionGgrUsdAmount
        int gameId FK
        string roundId
    }

    Transaction {
        string id PK
        enum currencyId
        datetime createdAt
        int userId FK
        int adminUserId FK
        decimal amount
        decimal beforeBalance
        decimal afterBalance
        enum status
        enum type
        enum tag
        string roundId
        json payload
        string originalId UK
    }

    UserBalance {
        int userId PK,FK
        enum currencyId PK
        decimal amount
        decimal vaultAmount
    }

    UserStats {
        int userId PK,FK
        enum currencyId PK
        decimal totalDeposits
        decimal totalWithdraws
        decimal totalWagered
        decimal totalPayouts
        int totalBets
        decimal ltv
    }

    UserStatsUsd {
        int userId PK,FK
        decimal totalDeposits
        decimal totalWithdraws
        decimal totalWagered
        int totalBets
        decimal ltv
        datetime firstDepositAt
        datetime lastWageredAt
    }

    Deposit {
        string id PK
        decimal amount
        enum currencyId
        enum status
        enum providerId FK
        decimal usdAmount
        string externalId UK
        string txHash UK
        enum networkId
        int userId FK
        string transactionId UK,FK
        string affiliateCodeId FK
    }

    Withdraw {
        string id PK
        decimal amount
        enum currencyId
        enum status
        decimal usdAmount
        int userId FK
        string recipientAddress
        enum providerId FK
        enum networkId
        string transactionId FK
        int confirmedByUserId FK
    }

    PaymentProvider {
        enum id PK
        int priority UK
        boolean isDepositEnabled
        boolean isWithdrawEnabled
    }

    DepositWallet {
        string id PK
        enum currencyId
        string address
        enum providerId FK
        int userId FK
        enum networkId
    }

    CryptoCurrencyConfig {
        int id PK
        enum currencyId UK
        string name
        boolean withdrawsEnabled
        boolean depositsEnabled
    }

    GameIdentity {
        int id PK
        string name
        string slug UK
        enum type
        int providerId FK
        boolean enabled
        string originalSlug UK
        json images
    }

    GameProvider {
        int id PK
        string name
        string slug UK
        string prefix
        boolean enabled
    }

    GameInCategory {
        enum category PK
        int gameId PK,FK
        int order
    }

    GamePopularity {
        int gameId UK,FK
        bigint gamePlayed
        bigint launchCount
    }

    GgrCommissionRule {
        string id PK
        decimal ggrPercent
        int providerId FK
        enum category
        int gameId UK,FK
    }

    Tip {
        int id PK
        int senderId FK
        int recipientId FK
        decimal amount
        enum currencyId
        boolean isPublic
    }

    Rakeback {
        int userId PK,FK
        enum currencyId PK
        decimal instantClaimable
        decimal dailyClaimable
        decimal weeklyClaimable
        decimal monthlyClaimable
    }

    UserFavoriteGame {
        int userId PK,FK
        int gameId PK,FK
    }

    HouseGameMines {
        string gameId PK
        int userId FK
        json board
        boolean isFinished
    }

    HouseGameMonkeyRun {
        string gameId PK
        int userId FK
        decimal betAmount
        enum currencyId
        int step
        enum difficulty
        boolean isFinished
    }

    HouseGameBlackjack {
        string roundId PK
        int userId FK
        json payload
        boolean isFinished
    }

    User ||--o{ Bet : "places"
    User ||--o{ Transaction : "owns"
    User ||--o{ UserBalance : "holds"
    User ||--o{ UserStats : "per-currency stats"
    User ||--o| UserStatsUsd : "aggregate stats"
    User ||--o{ Deposit : "deposits"
    User ||--o{ Withdraw : "withdraws"
    User ||--o{ DepositWallet : "wallet"
    User ||--o{ Tip : "sends"
    User ||--o{ Tip : "receives"
    User ||--o{ Rakeback : "earns"
    User ||--o{ UserFavoriteGame : "favorites"
    User ||--o{ HouseGameMines : "plays mines"
    User ||--o{ HouseGameMonkeyRun : "plays monkey run"
    User ||--o{ HouseGameBlackjack : "plays house bj"
    Bet }o--|| GameIdentity : "on game"
    GameIdentity }o--|| GameProvider : "from"
    GameIdentity ||--o{ GameInCategory : "categorized"
    GameIdentity ||--o| GamePopularity : "popularity"
    GameIdentity ||--o| GgrCommissionRule : "commission"
    GameProvider ||--o{ GgrCommissionRule : "rules"
    Deposit }o--|| PaymentProvider : "via"
    Withdraw }o--|| PaymentProvider : "via"
    Deposit |o--o| Transaction : "creates"
    Withdraw }o--|| Transaction : "creates"
    Transaction |o--o| Transaction : "rollback of"
    DepositWallet }o--|| PaymentProvider : "issued by"
    UserFavoriteGame }o--|| GameIdentity : "game"

Table reference

Table PK Growth rate Prisma line
Bet id (uuid) High — every game round api.prisma:635
Transaction id (uuid) Very high — every balance change api.prisma:712
UserBalance (userId, currencyId) One row per user per currency api.prisma:363
UserStats (userId, currencyId) Aggregated per currency api.prisma:947
UserStatsUsd userId Single USD-denominated aggregate api.prisma:965
Deposit id (uuid) Per crypto deposit api.prisma:1184
Withdraw id (uuid) Per withdrawal request api.prisma:1136
PaymentProvider id (enum) 4 rows (CCPAYMENT, NOWPAYMENTS, SKINDECK, TEST) api.prisma:1221
DepositWallet id (uuid) One per user per network per currency api.prisma:1235
CryptoCurrencyConfig id (autoincrement) ~13 rows (one per CurrencySymbol) api.prisma:184
GameIdentity id (autoincrement) ~2000+ (all slot/house games) api.prisma:874
GameProvider id (autoincrement) ~50 (slot providers + house) api.prisma:822
GgrCommissionRule id (string) Per provider/category/game overrides api.prisma:799
Tip id (autoincrement) User-to-user tips api.prisma:555
Rakeback (userId, currencyId) Accumulated rakeback per currency api.prisma:1396
HouseGameMines gameId (uuid) Active mines sessions api.prisma:1433
HouseGameMonkeyRun gameId (uuid) Active monkey-run sessions api.prisma:1449
HouseGameBlackjack roundId (string) Active single-player BJ sessions api.prisma:1500

Key design notes

  • Transaction tags (api.prisma:578-600): 18 values covering every balance-change reason. The tag field drives reporting and filtering — it is the semantic type of the money movement.
  • Transaction self-referral: originalId links rollback transactions to their originals (api.prisma:735-741).
  • Bet.roundId uniqueness: @@unique([roundId, userId]) (api.prisma:668) prevents double-settle for the same user in the same round.
  • Balance views: UserBalanceEvo and UserBalanceDbc (api.prisma:1731-1749) are Postgres views that extract single-currency balances for quick lookups.

Cross-references

  • Flow docs: docs/flows/place-bet.md, docs/flows/deposit.md, docs/flows/withdraw.md.
  • Security findings: SF-004 (double-settle), SF-006 (no pg CHECK on balance), SF-013 (vault overdraft) in docs/security-register.md.
  • Bet settlement queue: apps/api/src/bet/queue/ — see docs/recipes/add-bullmq-queue.md.