If you're building a crypto swap flow inside Telegram, you have two interfaces to choose from: a bot (chat commands and inline buttons) or a Mini App (a web app rendered inside Telegram). Most production exchangers end up shipping both. Here's how they split the work, and the pitfalls that actually break swaps.
When to use which
| Bot | Mini App | |
|---|---|---|
| Best for | Status updates, quick repeat swaps, notifications | First-time swaps, pair search, address input |
| UI | Messages + inline keyboards | Full HTML/JS UI |
| Searching 1,000s of assets | Painful | Natural (search + filter) |
| Push notifications | Native | Needs the bot anyway |
A practical split: the Mini App handles the swap creation, and the bot handles everything after: deposit confirmation, status changes, completion.
1. Never trust initData on the client
The Mini App receives window.Telegram.WebApp.initData, a query string with user info and a hash. Your backend must validate it before creating any swap tied to a user.
The algorithm (from Telegram's docs):
- Remove
hashfrom the params - Sort the remaining
key=valuepairs alphabetically and join them with\n secret_key = HMAC_SHA256(key="WebAppData", msg=bot_token)- Compare
hex(HMAC_SHA256(key=secret_key, msg=data_check_string))withhash
import crypto from "node:crypto";
export function validateInitData(initData, botToken, maxAgeSec = 3600) {
const params = new URLSearchParams(initData);
const hash = params.get("hash");
params.delete("hash");
const dataCheckString = [...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 computed = crypto
.createHmac("sha256", secretKey)
.update(dataCheckString)
.digest("hex");
if (computed.length !== hash?.length ||
!crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(hash))) {
return null;
}
const authDate = Number(params.get("auth_date"));
if (Date.now() / 1000 - authDate > maxAgeSec) return null;
return JSON.parse(params.get("user") ?? "null");
}
Also check auth_date. A valid-but-old initData string is still replayable.
2. Quotes expire, so design for it
A swap quote is only valid for a short window. If a user opens the Mini App, gets distracted, and comes back ten minutes later, the rate on screen is stale.
- Store
quote_expires_atserver-side, and don't trust the client's timer - Re-quote on "Confirm" if expired, and show the diff
- Decide upfront how you handle a deposit that arrives after expiry (re-quote, refund, or floating rate)
3. Memo/tag networks cause most "missing deposit" tickets
Assets on networks like XRP, XLM, EOS, and TON (for exchange deposit addresses) often require a memo/destination tag in addition to the address. If the user omits it, funds land in the shared deposit address with no way to match them automatically.
- Make the memo field required in the UI for those networks, not optional
- Show it with the same visual weight as the address, plus a copy button
- In the bot, send address and memo as separate messages so each can be copied on mobile
4. Network ambiguity is the second biggest issue
"USDT" isn't one asset. It's USDT-TRC20, USDT-ERC20, USDT-BEP20, and so on. EVM chains share the same 0x address format, so an address check alone won't catch a wrong-network send.
- Make network selection explicit, never implicit
- Display the network name next to every address you show
5. Deep links connect the two interfaces
Use startapp parameters to hand off from bot to Mini App with context:
https://t.me/YourBot/app?startapp=swap_BTC_USDTTRC20
The parameter shows up as start_param in initData, so a bot message like "Swap again?" can open the Mini App pre-filled.
6. Push status through the bot, don't poll in the UI
Mini Apps close. Users leave. Swap status (awaiting deposit β confirming β exchanging β sent) should be pushed as bot messages, triggered by your backend's state machine and not by a polling loop in a web view nobody is looking at.
For a live example of this split, NefiSwap runs swap creation in a Telegram Mini App and uses a bot for quick swaps and status updates across 8,000+ assets: nefiswap.com. Disclosure: written by the NefiSwap team.
Top comments (0)