Tap-to-Earn Telegram Mini App Development
Notcoin in 2024 proved Telegram Mini App can gather tens of millions users through simplest mechanic: click button, earn coins. After it—wave: Hamster Kombat, Blum, Major—each with variations. Tap-to-earn isn't about deep gameplay, it's about virality, retention, efficient TON ecosystem onboarding.
Technically: React app inside Telegram WebApp, Node.js/Go backend, TON Connect for wallet, off-chain coin storage with on-chain conversion at listing.
Telegram Mini App: Technical Foundation
Telegram provides window.Telegram.WebApp—object with user data and interface methods. Authentication via initData—string Telegram passes on app opening.
const tg = window.Telegram.WebApp
tg.ready() // notify Telegram app is ready
tg.expand() // fullscreen
const user = tg.initDataUnsafe.user
// { id: 123456789, first_name: "Ivan", username: "ivan_user", ... }
// IMPORTANT: initDataUnsafe—unverified client-side
// For backend requests—pass tg.initData (string with signature)
Backend verification:
import { createHmac } from 'crypto'
function verifyTelegramAuth(initData: string, botToken: string): boolean {
const params = new URLSearchParams(initData)
const hash = params.get('hash')
params.delete('hash')
// Sort and concatenate
const dataCheckString = Array.from(params.entries())
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}=${v}`)
.join('\n')
const secretKey = createHmac('sha256', 'WebAppData').update(botToken).digest()
const expectedHash = createHmac('sha256', secretKey)
.update(dataCheckString).digest('hex')
return hash === expectedHash
}
This verification is mandatory on every backend request. Without it—any user can fake user.id and steal another's coins.
Tap Mechanic and Performance Optimization
Tap = frontend event → send coins to backend. Naive: each tap = HTTP request. At 10 tap/sec × 100k concurrent = 1M RPS. Unrealistic.
Batching taps. Client accumulates taps locally, sends batch every 2-5 seconds:
class TapBatcher {
private pendingTaps = 0
private flushInterval: ReturnType<typeof setInterval>
constructor(private userId: number, private flushEveryMs = 3000) {
this.flushInterval = setInterval(() => this.flush(), flushEveryMs)
}
tap(count: number = 1) {
this.pendingTaps += count
// Immediate optimistic UI update
}
async flush() {
if (this.pendingTaps === 0) return
const toSend = this.pendingTaps
this.pendingTaps = 0
try {
await api.post('/taps', { userId: this.userId, count: toSend })
} catch {
this.pendingTaps += toSend
}
}
}
Optimistic UI. Coins update instantly, no backend wait. Feel is critical.
Energy system. Limit taps via energy pool: 1 energy per tap, N energy regen/hour. Both game mechanic and rate limiter.
Backend Architecture for High Load
Thousands concurrent users at launch.
Redis for real-time state. All game data in memory: balance:{userId}, energy:{userId}, lastTap:{userId}. Lua script for atomic update:
local energy = tonumber(redis.call('GET', KEYS[1])) or 1000
local taps = tonumber(ARGV[1])
local energyCost = taps
if energy < energyCost then
return {0, energy}
end
redis.call('DECRBY', KEYS[1], energyCost)
redis.call('INCRBY', KEYS[2], taps * tonumber(ARGV[2]))
return {1, energy - energyCost}
Lua executes atomically in Redis—no race condition.
PostgreSQL for persistence. Periodically (every N minutes or on event) write Redis data to PostgreSQL. On restart—recover from DB to Redis.
TON Connect and Coin Conversion
Off-chain click coins. Conversion to real TON tokens—at TGE or scheduled.
import { TonConnectUI } from '@tonconnect/ui-react'
const tonConnect = new TonConnectUI({
manifestUrl: 'https://your-app.com/tonconnect-manifest.json'
})
const wallet = await tonConnect.connectWallet()
async function claimTokens(userId: number) {
const claimData = await api.post('/prepare-claim', { userId })
await tonConnect.sendTransaction({
validUntil: Math.floor(Date.now() / 1000) + 300,
messages: [{
address: CLAIM_CONTRACT_ADDRESS,
amount: '50000000',
payload: claimData.payload,
}]
})
}
TON Jetton (ERC-20 analogue)—standard for fungible tokens. Claim contract verifies backend signature, mints Jettons.
Viral Mechanics and Referral
Tap-to-earn without virality dies fast. Referral is mandatory:
Referral code: deep link https://t.me/YourBot?startapp=ref_userId. On first launch with param: user linked to referrer, both get bonus.
Multi-level referrals. Hamster Kombat: 25k coins per friend, 10% per tap. Exponential growth but exponential inflation.
Daily tasks + combo. Daily missions (subscribe channel, mention, invite X friends) give bonus coins. Combo—guess card combination for bonus.
Security and Anti-cheat
Rate limiting per userId. Max N batches/sec. Even with batching, automation can send too fast.
Energy ceiling. Server stores last energy update timestamp. Max accumulated energy limited physically. Client can't claim more than time allows.
Suspicious patterns. Tap rate > physically possible (>15 tap/sec stable) → flag. Bots detected and banned.
Bot detection via Telegram data. Accounts without username, without photo, recently created—weighted lower.
Stack
| Component | Technologies |
|---|---|
| Frontend | React + TypeScript + Vite |
| UI | Tailwind + Framer Motion |
| Backend | Node.js (Fastify) or Go |
| Real-time state | Redis + Lua scripts |
| Database | PostgreSQL |
| TON integration | TON Connect UI + @ton/core |
| Smart contracts | Tact (FunC wrapper) |
| Deploy | Docker + Nginx / Coolify |
Development Process
Game design + tokenomics (1 week). Energy system, reward per tap, referral structure, viral mechanics.
Backend + Telegram auth (1-2 weeks). initData verification, Redis tap pipeline, energy, referral.
Frontend Mini App (1-2 weeks). Tap interface with animations, energy indicator, leaderboard, referral onboarding.
TON integration (1 week). TonConnect connection, Jetton contract, claim.
Testing and launch (1 week). Load testing Redis pipeline, anti-cheat test, soft launch.
Minimal tap-to-earn without TON—3-4 weeks. With TON token, referral, full anti-cheat—6-8 weeks.







