Objective
Ensure pack opening is deterministic, openable, and transparently disclosed across all clients.Canonical data model
Pack.maxReveal: number of cards revealed per open.PackCollectible.quantityandPackCollectible.weight: weighted draw pool.Collectible.rarity: rarity bucket (COMMON,RARE,EPIC,LEGENDARY,MYTHIC).Product.metadata.dropRates: disclosure projection fields, not draw engine source-of-truth.
Source of truth
- Draw execution:
server/minting-actions.tsand weighted random logic. - Odds math: centralized helper logic in backend pack-odds utilities.
- Purchase/openability gating: purchase, pack, and minting server routes/actions.
Openability invariants
A pack is openable only if:- At least one
PackCollectibleexists. - At least one entry has
weight > 0. sum(quantity) >= maxReveal.
API disclosure contract
GET /api/products: pack metadata includes odds/disclosure fields.GET /api/packs/{id}/odds: canonical odds payload for targeted disclosure rendering.POST /api/packs/{purchaseId}/open: validates ownership/state and performs open behavior.
Testing requirements
- Unit tests for odds math and invariants.
- Integration checks for openability enforcement and disclosure payload correctness.
- User-facing failure messages must remain actionable and explicit.
