Telegram-native token tipping on TON, powered by STON.fi.
TipSwap is a Telegram bot that lets anyone tip TON, USDT, or STON tokens to any Telegram user — even when the sender and recipient hold different tokens. The bot swaps across tokens on STON.fi behind the scenes, so the recipient gets what they want without ever opening a DEX.
flowchart TB
subgraph Users["👤 Users"]
TG["Telegram App<br/>/start /wallet /swap /tip /balance /help"]
WB["Web Browser<br/>Landing page · Admin dashboard · Mini App"]
end
subgraph App["⚡ Application — Next.js / Vercel"]
API["API Routes<br/>/api/bot · /api/miniapp/* · /api/tonpay/webhook"]
BOT["grammY Bot"]
USERS["User & wallet queries"]
TON_WALLET["TON client<br/>Balance · Broadcast"]
SWAP["Swap engine<br/>STON.fi SDK · Quotes · Gas"]
TONPAY["TON Pay<br/>External wallet direct sends"]
CRYPTO["Mnemonic encryption<br/>AES-256-GCM"]
SUPALIB["Supabase clients<br/>admin · client · server"]
end
subgraph Infra["☁️ Infrastructure"]
SUPABASE[("Supabase<br/>PostgreSQL")]
TON[("TON Blockchain<br/>TONCenter RPC")]
STON[("STON.fi Contracts<br/>CPIRouterV2_2")]
end
TG -- "HTTPS webhook" --> API
WB -- "HTTP" --> API
API --> BOT
BOT --> USERS
BOT --> TON_WALLET
BOT --> SWAP
API --> TONPAY
USERS --> SUPALIB
TON_WALLET --> SUPALIB
SWAP --> SUPALIB
TONPAY --> SUPALIB
SUPALIB --> SUPABASE
TON_WALLET --> TON
SWAP --> TON
SWAP --> STON
How a swap works:
- A user sends
/swap 0.1 TON USDTto the Telegram bot - The bot looks up the user's managed wallet in Supabase and decrypts the mnemonic
- It checks the TON balance to ensure there's enough for gas (0.2–0.35 TON depending on the route)
- It builds the swap transaction via STON.fi's CPIRouterV2_2 contract
- It requests a live STON.fi simulation for routing, gas, and expected output
- It signs and broadcasts the transaction, then polls for confirmation (up to 60s)
- It logs the result in Supabase and sends the user a confirmation with a tonviewer link
Key design decisions:
- Server-only bot code — the grammY handlers use a
"server-only"import and are never exposed to the browser - Three Supabase clients — service role for bot/API routes, anonymous for the landing page, authenticated for Next.js server components
- Stateless handlers — every request fetches fresh state from Supabase and TON RPC; no in-memory session
- Live STON.fi simulation — swaps and token tips require a fresh route simulation before funds move
- Exponential backoff — TONCenter 429 responses trigger up to 5 retries with 2× delay starting at 600ms
Database: Bot data lives in tg_users, tg_wallets, tg_swaps, tg_tips, tg_external_tip_payments, tg_tip_batches, tg_tip_claims, and tg_group_messages; public waitlist signups live in waitlist. RLS is enabled. Only waitlist allows anonymous inserts — all bot data is service-role only. Wallet mnemonics are encrypted at rest with AES-256-GCM.
| Layer | Technology |
|---|---|
| App framework | Next.js 16 (App Router) |
| Runtime | Node.js |
| UI | React 19, Tailwind CSS v4, framer-motion |
| Telegram bot | grammY |
| TON blockchain | @ton/ton, @ton/core |
| DEX integration | @ston-fi/sdk |
| Database | Supabase (PostgreSQL) |
| Command | Description |
|---|---|
/start |
Register or restore your managed wallet |
/wallet |
Show wallet address and TON balance |
/balance |
Show TON, USDT, and STON balances |
/connect <address> |
Track an external TON wallet and receive tips there |
/managed |
Switch back to the system-generated managed wallet |
/swap <amount> <from> <to> |
Execute a cross-token swap |
/tip <amount> <receive-token> @user |
Tip a Telegram user from TON by default, or create a claim link if they have not started the bot |
/tip <amount> <receive-token> from <pay-token> @user |
Tip with an explicit pay token |
/receive <token> |
Set your default receive token |
/settip <amount> <receive-token> from <pay-token> |
Set reaction tip defaults |
/settings |
Show wallet and tip preferences |
/history |
Show recent swap/tip activity |
/help |
Full command reference |
Supported tokens: TON, USDT, STON
app/
page.tsx Landing page
admin/setup/ Webhook management UI
miniapp/ Telegram Mini App wallet, send, claim, history UI
api/bot/route.ts Telegram webhook receiver
api/bot/setup/route.ts Webhook setup/inspection API
api/miniapp/ Authenticated Mini App APIs
api/tonpay/webhook/ TON Pay webhook receiver
api/waitlist/route.ts Public waitlist signup
tonconnect-manifest.json/ TON Connect manifest route
lib/
bot/
index.ts grammY command handlers
tips.ts Shared tip, claim, and Mini App tip helpers
users.ts Supabase helpers for users, wallets, swaps, tips
miniapp/
auth.ts Telegram Mini App initData validation
external-tips.ts TON Pay and STON.fi wallet-signed tip flow
ston/swap.ts STON.fi swap construction, quotes, gas estimation
supabase/ Three clients + generated types
wallet/
ton.ts TON client, wallet gen, balance, broadcast
crypto.ts AES-256-GCM mnemonic encryption
components/site/ Landing page sections
components/ui/ Shared Radix UI primitives
docs/ Production and Telegram testing guides
scripts/ SQL setup and wallet recovery helpers
Requirements: Node.js 20+, pnpm
pnpm install
pnpm devOpen http://localhost:3000.
To test the bot locally, you need a public HTTPS URL:
cloudflared tunnel --url http://localhost:3000Then go to /admin/setup, paste your ADMIN_SETUP_TOKEN, and click Set webhook.
| Variable | Purpose |
|---|---|
TELEGRAM_BOT_TOKEN |
Bot token from @BotFather |
TELEGRAM_BOT_USERNAME |
Bot username without @, used in claim links |
NEXT_PUBLIC_TELEGRAM_BOT_USERNAME |
Public bot username for Mini App fallback links |
TELEGRAM_WEBHOOK_SECRET |
Secret token validated on webhook requests |
ADMIN_SETUP_TOKEN |
Bearer token for webhook setup API (production) |
WALLET_ENCRYPTION_KEY |
Symmetric key for mnemonic encryption (min 32 chars) |
NEXT_PUBLIC_SUPABASE_URL |
Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase anon key |
NEXT_PUBLIC_APP_URL |
Public HTTPS app URL for Mini App and TON Connect links |
NEXT_ALLOWED_DEV_ORIGINS |
Optional comma-separated local tunnel origins for Next.js dev |
SUPABASE_SERVICE_ROLE_KEY |
Supabase service role key for bot/admin API routes |
TON_API_KEY |
TONCenter API key for higher rate limits |
TONPAY_CHAIN |
mainnet for production, testnet only for testnet wallets |
TONPAY_API_KEY |
Optional TON Pay Merchant API key for dashboard tracking |
TONPAY_WEBHOOK_SECRET |
Optional TON Pay webhook secret for /api/tonpay/webhook |
The admin Supabase client also accepts SUPABASE_SERVICE_ROLE_KEY or SUPABASE_SECRET_KEY, and falls back SUPABASE_URL → NEXT_PUBLIC_SUPABASE_URL.
Copy .env.example for a safe template. Use the Supabase project API URL for NEXT_PUBLIC_SUPABASE_URL, not the Postgres connection string.
Existing Supabase projects should run scripts/002_external_tip_payments.sql once to add Mini App external-wallet payment tracking. New projects can run scripts/001_init_schema.sql.
Deploy to Vercel with all environment variables configured. The bot runs on TON mainnet.
After deployment, set the webhook from /admin/setup, or with:
curl -X POST https://<your-vercel-domain>/api/bot/setup \
-H "authorization: Bearer $ADMIN_SETUP_TOKEN" \
-H "content-type: application/json" \
--data '{"action":"set","url":"https://<your-vercel-domain>"}'The setup/inspection API (GET|POST /api/bot/setup) requires Authorization: Bearer <ADMIN_SETUP_TOKEN> in production. Every webhook request validates X-Telegram-Bot-Api-Secret-Token — requests without it get a 403.
For the full Vercel Hobby checklist, see docs/production-vercel.md.
For a non-technical Telegram testing walkthrough, see docs/telegram-user-test-guide.md.
- Error handling: Balance preflight prevents gas-out failures. TONCenter 429s trigger exponential backoff. RPC errors produce user-friendly messages.
- Hot wallet model: Per-user managed wallets are convenient but carry risk. Production deployments should add per-user limits, wallet graduation, command rate limiting, and monitoring.
- Vercel Hobby: Keep Fluid Compute enabled. The webhook function is configured for a longer Hobby-safe execution window, and batch tips are limited to three recipients because each recipient gets a separate transaction.
| Command | Description |
|---|---|
pnpm dev |
Start the dev server |
pnpm build |
Production build |
pnpm start |
Start the production server |
pnpm lint |
Run ESLint |
pnpm typecheck |
TypeScript type checking |
pnpm verify |
Typecheck + lint + build (CI) |
MIT