$LEMON24H n/a
Menu
Switch network
Release 001 / Telegram trading bot

The bot is open source. The fees are yours.

Every line of the Telegram trading bot that runs lemon.fun, published under MIT. Launch or connect a coin and your own branded bot is live in seconds, paying 0.30% of every trade straight to your wallet. We take zero.

MIT/v1.0.0/18 modules/6,895 lines/245 KB/Robinhood Chain 4663

Why it is public

Innovation moves faster when one builder's tools become the next builder's starting point. So the code is open. Every module, the same bytes production runs, no trimmed down teaching copy.

But open source means nothing if creators still beg a platform for scraps. So we went further: the hosted version pays the creator, not us. Launch a coin or paste any Robinhood Chain contract on lemon.fun/launch and the bot is yours. Your name, your welcome line, your community, and 0.30% of every routed trade, on chain, forever. No monthly fee, no lemon.fun cut.

The release

Every path links to the raw file. Together they are a complete bot: webhook routers, the command surface, the execution engine, hosted wallets, the on chain data layer, and the white label layer that lets one deployment serve many independent bots.

Read it over HTTP

One endpoint, three shapes. No key, CORS open, so a browser, a script or an agent can all pull it.

GET /api/public/open-source/telegram-bot
    manifest: paths, line counts, byte sizes, env vars, tables

GET /api/public/open-source/telegram-bot?file=src/lib/lemon/traderTrade.server.ts
    one module, text/plain

GET /api/public/open-source/telegram-bot?format=bundle
    manifest plus every file body, keyed by path
# write the whole release to disk, keeping the original paths
curl -s https://lemon.fun/api/public/open-source/telegram-bot?format=bundle \
  | node -e '
    const fs = require("fs"), path = require("path");
    let raw = ""; process.stdin.on("data", c => raw += c).on("end", () => {
      const { contents } = JSON.parse(raw);
      for (const [file, body] of Object.entries(contents)) {
        fs.mkdirSync(path.dirname(file), { recursive: true });
        fs.writeFileSync(file, body);
      }
      console.log(Object.keys(contents).length + " files written");
    });
  '

What it needs to run

Plain TypeScript on a worker runtime, viem for chain reads and writes, Postgres for state. No queue, no custom node, no cron dependency in the trading path. A BotFather token and these variables are enough.

TELEGRAM_BOT_TOKENrequired
BotFather token for the first party bot. Tenant bots carry their own encrypted token in the database.
WALLET_MASTER_KEYrequired
32 byte hex key. Encrypts hosted wallet keys and tenant bot tokens.
SUPABASE_URLrequired
Postgres project the bot stores users, wallets and trades in.
SUPABASE_SERVICE_ROLE_KEYrequired
Server side writes. Never shipped to a browser.
PUBLIC_BASE_URLrequired
Canonical https origin. Used to register webhooks with Telegram.
LOVABLE_API_KEYoptional
Only needed for the model backed analysis screen.

State it expects

Five tables. Everything a screen shows is derived from them plus a live chain read, so an empty cache never invents a number.

telegram_users
One row per chat, scoped by bot_id. Null bot_id is the first party bot.
trader_wallets
Hosted wallet per user. Private key stored as an AES-GCM envelope.
trader_trades
Every fill, with input, output, gas and the price used. Cost basis is derived from this.
utility_bots
Tenant bot registry: owner wallet, encrypted token, branding, paired coin, fee bps.
token_metadata
Launch records. Optional for trading, since token data is read from the chain.

Point Telegram at it

One route serves every bot. The bot id in the path selects the tenant, the secret header proves the update came from Telegram, and the decrypted token is only held for the life of that request.

# first party bot
curl -s "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
  -d url=https://yourdomain.com/api/public/telegram/bot \
  -d secret_token=$TELEGRAM_WEBHOOK_SECRET

# a tenant bot: same code path, its own token and branding
curl -s "https://api.telegram.org/bot$TENANT_TOKEN/setWebhook" \
  -d url=https://yourdomain.com/api/public/utility-bot/$BOT_ID \
  -d secret_token=$DERIVED_SECRET

Data endpoints for integrators

The bot reads prices, pools, holders and trades through these. They are open, so your own bot, terminal or agent can use them instead of running an indexer. Read only, cached for a few seconds, full reference at /docs/api.

GET  /api/public/launchpad/tokens?limit=50&graduated=false
GET  /api/public/launchpad/token/0x...              price, cap, liquidity, holders, utilityBot
GET  /api/public/launchpad/token/0x.../trades?limit=100
GET  /api/public/launchpad/token/0x.../holders
GET  /api/public/launchpad/token/0x.../candles?tf=5m
GET  /api/public/launchpad/token/0x.../creator-earnings
GET  /api/public/launchpad/vault/0x...              stock dividend rounds and claims
GET  /api/public/launchpad/stats
POST /api/public/rpc                                proxied Robinhood Chain JSON-RPC
curl -s https://lemon.fun/api/public/launchpad/token/0xf0e17e54239cd945cd7bea471a3a2ca6a8c7f7a3 \
  | jq '{symbol, priceUsd, marketCapUsd, utilityBot}'

Before you take it live

Hosted wallets are real custody. Read walletCrypto.server.ts and traderWallets.server.ts before you accept a single deposit, keep the master key out of every log, and rotate it with a re encrypt pass rather than in place. Row level security is what stops one bot from reading another bot's users, so keep the policies when you copy the schema. The execution engine retries only on a confirmed revert, never on a confirmation timeout, which is the difference between a second attempt and a double spend.

Shipped something with this, or found a hole? hello@lemon.fun or @lemondotfun. Security reports get answered first.