Skip to content

Leaderboards

Purpose

The Leaderboards screen manages tournament-style leaderboards (daily / weekly / monthly), schedules, and prize distributions. Players see live ranks on the player site; admins configure them, watch progress, and manually award prizes to winners.

Audience

Marketing (configure prizes), customer support ("am I really #5?" disputes), risk (validate non-bot top ranks).

Path in admin-fe

Screen URL Page
Leaderboards list /leaderboards ebit-admin-fe/src/app/(dashboard)/leaderboards/page.tsx
Leaderboard detail /leaderboards/[slug] ebit-admin-fe/src/app/(dashboard)/leaderboards/[slug]/page.tsx

Backing API endpoints

Endpoint Source
GET /admin/leaderboards/schedule (list schedule slots) apps/api/src/leaderboard/admin.leaderboard.controller.ts:45
GET /admin/leaderboards/schedule/:id (one slot) apps/api/src/leaderboard/admin.leaderboard.controller.ts:51
POST /admin/leaderboards/schedule/:id (upsert schedule) apps/api/src/leaderboard/admin.leaderboard.controller.ts:57
GET /admin/leaderboards (list active leaderboards) apps/api/src/leaderboard/admin.leaderboard.controller.ts:72
GET /admin/leaderboards/:leaderboardId/users (paginated leaderboard) apps/api/src/leaderboard/admin.leaderboard.controller.ts:79
GET /admin/leaderboards/:leaderboardId/users/:userId (drill one user's rank) apps/api/src/leaderboard/admin.leaderboard.controller.ts:91
GET /admin/leaderboards/:leaderboardId (config + summary) apps/api/src/leaderboard/admin.leaderboard.controller.ts:109
POST /admin/leaderboards/give-prize apps/api/src/leaderboard/admin.leaderboard.controller.ts:103

Frontend wiring: ebit-admin-fe/src/queries/leaderboards/.

Key actions

Action Required permission API call DB tables touched Audit-logged?
List leaderboards leaderboard.view GET /admin/leaderboards Leaderboard yes
List schedule slots leaderboard.view GET /admin/leaderboards/schedule LeaderboardSchedule yes
Edit schedule (start/end, prize pool) leaderboard.edit POST /admin/leaderboards/schedule/:id LeaderboardSchedule yes
List ranks for a leaderboard leaderboard.view GET /admin/leaderboards/:leaderboardId/users LeaderboardUser, User yes
Drill one user leaderboard.view GET /admin/leaderboards/:leaderboardId/users/:userId same yes
Manually award a prize leaderboard.give-prize POST /admin/leaderboards/give-prize LeaderboardPrize, Transaction, Account yes

The runtime that calculates rank deltas runs on a BullMQ leaderboard queue (apps/api/src/leaderboard/queue/) — admin only configures and reads.

Filters and views

  • StatusUPCOMING / LIVE / ENDED.
  • Type / scope — daily / weekly / monthly / event.
  • Game filter — leaderboards can be game-specific.
  • Top-N view — pagination defaults to top 100; supports infinite scroll.

Common workflows

  1. Configure a weekly leaderboard. Marketing opens /leaderboards, picks the week's slot, edits prize pool: 1st = 1000 USDT, 2-5 = 200 each, 6-20 = 50 each. Saves via POST /admin/leaderboards/schedule/:id. Player site picks up at next refresh.
  2. Review live ranks. CS views /leaderboards/[slug]. Spot-check top 10 — are any flagged as bot or banned? If so, trigger a recalculation: engineering republishes the queue (out-of-band).
  3. Award prizes after end. When a leaderboard ends, marketing reviews top 20, click Give Prize per row. Backend writes to player accounts via Transaction. Prizes log into LeaderboardPrize table.
  4. Dispute "I should be #3". Drill into the user via GET /admin/leaderboards/:lbId/users/:userId. See score breakdown + bet contribution timestamps. Cross-reference with bets-history.md.
  5. Cancel a leaderboard. Set status CANCELLED via schedule edit. Prize pool not awarded; player site shows cancellation note.

Edge cases / gotchas

  • give-prize is per-user, not bulk. For 50 winners, that's 50 calls. Front-end loops; engineering may script out-of-band for huge tournaments.
  • Bot users are filtered out at rank-calculation time by the BullMQ queue — bot bets don't count.
  • Banned users keep their rank until manually removed. Operator should manually give-prize to the next eligible player.
  • Leaderboard score formulas vary. Some leaderboards weight by wager, some by net loss, some by # of plays. The formula is captured in Leaderboard.scoringMethod and applied in the BullMQ runtime.
  • Schedule edits during a live leaderboard are dangerous. Changing prize pool mid-run can confuse players. Avoid; or post a customer notice via handover/customer-comms/.
  • give-prize writes a single Transaction. It does NOT mark the position as awarded automatically — operator must keep the record.
  • Per-game leaderboards — slug-scoped (e.g., "Plinko Weekly"). Schema link is Leaderboard.gameSlug.
  • Time zones: leaderboard windows are UTC. A "weekly" leaderboard runs Mon 00:00 UTC to Sun 23:59 UTC.
  • Player-side leaderboard view: apps/api/src/leaderboard/leaderboard.controller.ts + leaderboard.gateway.controller.ts
  • BullMQ queue: apps/api/src/leaderboard/queue/
  • Per-user contributing bets: bets-history.md
  • Bot exclusion: bots.md
  • Audit: admin-logs.md
  • Permission keys: libs/auth/src/permissions/const.ts:69-79

Sequence — manually awarding a prize after leaderboard ends

sequenceDiagram
    actor mkt as Marketing
    participant admin-fe
    participant api
    participant pg as Postgres
    mkt->>admin-fe: open /leaderboards/[slug]/ (status=ENDED)
    admin-fe->>api: GET /admin/leaderboards/:id/users?page=1
    api->>api: PermissionGuard('leaderboard.view')
    api->>pg: SELECT LeaderboardUser ORDER BY rank
    pg-->>api: rows
    api-->>admin-fe: top 100
    loop top N winners
        mkt->>admin-fe: click Give Prize on row
        admin-fe->>api: POST /admin/leaderboards/give-prize { leaderboardId, userId, amount, currency }
        api->>api: PermissionGuard('leaderboard.give-prize')
        api->>pg: BEGIN — INSERT Transaction — UPDATE Account — INSERT LeaderboardPrize — INSERT AdminActionLog — COMMIT
        api-->>admin-fe: 200 OK
    end
    admin-fe-->>mkt: rows show prize-awarded badge