Разработка Telegram Mini App для криптокошелька
Telegram Mini App (TMA) — веб-приложение, встроенное в Telegram через WebView. Доступно 900+ миллионам пользователей без установки отдельного приложения. Для криптокошелька это радикально меняет onboarding: вместо «скачай приложение → создай seed phrase → запиши 12 слов → не потеряй» — просто открыть бота в Telegram.
Но TMA-кошелёк накладывает жёсткие ограничения: нет доступа к файловой системе, localStorage ненадёжен (Telegram может очистить в любой момент), WebView в iOS ограничен в crypto API, и главное — хранить seed phrase в TMA небезопасно. Это определяет архитектурный выбор: либо custodial, либо MPC, либо embedded wallet через Account Abstraction.
Техническая платформа
TON vs EVM
Два основных варианта для TMA-кошелька:
TON (The Open Network) — нативная интеграция с Telegram. TON Space (встроенный кошелёк Telegram) уже использует TON. TonConnect протокол — стандарт для dApp подключений в TON экосистеме. SDK: @tonconnect/sdk, @ton/core. Аудитория TON уже в Telegram, экосистема DeFi растёт быстро.
EVM через Abstract Wallet. Кошелёк для Ethereum/BSC/Polygon в TMA — более широкая DeFi экосистема, но нет нативной интеграции. Используется Web3Auth, Privy, или собственный MPC backend. Подходит если ваша аудитория ориентирована на существующий EVM DeFi.
Комбинированный подход: TON для нативных TG активностей + EVM для DeFi. Технически сложнее, но даёт максимальное покрытие.
Telegram WebApp API
// Инициализация TMA
import WebApp from '@twa-dev/sdk';
WebApp.ready(); // сигнализируем Telegram, что приложение готово
WebApp.expand(); // разворачиваем на весь экран
// Данные пользователя (ВАЖНО: верифицировать на сервере)
const user = WebApp.initDataUnsafe.user;
// initData — строка для верификации подписи Telegram
// Кнопки
WebApp.MainButton.setText('Подтвердить транзакцию');
WebApp.MainButton.onClick(() => handleTransaction());
WebApp.MainButton.show();
// Haptic feedback для транзакций
WebApp.HapticFeedback.notificationOccurred('success');
// Цветовая схема Telegram
const isDark = WebApp.colorScheme === 'dark';
Верификация initData на сервере — обязательна для любой wallet операции:
// Backend: Node.js
import crypto from 'crypto';
function verifyTelegramData(initData: string, botToken: string): boolean {
const params = new URLSearchParams(initData);
const hash = params.get('hash');
params.delete('hash');
const dataCheckString = Array.from(params.entries())
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}=${v}`)
.join('\n');
const secretKey = crypto
.createHmac('sha256', 'WebAppData')
.update(botToken)
.digest();
const expectedHash = crypto
.createHmac('sha256', secretKey)
.update(dataCheckString)
.digest('hex');
return hash === expectedHash;
}
Telegram подписывает initData ботовым токеном. Подделать без знания токена невозможно. Каждый запрос к wallet API должен включать initData и проходить эту проверку.
Архитектура кошелька: три подхода
1. Custodial с MPC backend
Пользователь не управляет ключами напрямую. Платформа хранит ключи в MPC (несколько серверных шардов). Идентификация: telegram_user_id.
User (TMA) → Backend API (initData verification) → MPC Signing Service → Blockchain
Плюсы: нет проблемы хранения ключей на клиенте, простой recovery, быстрый UX. Минусы: custodial риск, нужна лицензия (зависит от юрисдикции), пользователь доверяет платформе.
Реализация: Web3Auth MPC Core Kit на сервере, или Fireblocks API. Telegram user ID → deterministic wallet address через сервис. Поддержка EVM + TON в одной системе.
2. Embedded wallet с AA (Account Abstraction)
Используем ERC-4337 Smart Account. Ключ генерируется на устройстве (в памяти TMA), но контракт-кошелёк может иметь несколько владельцев и recovery механизм.
// Создание AA wallet через ZeroDev SDK
import { createKernelAccount, createKernelAccountClient } from '@zerodev/sdk';
import { signerToEcdsaValidator } from '@zerodev/ecdsa-validator';
// Signer из Telegram initData (детерминированный из user_id + секрет)
const signer = privateKeyToAccount(deriveKeyFromTelegram(initData));
const ecdsaValidator = await signerToEcdsaValidator(publicClient, {
signer,
kernelVersion: KERNEL_V3_1,
});
const account = await createKernelAccount(publicClient, {
plugins: { sudo: ecdsaValidator },
kernelVersion: KERNEL_V3_1,
});
// Gasless транзакции через Paymaster
const kernelClient = createKernelAccountClient({
account,
paymaster: { getPaymasterData: async (userOp) => ... },
});
Преимущество AA: gasless транзакции (paymaster платит gas), batched operations, social recovery через смарт-контракт. Недостаток: ключ в памяти TMA — при закрытии приложения нужно либо хранить на сервере (custodial элемент), либо требовать повторной деривации.
3. TonConnect для TON
import TonConnect from '@tonconnect/sdk';
const connector = new TonConnect({
manifestUrl: 'https://yourapp.com/tonconnect-manifest.json',
});
// Открываем список кошельков (TON Space, Tonkeeper и др.)
const walletsList = await connector.getWallets();
// Подключение конкретного кошелька
await connector.connect({
universalLink: wallet.universalLink,
bridgeUrl: wallet.bridgeUrl,
});
// Отправка транзакции
await connector.sendTransaction({
validUntil: Math.floor(Date.now() / 1000) + 300,
messages: [{
address: recipientAddress,
amount: toNano('0.5').toString(),
payload: cell.toBoc().toString('base64'),
}],
});
TonConnect открывает TON Space или Tonkeeper через deep link. Для кошелька самого приложения — используется TON SDK с ключами, хранящимися через ваш backend.
Безопасное хранение ключей в TMA
Главная проблема TMA: нет безопасного хранилища. Что НЕ подходит:
-
localStorage— очищается Telegram, читается JavaScript -
sessionStorage— живёт только сессию - IndexedDB — аналогично localStorage по безопасности
Решение: серверное хранилище с шифрованием. Ключ (или MPC share) хранится на сервере, зашифрованный ключом, известным только пользователю. Пользователь вводит PIN → PIN преобразуется в encryption key через PBKDF2/Argon2 → расшифровывает share.
// Client-side: шифрование share перед отправкой на сервер
async function encryptShare(share: Uint8Array, pin: string): Promise<string> {
const pinKey = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(pin),
'PBKDF2',
false,
['deriveKey'],
);
const salt = crypto.getRandomValues(new Uint8Array(16));
const aesKey = await crypto.subtle.deriveKey(
{ name: 'PBKDF2', salt, iterations: 600_000, hash: 'SHA-256' },
pinKey,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt'],
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
aesKey,
share,
);
return JSON.stringify({
salt: btoa(String.fromCharCode(...salt)),
iv: btoa(String.fromCharCode(...iv)),
data: btoa(String.fromCharCode(...new Uint8Array(encrypted))),
});
}
Сервер хранит зашифрованный blob, расшифровать без PIN невозможно. PIN не передаётся на сервер.
UI/UX паттерны для TMA-кошелька
Адаптация под Telegram UI
TMA должен выглядеть нативно в Telegram. Используем CSS переменные Telegram:
:root {
--tg-theme-bg-color: var(--tg-color-scheme-bg);
--tg-theme-text-color: var(--tg-color-scheme-text);
--tg-theme-button-color: var(--tg-color-scheme-button);
--tg-theme-button-text-color: var(--tg-color-scheme-button-text);
}
Библиотека компонентов: @telegram-apps/telegram-ui — готовые компоненты в стиле Telegram (Cell, List, Section, Modal). Используем вместо кастомного дизайна — пользователи сразу чувствуют знакомый интерфейс.
Транзакционный UX
Подтверждение транзакции в TMA — критический момент. Пользователь должен чётко видеть: что подписывает, сколько стоит, gas fee (или "бесплатно" если gasless). Используем WebApp.showConfirm() для простых операций или кастомный modal для сложных.
// Показываем нативный confirm Telegram для критичных операций
const confirmed = await new Promise<boolean>((resolve) => {
WebApp.showConfirm(
`Отправить ${amount} USDT на ${shortAddress(recipient)}?`,
resolve,
);
});
Инфраструктура и стек
Frontend: React 18 + TypeScript + Vite. @twa-dev/sdk для Telegram API. wagmi + viem для EVM. @ton/ton для TON. Tailwind CSS с Telegram CSS переменными.
Backend: Node.js + Fastify. Telegram Bot API для уведомлений. PostgreSQL для user data + wallet metadata. Redis для сессий и rate limiting.
Web3 инфраструктура: Alchemy / Infura для EVM RPC. TON Center или orbs.com для TON. Bundler + Paymaster (Pimlico, ZeroDev) для AA.
| Компонент | Технология |
|---|---|
| TMA Framework | @twa-dev/sdk + React |
| EVM Wallet | Web3Auth MPC / ZeroDev AA |
| TON Wallet | TonConnect + @ton/core |
| Auth | Telegram initData verification |
| Key storage | Server-side encrypted shares |
| Notifications | Telegram Bot API |
Ориентиры по срокам
MVP (custodial, один chain, базовый send/receive): 4–6 недель. Полноценный кошелёк с MPC, AA gasless, multi-chain, TON + EVM: 3–5 месяцев.
Отдельно: любой кошелёк, работающий с реальными деньгами пользователей, требует security audit до публичного релиза.







