Skip to content

About

Self-hosted, config-branded Telegram + Discord trading bot for the MOON.lite router/aggregator on Arc — non-custodial, real-time gRPC feed, sniper/copy/limit/trailing stop, referrals & shareable P&L cards.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

Repository files navigation

MOON.lite Whitelabel Trading Bot

A self-hosted, config-branded Telegram + Discord trading bot for the MOON.lite router and aggregator on the Arc blockchain.

You run it on your own infra under your own brand. It holds all of your users' data and keys, signs every transaction locally, and reaches the MOON.lite real-time feed + execution plane over gRPC. MOON.lite never sees a private key — the bot is non-custodial to MOON.lite (you are the custodian for your users; the UI states this plainly and lets any user export their key).

  • App: https://moonlite.so
  • API: https://api.moonlite.so
  • Chain: Arc testnet · chainId 5042002 · native ETH
  • Router (RouterV2): 0x37736562dd741A35291379cA3fa0EBb420DC4376 · swapExactIn selector 0xb78f3852 · 14-field EIP-712 SwapAuthorization

This repository is the client only: the bot, the client-side signer, the P&L card renderer, and the gRPC .proto + client shim it needs. The MOON.lite hub server, routing engine, and transaction-relay path are not here and are not required to run the bot.


⚡ Operator checklist — the 5 things you must set

Everything else has sane defaults. Copy config.example.toml → config.toml, then set:

  1. [brand] name · logo_path · colors — your identity (shown in-app and on the P&L card).
  2. [economics] partner_bps — your fee band: 0 (advertise 0% and keep positive slippage above the quote) or 3–10 bps off the top. 1 and 2 are rejected — RouterV2 has no valid regime there.
  3. [economics] fee_recipient — your payout wallet. The placeholder zero-address fails validation on purpose so you can't ship without setting it.
  4. ML_FEED_KEY (env) — your MOON.lite feed key (x-ml-key), needed only for the real-time gRPC feed + execution plane (the /quote + /swap HTTP plane is public). Register your integration on the MOON.lite partners page with your payout address — your feed key is minted instantly in the response (one per payout address; re-reveal it later by signing from your payout wallet). See Getting a feed key.
  5. TG_BOT_TOKEN (env) — from @BotFather. (Optional: DC_BOT_TOKEN for Discord.)

Then run it. The config is validated fail-fast — a bad value aborts the boot with a clear message, never a silent default:

cp config.example.toml config.toml && $EDITOR config.toml
export ML_FEED_KEY=...  TG_BOT_TOKEN=...  KEY_MASTER=<your key-encryption passphrase>
cargo run --release -- --config config.toml

New to the bot? Read Quick start below, then docs/CONFIG.md for every field.


What you get

Area Capability
Manual trading Buy / Sell by pasting a token address → live token card → amount menu → quote → sign → submit
Per-user settings Slippage (→ minOut), priority/gas tier, effective fee within your band, default amounts, auto-approve
Strategies Sniper, copy-trade, limit orders, positions, trailing stop, AFK, Twitter — each on the real-time feed
Real-time feed gRPC price / wallet / launch streams, deduped into one upstream subscription for all your users
Referrals First-touch attribution, self-funding payouts from realized fee revenue, deep-link + P&L-card QR
Withdrawals Plain ERC-20 / native transfers to any address, signed with the user key, broadcast to the public RPC
Key export Private key / seed shown once, auto-deletes after 30s (survives a restart mid-countdown)
P&L card Branded, shareable win/loss image with weighted-average cost basis and a referral QR
Branding Name, colors, logo, font, taglines, card backgrounds — all via config.toml

Quick start

1. Prerequisites

  • Rust (stable, 1.75+) and cargo
  • A Telegram bot token from @BotFather and/or a Discord bot token
  • A MOON.lite feed API key (x-ml-key) for the real-time feed + execution plane. Register your integration on the partners page with your payout address — your feed key is minted instantly in the register response (one per payout address; reveal it again later by signing from your payout wallet). See Getting a feed key.
  • A payout wallet (your [economics] fee_recipient) — where your fees land, and the address you register on the partner leaderboard.

2. Configure

cp config.example.toml config.toml
$EDITOR config.toml

Fill in your brand, your x-ml-key, your fee partner_bps (see below), and your fee_recipient wallet. Secrets (bot tokens, key-encryption passphrase) are read from environment variables referenced as env:VAR_NAME — never write real secrets into the file. See docs/CONFIG.md for every field.

3. Run

export TG_BOT_TOKEN=...        # from BotFather
export DC_BOT_TOKEN=...        # optional, for Discord
export KEY_MASTER=...          # your key-encryption passphrase (keep it safe)

cargo run --release -- --config config.toml

On start the bot validates the config fail-fast, connects the feed, rehydrates every user's strategies from its database, and re-subscribes. If on_start_open_url = true (default) it opens your operator dashboard in a browser — set it to false for headless/cron restarts.


Getting a feed key

The gRPC feed + execution plane (ml.px.v1.Fx) is key-gated; every call carries your x-ml-key (see docs/FEED-API.md). The quote & swap HTTP plane (/quote, /swap) is public and needs no key — only this real-time feed + execution layer does.

