Skip to content

zynorr/tipswap

Repository files navigation

TipSwap

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.


Architecture

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
Loading

How a swap works:

  1. A user sends /swap 0.1 TON USDT to the Telegram bot
  2. The bot looks up the user's managed wallet in Supabase and decrypts the mnemonic
  3. It checks the TON balance to ensure there's enough for gas (0.2–0.35 TON depending on the route)
  4. It builds the swap transaction via STON.fi's CPIRouterV2_2 contract
  5. It requests a live STON.fi simulation for routing, gas, and expected output
  6. It signs and broadcasts the transaction, then polls for confirmation (up to 60s)
  7. 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.


Stack

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)

Bot commands

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


Project layout

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

Getting started

Requirements: Node.js 20+, pnpm

pnpm install
pnpm dev

Open http://localhost:3000.

To test the bot locally, you need a public HTTPS URL:

cloudflared tunnel --url http://localhost:3000

Then go to /admin/setup, paste your ADMIN_SETUP_TOKEN, and click Set webhook.


Environment variables

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_URLNEXT_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.


Deployment

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.


Operational notes

  • 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.

Scripts

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)

License

MIT

About

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors