Solard Documentation
Solard is a local-first Solana trading terminal and a multi-wallet CLI/SDK for memecoin traders and AI agents. It runs entirely on your machine: transactions are constructed locally against venue programs directly (no Jupiter, no aggregator), signed with keys that never leave an encrypted local SQLite database, and submitted through your own RPC, Helius Sender, or Jito.
There is one binary with two personalities:
- solard start — boots the supervised worker fleet and the web console (live pump feed, trading, portfolio, launches, signals).
- solard <command> — the headless CLI: wallets, groups, trades, quotes, launches, scripts. Everything the console does, scriptable.
Both read and write the same local database, so anything you do in one is visible in the other — and in any script or agent you point at it.
#Quickstart
Requirements
- Bun ≥ 1.1 — solard is Bun-native (SQLite, workers, single-file compile).
- A Helius API key (free tier works for the feed; paid for LaserStream/Sender).
- Optional: a Jito block engine endpoint for bundles.
Install
$ git clone https://github.com/7flash/solard $ cd solard $ bun install $ cp .env.example .env # then set HELIUS_RPC_URL + SOLARD_MASTER_KEY
Boot the console
$ bun bin/solard.ts start --open # workers come online, console opens at http://localhost:3000 # flags: --host 0.0.0.0 --port 4000 to serve on your LAN
First (simulated) trade
$ solard import <base58_private_key> main # encrypted with SOLARD_MASTER_KEY $ solard token <token_ca> wif # register + fetch metadata $ solard quote buy wif --sol 0.5 # inspect the quote, nothing sent $ solard buy wif -w main --sol 0.5 --simulate-only
Nothing in the steps above can spend SOL. Live execution is a separate, explicit decision — see the safety model.
#How it works
Solard is a set of independent workers supervised by bgrun, all writing into one local SQLite database, with three read surfaces on top. No worker talks to another worker; the database is the only contract.
The worker fleet
| worker | kind | what it does |
|---|---|---|
| server-worker | server | Serves the web console and the HTTP API. |
| helius-logs | stream | Standard logsSubscribe on the pump program; parses create/trade events from logs. |
| helius-laserstream | stream | transactionSubscribe over LaserStream for full parsed transactions with lower latency. |
| pumpportal-live | stream | Independent second opinion on the same market — used to cross-check the Helius feeds. |
| curve-snapshots | stream | Polls bonding-curve accounts directly for ground-truth reserves and progress. |
| holder-snapshots | stream | Samples largest token accounts on an interval; powers the distribution view. |
| metadata-repair | stream | Backfills missing token metadata behind a circuit breaker (fail fast, cool down, retry). |
| reconciler | reconciler | Compares feeds, marks stale ingestion, repairs divergence between sources. |
| telegram-signals | signals | Polls configured Telegram channels and projects calls into the signals table. |
Supervision & tracing
Every worker registers with bgrun with a staleness deadline — if a stream stops writing heartbeats, it's restarted, and the console shows it as degraded before your positions notice. Every operation is traced end-to-end with measure-fn: quotes, sends, confirmations, feed ingestion — all have timing spans you can inspect when something is slow.
Why multiple feeds for the same market?
Because each one lies differently. Log parsing is cheap but drops events under load; LaserStream is complete but is a different subscription model; PumpPortal is someone else's infrastructure. The reconciler's job is to notice when they disagree, and the live doctor (/api/terminal/doctor) will tell you exactly which feed is stale and since when.
#CLI reference
Everything below persists into the shared database and is immediately visible in the web console. Run solard help for the always-current version of this list.
Wallets & tokens
solard import <private_key> [name] # encrypted at rest cat key.json | solard import --stdin [name] solard wallets [--token <token>] [--addresses-only] solard token <token_ca> [name] [--metadata-json <json>] solard token set <token|ca> [--pool <address>] [--quote-mint <mint>] solard token refresh <token|ca> solard tokens
Groups
solard group create <name> [description] solard group add <group> <wallet> [weight_bps] # weighted sizing solard group add-many <group> w1,w2,w3 solard group show <group> solard group list
Prices & quotes
solard quote buy <token|ca> --sol 0.5 # inspect without submitting solard price <token|ca> # sample current venue price solard price average <token|ca> --period 15m solard price watch <token...> [--interval 1s] [--period 1m]
Trading
solard buy <token|ca> (-w <wallet> | --wallets w1,w2 | --group <name>) --sol 0.5 [--slippage-bps 1500] [--sender rpc|helius|jito] [--simulate-only] solard sell <token|ca> (-w <wallet> | --wallets w1,w2 | --group <name>) [--bps 10000] [--slippage-bps 1500] [--sender rpc|helius|jito] [--simulate-only] solard claim <token|ca> -w <wallet> # pump creator fees solard unwrap-wsol (-w <wallet> | --group <name>)
Exactly one of --wallet, --wallets, or --group must be supplied. Group trades over --sender jito are packed as bundles, five transactions per bundle. --bps 10000 on sell means the full position; --bps 5000 is half.
Watching & transactions
solard watch token <token|ca> [label] solard watch wallet <wallet> [label] solard watch program <address> [label] solard watch list solard history solard jito tip-accounts solard alt create -w <wallet> # address lookup tables solard alt extend <address> -w <wallet> <account...>
Scripts & workers
solard scripts # list solard.config.ts registrations solard run <name-or-path> [flags...] solard workers status # fleet health from the CLI solard workers ensure # start anything that's down
#Web console
solard start --open serves the console at http://localhost:3000. It is a thin, fast view over the same database the CLI writes — SSR-rendered, keyboard-driven, no build step.
| page | what's on it |
|---|---|
| /terminal | The live pump feed: new creates, trades, curve progress, SMA indicators, per-token holder distribution, feed-health strip. |
| /trade | Buy/sell forms against any registered token, wallet/group selector, sender choice, quote preview, simulation results before anything is live. |
| /portfolio | Positions across all wallets and groups: cost basis, current venue price, unrealized PnL, claimable creator fees. |
| /wallets | Import, group management, weights, batch SOL balances, batch fund/sweep. |
| /watchlists | Watched tokens, wallets, and programs with live activity. |
| /launch | Full pump.fun launch flow: metadata upload, vanity mint, buyer-group coordination, submit mode — as a form. Launches run as background jobs with step-by-step logs. |
| /signals | Telegram signal projections mapped onto live token state. |
| /activity | Everything solard has executed: sends, confirmations, errors, with measure-fn traces. |
Auth
The console binds to localhost by default and needs no auth. If you serve it beyond localhost (--host 0.0.0.0), set SOLARD_WEB_TOKEN — every browser and API call must then send it as the x-solard-web-token header.
#Venues & routing
A venue is anywhere a token can be traded. Solard ships two venue plugins and constructs their instructions manually — no aggregator in the path, which means no aggregator fees, no extra CPI depth, and quotes computed from on-chain state you fetched yourself.
- pump.fun bonding curve, pre-migration
- quote = exact curve math from the bonding account
- buy/sell + creator-fee claims
- launch deployments happen here
- PumpSwap pool, post-migration
- quote = constant-product from pool reserves
- buy/sell via direct AMM CPIs
- pool + quote mint overridable per token
The route resolver
You never pick a venue by hand. For each trade the resolver reads the token's on-chain state: if the bonding curve is live and incomplete, the trade routes to pump-curve; once the token has migrated, it routes to pumpswap-amm using the stored (or auto-discovered) pool. The decision happens at quote time, so a token that migrates between your quote and your send fails loudly instead of trading against a dead curve.
Venues are a plugin interface — adding a new one means implementing quoteBuy / quoteSell / instruction builders, and every workflow, script, and console form gets it for free.
#Senders
Venue plugins produce instructions; senders get them on chain. Pick per trade with --sender.
| sender | path | when to use |
|---|---|---|
| rpc | Your RPC's sendTransaction with priority fees. | Default. Cheap, fine for anything that isn't a race. |
| helius | Helius Sender fast lane with a tip account. | Latency-sensitive single transactions — snipes, exits. Configure HELIUS_SENDER_URL + HELIUS_TIP_ACCOUNT. |
| jito | Bundles via a Jito block engine. | Atomic group execution: 5 tx per bundle, all-or-nothing per bundle. Group buys/sells and coordinated launch buys. |
Every sender path supports simulation first; --simulate-only runs the full pipeline — quote, build, simulate — and stops before broadcast, printing compute units and balance changes.
#Launching tokens
A pump.fun launch in solard is one command coordinating three phases: metadata → deployment → buyer wave.
$ solard launch pump --creator dev \ --image ./logo.png --description "..." \ --buyer-group snipers \ --submit-mode spam-after-market-ready \ --sender-tps 40 --helius-tip-sol 0.01 \ --live
Metadata
Uploaded to IPFS through the pump frontend endpoint by default, or Pinata with PUMP_METADATA_PROVIDER=pinata + a JWT. Twitter/Telegram/website/video links ride along; --hide-name supported. You can also pre-upload with solard metadata upload and pass --uri.
Vanity mints
The launcher can grind a mint keypair whose address ends in your chosen suffix (multi-threaded, runs while you prepare metadata), so your CA reads like ...pump or your ticker.
Submit modes
| mode | behavior |
|---|---|
| after-deploy-processed | Wait for the deploy tx to reach processed, then send buyer transactions. Safest, default. |
| spam-after-market-ready | Start firing buys the instant the curve account exists, resending at --sender-tps until confirmed. For competitive launches. |
| fast-spam | Fire buys alongside the deploy without waiting. Maximum aggression, expect some failures. |
Buyer-group transactions are pre-built against a prepared address lookup table (solard run prepare-pump-launch-alt) so they fit and land smaller. Every launch runs as a background job — watch it on /launch or tail the CLI output.
#Scripts & workflows
Your strategy is a TypeScript file that imports the SDK. Register it in solard.config.ts, run it with solard run, pass it flags like any command.
import { defineSolardConfig } from "solard/runner"; export default defineSolardConfig({ scripts: { snipe: "./scripts/snipe.ts", dca: { path: "./scripts/dca.ts", description: "laddered entries" }, }, });
import { openSolard } from "solard"; const solard = await openSolard(); // shared db + env // wait for a launch matching a name, then buy across a group const launch = await solard.launches.waitFor({ name: "EXACT NAME" }); const receipt = await solard.buy({ token: launch.mint, group: "snipers", sol: 0.05, sender: "jito", live: process.argv.includes("--live"), // simulate by default }); console.log(receipt.signatures);
Built-in workflows
| workflow | what it composes |
|---|---|
| snipe | Launch discovery → group buy the moment the market exists. |
| claim-trade-send | Claim creator fees from any claim source → buy a token quoted in the same asset → deliver the guaranteed minimum output to a recipient. Venue-agnostic. |
| wait-launch-trade-group | Block on a launch source, then execute a coordinated group entry with per-wallet sizing. |
Workflows are plugins too — they receive a host with resolveToken, route, signer, and the sender registry, so they work on any venue that exists now or later.
#AI agents
Solard was built to be driven by agents as much as by humans. There are three integration surfaces, in order of tightness. In all three, the safety gate stays in charge: an agent cannot trade live unless you flipped SOLARD_ENABLE_LIVE_TRADES=1, no matter what it asks for.
1 · The SDK — for TypeScript agents
If your agent runs in Bun/Node, import solard directly. Everything is typed: wallet refs, token refs, group refs, quotes, receipts. This is the same code path the CLI uses.
import { openSolard } from "solard"; const solard = await openSolard(); // register the agent — its actions are attributed in activity logs const agent = await solard.agents.create("scout-1", { wallet: "agent-hot", config: { maxSolPerTrade: 0.25, allowedVenues: ["pump-curve"] }, }); // read market state the workers have already indexed const feed = await solard.terminal.feed({ limit: 50 }); const holders = await solard.terminal.holders(feed[0].mint); // quote → decide → execute (simulated unless the gate is open) const quote = await solard.quoteBuy(feed[0].mint, { sol: 0.1 }); if (quote.priceImpactBps < 300) { await agent.buy({ token: feed[0].mint, sol: 0.1, sender: "helius" }); }
2 · The CLI — for tool-calling agents
For LLM agents that execute shell tools (Claude Code, custom harnesses), the CLI is the tool set. Every command reads flags, prints structured output, and exits nonzero on failure — exactly what a tool loop wants. A minimal tool manifest:
solard tokens # list what's registered solard price <token> # current venue price solard quote buy <token> --sol X # read-only quote solard buy ... --simulate-only # full dry-run with balance deltas solard buy ... # only works if the gate is open solard history # what happened
quote, price, and --simulate-only freely; require human confirmation (or a size-capped agent wallet) for anything live. The weight_bps on group membership and a dedicated low-balance agent wallet are your blast-radius controls.3 · The HTTP API — for anything else
Python agents, remote orchestrators, anything that speaks HTTP — the console's API is a complete surface. See the endpoint list. Authenticate with x-solard-web-token when the token is set.
import requests S = "http://localhost:3000" H = {"x-solard-web-token": token} feed = requests.get(f"{S}/api/terminal/feed", headers=H).json() quote = requests.post(f"{S}/api/trade/buy", headers=H, json={ "token": feed["data"][0]["mint"], "wallet": "agent-hot", "sol": 0.1, "live": False, # simulation; live=True requires the env gate }).json()
#HTTP API
All routes are served by the console server. Responses use a uniform envelope: { ok, data } on success, { ok: false, error: { code, message } } on failure.
| route | method | purpose |
|---|---|---|
| /api/status | GET | Server + gate status, versions, db path. |
| /api/health · /api/terminal/health | GET | Worker fleet health with staleness per stream. |
| /api/terminal/doctor | GET | Deep feed diagnosis — which source is stale, last event per feed. |
| /api/terminal/feed · /api/pump-live | GET | Live pump feed: creates, trades, curve progress. |
| /api/terminal/trades | GET | Trade stream for a mint. |
| /api/terminal/curve | GET | Bonding-curve snapshots for a mint. |
| /api/terminal/holders · /api/token-holders | GET | Holder distribution series (the terminal chart's data). |
| /api/market/sma | GET | SMA indicator values. |
| /api/price | GET | Venue price for a registered token. |
| /api/portfolio · /api/overview | GET | Positions, balances, PnL across wallets/groups. |
| /api/wallets/import | POST | Import a wallet (encrypted at rest). |
| /api/groups/create · /api/groups/add | POST | Group management. |
| /api/trade/buy · /api/trade/sell | POST | Execute or simulate a trade; live:true rejected unless the gate is open. |
| /api/launch/pump | POST | Start a launch job; poll /api/jobs for progress. |
| /api/signals · /api/terminal/signals | GET | Telegram signal projections. |
| /api/processes · /api/workers/ensure | GET/POST | Inspect and (re)start the worker fleet. |
#Environment
| variable | default | meaning |
|---|---|---|
| SOLARD_DB_PATH | ./solard.db | The shared SQLite database — wallets, tokens, feed, everything. |
| SOLARD_MASTER_KEY | — | Required to import/decrypt wallets. Lose it and stored keys are unrecoverable. |
| HELIUS_RPC_URL | — | Primary RPC. RPC_ENDPOINT accepted for compatibility. |
| HELIUS_API_KEY | — | Used to derive the websocket URL for stream workers. |
| HELIUS_SENDER_URL | — | Helius Sender fast-lane endpoint (regional or global). |
| HELIUS_TIP_ACCOUNT | — | Tip account for Sender submissions. |
| JITO_BLOCK_ENGINE_URL | mainnet.block-engine.jito.wtf | Only when explicitly selecting the jito sender. |
| SOLARD_ENABLE_LIVE_TRADES | 0 | The live gate. Everything is simulation until this is 1. |
| SOLARD_WEB_TOKEN | — | When set, all console/API calls must send x-solard-web-token. |
| SOLARD_PUMP_FEED_SOURCE | helius | Primary feed source for the terminal. |
| PUMP_METADATA_PROVIDER | pump-frontend | IPFS provider for launch metadata; pinata needs PINATA_JWT. |
| SOLARD_LOG_LEVEL | info | measure-fn log verbosity. |
| SOLARD_MAX_JOBS | 100 | Background job retention (launches). |
| SOLARD_METADATA_CIRCUIT_* | 5 / 60000 / 10000 | Metadata-repair circuit breaker: failures to open, open ms, half-open retry ms. |
#Safety model
Solard assumes you will eventually connect something impulsive to it — a 4am you, or an agent. The defaults are built for that.
- Dry-run by default.
SOLARD_ENABLE_LIVE_TRADES=0ships in.env.example. Live buy, sell, and launch — CLI, console, and API alike — rejectlive=trueuntil you flip it. Simulation runs the entire pipeline and reports balance deltas. - Keys encrypted at rest. Wallet secrets are encrypted with
SOLARD_MASTER_KEYbefore touching disk. The key itself lives only in your environment. - Simulate anything.
--simulate-onlyworks on every trade path including group bundles, printing compute and slippage before you commit. - Blast-radius controls for agents. Dedicated low-balance agent wallets, per-wallet
weight_bpsin groups, and per-agent config caps. - Local by default. The console binds localhost. Widening it is an explicit flag, and a token gate exists for when you do.
- Everything attributable. Every execution is logged with its origin (CLI, console, script, agent) and its full measure-fn trace on
/activity.