Issuance is self-serve and instant — registering your integration mints your key on the spot. No waiting, no manual coordination.

  1. Open the MOON.lite partners page and fill in Register your integration: your name, type (Web / Telegram / Discord), link, logo, and your payout address (the same wallet you put in [economics] fee_recipient). This POSTs to /partners/register.

  2. Your key is minted immediately. The response body returns your full feed key — an ml_live_… string — exactly once, at mint time, so copy it right then. There is one key per payout address (idempotent: registering the same address again just returns the existing key, masked). The key is issued provisional and works against the live feed straight away. It is delivered only in the HTTPS response body, never in a URL or query string.

  3. Set it as the ML_FEED_KEY environment variable and you're live:

export ML_FEED_KEY=ml_live_xxxxxxxxxxxxxxxx   # keep it in the env, never in config.toml

ML_FEED_KEY authenticates your feed and execution calls. The config references it as feed_key = "env:ML_FEED_KEY" (see docs/CONFIG.md). Treat it like a password — never commit it, never place it in a query string or URL.

Lost it, or setting up a second machine? The safe lookup (GET /partners/registration) only ever returns the masked key. To recover the full key you prove you own the payout address: request a challenge (POST /partners/key/challenge), sign it with that wallet (EIP-191), and reveal (POST /partners/key/reveal). Knowing or typing an address is never enough — only a signature from the registered wallet reveals the key.

Keep it earning. Keys are anti-Sybil and profit-gated: a blacklisted address is rejected at mint, and a key that routes no fees can be suspended. Run a real integration and route volume — your key stays active and your logo climbs the leaderboard.

Leaderboard vs. key. Registering also lists you on the partner leaderboard as pending until your integration is approved for public display. That approval gates only your listing, not your key — your feed key works the moment it's minted.


Fee model — partner_bps ∈ {0, 3, 4, 5, 6, 7, 8, 9, 10}

Your fee band is validated at load. 1 and 2 are rejected — they have no valid regime on RouterV2 (protocolBps must be 0, or 3..=10).

  • 0 — advertise 0% fees and capture positive slippage above the signed quoted output (never the user's minOut buffer). RouterV2 CAPTURE mode.
  • 3..=10 — an explicit fee band, taken off the top; the trader keeps any positive slippage. Paid to your fee_recipient (partnerRecipient on-chain).

Referrals pay a configurable share (default 30%) of the realized fee revenue you actually collected — never of volume — so payouts can never exceed fees received. See docs/FEATURES.md.


How execution works (non-custodial)

Every trade — manual or strategy — takes the same path:

Quote (gRPC U1)  →  reconstruct the 14-field SwapAuthorization from MlQuoteReply.auth
                 →  verify trader/recipient=user, tokenOut, amountIn, fee band, net_out
                 →  RE-DERIVE the EIP-712 digest locally (never trust the hub's)
                 →  sign that digest with the user's key (client-side EIP-712)
                 →  build swapExactIn(auth, sig, hops) calldata + sign the eth tx
                 →  Submit (gRPC U2)  →  MOON.lite relays it to RouterV2

MOON.lite's Quote (U1) returns an unsigned plan whose money-critical part is a structured MlAuth mirror — the exact 14-field SwapAuthorization RouterV2 verifies — alongside the gross/net outputs, the opaque hops_abi/candidates_abi route material, and the hub's digest. Your bot reconstructs the auth from that mirror, verifies it (trader = user, recipient = user, tokenOut/amountIn as requested, protocolBps == your band, and net_out consistent with your band), re-tightens minOut from the user's slippage, then re-derives the EIP-712 digest itself and signs that — it never trusts MlQuoteReply.digest. Submit (U2) only accepts an already-signed swapExactIn to the router — it cannot sign for a user. Withdrawals and referral payouts are plain transfers signed locally and broadcast directly to the public RPC (the source of truth for nonces).

See docs/FEED-API.md for the full gRPC surface.


Custody & security

  • Each user has ≥1 EOA. Keys are generated in the bot, encrypted at rest (Argon2id master key → per-user HKDF DEK → XChaCha20-Poly1305), and never leave your host. The database alone is useless without the key-encryption passphrase.
  • Keys are decrypted only inside the signer and zeroized immediately after use.
  • The bot re-checks every quote before signing, so a hostile or compromised hub cannot redirect funds — the signed authorization pins recipient = user and minOut.
  • You are the custodian for your users. The bot's UI states this and lets any user export their key or seed at any time.

Full threat model in docs/FEATURES.md.


Documentation

Repository layout

crates/
  signer/      # client-side EIP-712 SwapAuthorization signing (no I/O, no hardcoded keys)
  botcore/     # transport-agnostic core: config, subscription manager, key vault,
               #   strategies, referrals, withdrawals, P&L card (resvg), gRPC feed client
  bot-tg/      # Telegram presenter (teloxide)
  bot-dc/      # Discord presenter (serenity)
proto/         # the ml.px.v1.Fx .proto wire contract (client stubs generated at build)
docs/          # this documentation
config.example.toml

License

MIT — see LICENSE.

The P&L card renderer bundles the Inter typeface (© 2016 The Inter Project Authors), distributed unmodified under the SIL Open Font License 1.1 — see crates/botcore/assets/fonts/LICENSE-Inter.txt. The OFL governs the font files only; all code remains MIT.


The bot is provided as-is; you are responsible for your users' funds and for complying with the law in your jurisdiction.

About

Self-hosted, config-branded Telegram + Discord trading bot for the MOON.lite router/aggregator on Arc — non-custodial, real-time gRPC feed, sniper/copy/limit/trailing stop, referrals & shareable P&L cards.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages