Розробка Telegram Mini App слотів
Telegram Mini Apps (TMA) — це WebApp всередину мессенджера з аудиторією 900M+ користувачів. TON blockchain нативно інтегрований з Telegram через @wallet бот й TON Connect протокол. Слоти в TMA — один з найпоширеніших GameFi use cases для цієї екосистеми. Розберемо повний стек: від TMA API до смарт-контракту на TON.
Telegram Mini App: технічні основи
TMA — це звичайна веб-програма (React/Vue), що відкривається у вбудованому браузері Telegram. Взаємодія з Telegram через window.Telegram.WebApp об'єкт:
import WebApp from "@twa-dev/sdk";
// Ініціалізація
WebApp.ready(); // повідомляємо Telegram, що додаток завантажено
WebApp.expand(); // розворачиваємо на весь екран
// Дані користувача (без додаткового входу)
const user = WebApp.initDataUnsafe.user;
// { id: 123456789, first_name: "Alex", username: "alex123", ... }
// Верифікація даних на сервері (HMAC-SHA256)
// initData містить підпис від Telegram — ніколи не довіряйте клієнту
const isValid = await verifyTelegramData(WebApp.initData, BOT_TOKEN);
// Haptic feedback
WebApp.HapticFeedback.impactOccurred("medium"); // при виграші
WebApp.HapticFeedback.notificationOccurred("success");
// Main Button (нативна кнопка Telegram)
WebApp.MainButton.setText("КРУТИТИ");
WebApp.MainButton.show();
WebApp.MainButton.onClick(() => spinReels());
Верифікація initData на бекенді
import crypto from "crypto";
function verifyTelegramWebAppData(initData: string, botToken: string): boolean {
const urlParams = new URLSearchParams(initData);
const hash = urlParams.get("hash");
urlParams.delete("hash");
// Сортування параметрів по абетці
const dataCheckString = Array.from(urlParams.entries())
.sort(([a], [b]) => a.localeCompare(b))
.map(([key, value]) => `${key}=${value}`)
.join("\n");
const secretKey = crypto
.createHmac("sha256", "WebAppData")
.update(botToken)
.digest();
const computedHash = crypto
.createHmac("sha256", secretKey)
.update(dataCheckString)
.digest("hex");
return computedHash === hash;
}
TON смарт-контракт для слотів
TON використовує FunC або Tact (більш модерний, TypeScript-подібний синтаксис) для смарт-контрактів. Слоти на TON — це контракт, що приймає TON монети й виплачує призи.
// Tact синтаксис
import "@stdlib/deploy";
struct SpinResult {
reel1: Int as uint8;
reel2: Int as uint8;
reel3: Int as uint8;
payout: Int as coins;
}
contract SlotMachine with Deployable {
owner: Address;
houseBalance: Int as coins = 0;
// Мінімальна й максимальна ставка
const MIN_BET: Int = ton("0.1");
const MAX_BET: Int = ton("10");
// Публічний ключ сервера для верифікації рандома
serverPublicKey: Int as uint256;
init(owner: Address, serverPublicKey: Int) {
self.owner = owner;
self.serverPublicKey = serverPublicKey;
}
// Приймаємо ставку
receive("spin") {
let ctx: Context = context();
require(ctx.value >= self.MIN_BET, "Bet too small");
require(ctx.value <= self.MAX_BET, "Bet too large");
// Emit подія — сервер підхопить й повернеть підписаний результат
emit(SpinRequested{
player: ctx.sender,
betAmount: ctx.value,
nonce: now()
}.toCell());
self.houseBalance += ctx.value;
}
// Сервер надсилає підписаний результат
receive(msg: ClaimWin) {
// Верифікуємо підпис сервера
let hash: Int = beginCell()
.storeAddress(msg.player)
.storeCoins(msg.betAmount)
.storeUint(msg.nonce, 64)
.storeUint(msg.reel1, 8)
.storeUint(msg.reel2, 8)
.storeUint(msg.reel3, 8)
.endCell()
.hash();
require(checkSignature(hash, msg.signature, self.serverPublicKey), "Invalid signature");
let payout: Int = self.calculatePayout(msg.reel1, msg.reel2, msg.reel3, msg.betAmount);
if (payout > 0) {
require(self.houseBalance >= payout, "Insufficient house balance");
self.houseBalance -= payout;
send(SendParameters{
to: msg.player,
value: payout,
mode: SendIgnoreErrors
});
}
}
fun calculatePayout(r1: Int, r2: Int, r3: Int, bet: Int): Int {
// Три сімки — джекпот
if (r1 == 7 && r2 == 7 && r3 == 7) { return bet * 100; }
// Три однакові
if (r1 == r2 && r2 == r3) { return bet * 10; }
// Два однакові
if (r1 == r2 || r2 == r3 || r1 == r3) { return bet * 2; }
// BAR комбінація
if (r1 == 6 && r2 == 6) { return bet * 3; }
return 0;
}
// Поповнення premio пулу
receive("deposit") {
self.houseBalance += context().value;
}
get fun balance(): Int { return self.houseBalance; }
}
TON Connect: підключення гаманця
import TonConnect from "@tonconnect/sdk";
const connector = new TonConnect({
manifestUrl: "https://yourapp.com/tonconnect-manifest.json",
});
// tonconnect-manifest.json:
// { "url": "https://yourapp.com", "name": "Slots Game",
// "iconUrl": "https://yourapp.com/icon.png" }
// Підключити гаманець (відкриває @wallet бот або зовнішній гаманець)
const walletsList = await connector.getWallets();
connector.connect({ universalLink: walletsList[0].universalLink, bridgeUrl: ... });
connector.onStatusChange((wallet) => {
if (wallet) {
console.log("Connected:", wallet.account.address);
initGame(wallet.account.address);
}
});
// Надіслати транзакцію (ставка в TON)
async function placeBet(amount: number) {
await connector.sendTransaction({
validUntil: Math.floor(Date.now() / 1000) + 300,
messages: [{
address: SLOT_CONTRACT_ADDRESS,
amount: String(amount * 1e9), // в nanoTON
payload: "spin", // текстовий коментар
}],
});
}
Ігровий процес й анімація
Слоти — анімація барабанів. Використовуємо CSS/Canvas анімацію або Pixi.js:
- Гравець натискає "Крутити" → надсилається TON транзакція
- Поки йде транзакція → барабаны крутяться в "очікуваній" анімації
- Game server замічає транзакцію → генерує випадковий результат → підписує → викликає
ClaimWinна контракті - Фронтенд отримує подію через TON Center API або tonSDK → барабани "гальмують" на фінальних символах
- Якщо виграш — haptic feedback + ефект монет
// Моніторинг подій контракту
async function watchSlotEvents(contractAddress: string) {
const client = new TonClient({ endpoint: "https://toncenter.com/api/v2/jsonRPC" });
setInterval(async () => {
const transactions = await client.getTransactions(Address.parse(contractAddress), {
limit: 10,
});
for (const tx of transactions) {
if (tx.inMessage?.body) {
// Парсимо результат барабанів з body транзакції
const result = parseSpinResult(tx.inMessage.body);
if (result && result.player === currentPlayerAddress) {
animateReels(result.reel1, result.reel2, result.reel3);
}
}
}
}, 2000);
}
Монетизація й TON екосистема
TON забезпечує кілька переваг для TMA слотів:
- @wallet інтеграція — користувачи Telegram часто мають TON гаманець через вбудований @wallet
- Комісії — транзакції в TON коштують ~$0.003–0.01, що прийнятно для ігрових мікротранзакцій
- TON Stars — внутрішня валюта Telegram (для non-crypto користувачів)
-
Viral механіки — нативний шаринг результатів у чати через
WebApp.openTelegramLink
RTP (Return to Player) для чесних слотів: 95–97%. House edge 3–5% забезпечує стійку економіку при достатньому обсязі ігор.







