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