Telegram Mini App Dice/Crash Development
Dice and Crash — two classic provably-fair genres. Dice: player bets that a random number will be above or below threshold. Crash: multiplier grows over time, player must cashout before it crashes. Both work well in Telegram Mini App format — simple mechanics, fast rounds, social element.
Provably Fair: Mathematical Honesty
Main difference from regular casino — cryptographic provably fairness. Any user can mathematically verify result wasn't rigged.
Scheme for Dice:
- Server generates
serverSeed, publishesserverSeedHash = SHA256(serverSeed) - User sets
clientSeed(or random assigned) - Result:
roll = HMAC_SHA256(serverSeed, clientSeed + ":" + nonce) % 10000 - After round server reveals
serverSeed— user verifies hash
import crypto from 'crypto'
function generateRoll(serverSeed: string, clientSeed: string, nonce: number): number {
const hmac = crypto.createHmac('sha256', serverSeed)
hmac.update(`${clientSeed}:${nonce}`)
const hex = hmac.digest('hex')
// Take first 8 hex chars (4 bytes), normalize to 0-9999
let result = parseInt(hex.slice(0, 8), 16)
result = result % 10000
return result
}
// User can verify:
function verify(serverSeed: string, serverSeedHash: string, clientSeed: string, nonce: number, claimedRoll: number): boolean {
const actualHash = crypto.createHash('sha256').update(serverSeed).digest('hex')
if (actualHash !== serverSeedHash) return false
const computedRoll = generateRoll(serverSeed, clientSeed, nonce)
return computedRoll === claimedRoll
}
nonce — increments with each bet, unique per roll. User can change clientSeed anytime — rotates server seed too (server publishes new hash). Standard scheme — used on Stake, BC.Game, Roobet.
Crash: Multiplier Algorithm
Crash multiplier — not truly random each moment. Generated in advance for entire round, players don't know crash point until it happens.
function generateCrashPoint(serverSeed: string, salt: string): number {
const hash = crypto.createHmac('sha256', serverSeed)
.update(salt)
.digest('hex')
// Use first 52 bits of hash
const h = parseInt(hash.slice(0, 13), 16)
// Distribution: P(crash ≥ x) = 1/x (for x >= 1)
// House edge: 1% (every 100th round auto-crashes at 1.00x)
if (h % 100 === 0) return 100 // 1% cases — instant crash
const e = 2 ** 52
return Math.floor((100 * e - h) / (e - h)) / 100
}
Mathematically gives distribution: P(crash ≥ 2x) ≈ 50%, P(crash ≥ 10x) ≈ 10%, etc. With house edge 1%.
WebSocket for Real-time Crash
Crash requires real-time multiplier updates — WebSocket mandatory:
// Server (Node.js + Socket.io)
io.on('connection', (socket) => {
// Client connects, gets current round state
socket.emit('round_state', currentRound)
})
// During round
let multiplier = 1.00
const interval = setInterval(() => {
multiplier *= 1.003 // exponential growth
if (multiplier >= crashPoint) {
clearInterval(interval)
io.emit('crash', { multiplier: crashPoint })
startNewRound()
} else {
io.emit('tick', { multiplier: parseFloat(multiplier.toFixed(2)) })
}
}, 100) // every 100ms
// Frontend (React + socket.io-client)
useEffect(() => {
const socket = io(BACKEND_URL)
socket.on('tick', ({ multiplier }) => {
setMultiplier(multiplier)
})
socket.on('crash', ({ multiplier }) => {
setGameState('crashed')
setCrashPoint(multiplier)
})
socket.on('round_start', (round) => {
setGameState('playing')
setMultiplier(1.00)
})
return () => socket.disconnect()
}, [])
Telegram Mini App Specifics
Initialization and Auth
const tg = window.Telegram.WebApp
tg.ready()
tg.expand()
// Pass initData to backend for verification
const response = await fetch('/api/auth', {
headers: { 'X-Init-Data': tg.initData }
})
TON Payments
For real bets — TON/Jetton transfers via TonConnect. Balances stored off-chain in system, deposit/withdrawal via blockchain:
// Deposit: player sends TON to contract or hot wallet
const depositTx = {
messages: [{
address: DEPOSIT_ADDRESS,
amount: toNano(depositAmount).toString(),
payload: beginCell()
.storeUint(userId, 64) // player ID in payload
.endCell()
.toBoc()
.toString('base64')
}],
validUntil: Math.floor(Date.now() / 1000) + 300
}
await connector.sendTransaction(depositTx)
Backend monitors TON address via tonapi.io or toncenter API, credits balance on confirmed transaction.
Telegram Stars for Micro Bets
For small bets (entertainment mode without real money) — Telegram Stars:
// Via bot API: create invoice for Stars
const invoice = await bot.api.createInvoiceLink(
'Deposit 100 Stars',
'Add 100 Stars to game balance',
JSON.stringify({ userId, amount: 100 }),
'', // provider token (empty for Stars)
'XTR', // Stars currency
[{ label: '100 Stars', amount: 100 }]
)
Stars don't convert directly to TON for game — separate game balance for fun mode.
Leaderboard and Social Mechanics
// Redis Sorted Set for realtime leaderboard
await redis.zadd('leaderboard:crash:daily', {
score: winAmount,
member: `${userId}:${username}`
})
// Top 10
const top10 = await redis.zrange('leaderboard:crash:daily', 0, 9, {
rev: true,
withScores: true
})
Social element of Crash — see other players' bets realtime. Public feed: who bet how much, who cashed out, who burned — amplifies FOMO and retention.
Stack and Timeline
| Component | Technology |
|---|---|
| Frontend | React + Telegram SDK + Chart.js |
| Backend | Node.js + Socket.io + PostgreSQL |
| Cache / realtime | Redis |
| Blockchain | TON + TonConnect |
| Bot | Grammy.js |
Provably fair Dice Mini App with TON balances: 3-4 weeks. Crash + Dice with public leaderboard, real-time bet feed, referral system: 6-8 weeks. Legal (gambling license) depends on jurisdiction — separate question beyond development.







