Appearance
Configuration
Zephyr persists state across a few files in the working directory:
config.toml: your preferences. RPC endpoints, processor toggles, buy/sell transaction params, and the[trade]defaults used to seed new mints.positions.json: runtime state. The currently-active mint, per-mint trade params (entry %, exit %, target change delay) that override[trade]defaults once a mint has a record, and per-mint trade stats / PnL history.wallet.enc+wallet.key: your trading wallet, encrypted. Created on first start from theprivate_keyyou put inconfig.toml(which is then removed). Back up both together — one without the other is useless.
When you change settings from the UI:
- Processor toggles, tips, delays, and buy/sell tx params → saved to
config.toml. - Active mint → saved to
positions.json(top-levelactive_mint), never touchesconfig.toml. - Entry %, exit %, or target change delay with an active mint → saved to that mint's record in
positions.json. Without an active mint → saved toconfig.toml[trade]defaults.
Buy amount is not persisted. Each UI client tracks its own last-used amount and sends it with every buy command.
You edit config.toml manually for initial setup and fields that aren't exposed in the UI (private_key, rpc_url, optionally grpc_url).
Full config.toml
This is the same config shipped in Getting Started. Copy this as your starting point and fill in your details.
toml
[trade]
# Defaults used to seed new mints. Per-mint overrides live in positions.json.
entry_pct = -10.0 # Entry target as % from reference price (negative = wait for dip)
exit_pct = 2.0 # Take-profit target as % above buy price
target_change_delay = 1000 # Ms between reference price updates (auto-rebuy)
[buy]
slippage = 150 # Slippage in bps (150 = 1.5%)
max_block_difference = 3 # Transaction freshness window in slots
nonce_delay = 25 # Nonce rotation interval in ms
panic_slippage = 1000 # Slippage for panic buy in bps
[buy.dynamic_fee]
percentile = 7500 # Fee percentile (×100, so 7500 = p75)
fee_multiplier = 1.0 # Multiply raw fee by this factor
fee_cap = 500000 # Max fee in microlamports (0 = no cap)
[buy.dynamic_tip]
percentile = 8500 # Must be in [general].tip_percentiles
tip_multiplier = 1.0 # Multiply raw tip by this factor
tip_floor = 0.003 # Minimum SOL tip (stream-fallback)
tip_cap = 0.005 # Maximum SOL tip; set == tip_floor to pin
[sell]
slippage = 200
max_block_difference = 5
nonce_delay = 25
panic_slippage = 2000
[sell.dynamic_fee]
percentile = 8500
fee_multiplier = 1.0
fee_cap = 800000
[sell.dynamic_tip]
percentile = 8500
tip_multiplier = 1.0
tip_floor = 0.003
tip_cap = 0.007
[network]
rpc_url = "<your-rpc-url>" # Solana RPC endpoint (read-only) — leave empty to use the Zephyr-provided default
grpc_url = "" # gRPC endpoint for real-time streaming (leave empty to use the Zephyr-provided default; Helius LaserStream NY/Frankfurt auto-picked by region)
grpc_token = "" # Auth token for the gRPC endpoint (ignored when grpc_url is empty)
processor = "all" # "all" or a specific one: jito, helius, etc.
[network.node]
enabled = true # Enable anonymous RPC sender
urls = [
"http://rpc-slc1.thornode.io",
"http://rpc-va-zesty.thornode.io",
"http://rpc-ny.thornode.io",
"https://rpc.shyft.to?api_key=<your-shyft-key>",
] # Send endpoints. MUST NOT reuse network.rpc_url
buy_delay = 50
sell_delay = 50
[network.jito]
enabled = true
buy_delay = 100
sell_delay = 100
# Optional per-processor override. Empty = use Zephyr's default fan-out.
# Non-empty = exact list that fires (good for non-NY VPS — restrict to
# nearby regions). Available on every processor and each bifrost subprovider.
# endpoints = [
# "https://frankfurt.mainnet.block-engine.jito.wtf",
# "https://amsterdam.mainnet.block-engine.jito.wtf",
# ]
[network.nextblock]
enabled = true
buy_delay = 25
sell_delay = 25
[network.helius]
enabled = true
buy_delay = 25
sell_delay = 25
[network.zeroslot]
enabled = true
buy_delay = 25
sell_delay = 25
[network.nozomi]
enabled = true
buy_delay = 25
sell_delay = 25
# Lunar has no Zephyr-provided key — keep it disabled unless you add your
# own api key below (an enabled Lunar with no key sends nothing, silently).
[network.lunar]
enabled = false
buy_delay = 25
sell_delay = 25
[network.astralane]
enabled = true
buy_delay = 25
sell_delay = 25
[network.stellium]
enabled = true
buy_delay = 25
sell_delay = 25
[network.falcon]
enabled = true
buy_delay = 25
sell_delay = 25
[network.bifrost_bloxroute]
enabled = true
buy_delay = 50
sell_delay = 50
[network.bifrost_astralane]
enabled = false
buy_delay = 50
sell_delay = 50
[network.bifrost_blockrazor]
enabled = true
buy_delay = 100
sell_delay = 100
[network.bifrost_lunarlander]
enabled = false
buy_delay = 100
sell_delay = 100
# Beam (RPC Fast) — one account, three relays. Needs your own Beam key.
# Enable the relays you want; the rate limit is per ACCOUNT, so split your
# account TPS across their api_keys[].tps rather than repeating it.
[network.beam_astralane]
enabled = false
buy_delay = 25
sell_delay = 25
[network.beam_bloxroute]
enabled = false
buy_delay = 25
sell_delay = 25
[network.beam_falcon]
enabled = false
buy_delay = 25
sell_delay = 25
# API keys live as array-of-tables. Each [[...api_keys]] adds one key with
# its own TPS budget. First-fit rotation; aggregate processor TPS = sum of
# per-key `tps` values. A send must clear BOTH the processor's
# buy_delay/sell_delay AND the selected key's `1000/tps`-ms interval.
[[network.jito.api_keys]]
api_key = "<your-jito-key>"
tps = 10
[[network.nextblock.api_keys]]
api_key = "<your-nextblock-key>"
tps = 50
# Optional: Zephyr provides a default Helius Sender key (per-IP TPS slice
# from a shared pool), no api key required. Add your own here only if you
# need more capacity (it's used as spillover).
# [[network.helius.api_keys]]
# api_key = "<your-helius-key>"
# tps = 50
# Optional — Zephyr provides 40 TPS on 0slot by default, no api key
# required. Add your own here only if you need more capacity (it's used
# as spillover).
# [[network.zeroslot.api_keys]]
# api_key = "<your-zeroslot-key>"
# tps = 50
# Optional: Zephyr provides 40 TPS on Nozomi by default, no api key
# required. Add your own here only if you need more capacity (it's used
# as spillover).
# [[network.nozomi.api_keys]]
# api_key = "<your-nozomi-key>"
# tps = 50
# Optional — Lunar has no Zephyr-provided default key. To use it, add your
# own Lunar api key here.
# [[network.lunar.api_keys]]
# api_key = "<your-lunar-key>"
# tps = 50
# Optional: Zephyr provides a default Astralane key, no api key required.
# Add your own here only if you need more capacity (it's used as spillover).
# [[network.astralane.api_keys]]
# api_key = "<your-astralane-key>"
# tps = 50
[[network.stellium.api_keys]]
api_key = "<your-stellium-key>"
tps = 50
# Optional — Zephyr provides 40 TPS on Falcon by default, no api key
# required. Add your own here only if you need more capacity (it's used
# as spillover).
# [[network.falcon.api_keys]]
# api_key = "<your-falcon-key>"
# tps = 50
# Beam needs your own key (no Zephyr default). One Beam key works for all
# three relays; split your account's TPS budget across them rather than
# repeating the full amount.
# [[network.beam_astralane.api_keys]]
# api_key = "<your-beam-key>"
# tps = 1
# [[network.beam_bloxroute.api_keys]]
# api_key = "<your-beam-key>"
# tps = 1
# [[network.beam_falcon.api_keys]]
# api_key = "<your-beam-key>"
# tps = 1
[general]
private_key = "<your-base58-private-key>" # Set once; encrypted to wallet.enc + wallet.key on first start, then removed from this file
access_code = "<your-access-code>" # Required: bot refuses to start if empty. Use a long random string
fee_percentiles = [5500, 6500, 7000, 7500, 8000, 8500, 9000] # Fee percentiles surfaced in the fee bar
tip_percentiles = [7000, 7500, 8000, 8500, 9000, 9800] # Tip percentiles surfaced in the tip bar
log_level = "info" # Log verbosity: error|warn|info|debug|trace ("trace" = full per-tx firehose)
nonces = [] # Nonce pubkeys (populated by --generate-nonces)[general]
| Field | Description |
|---|---|
private_key | Your wallet's base58-encoded private key. Set it once to load (or replace) your wallet: on the next start it's encrypted into wallet.enc + wallet.key, removed from config.toml, and read from the encrypted files on every startup after. Replacing it backs up the previous wallet first. Back up both files together. |
fee_percentiles | Priority fee percentiles (×100) the backend subscribes to, displayed in the fee bar |
tip_percentiles | Tip percentiles (×100) the backend subscribes to, displayed in the tip bar |
access_code | Password the extension uses to authenticate with the backend. Required — the bot refuses to start if it's empty, since the web interface is reachable from your machine's public IP. Use a long random string. |
log_level | Log verbosity for the zephyr target: error | warn | info | debug | trace (default info). debug adds lifecycle logs; trace adds the full per-broadcast firehose. |
nonces | Durable nonce account pubkeys (populated by --generate-nonces) |
[network]
| Field | Description |
|---|---|
rpc_url | Solana RPC endpoint for read-only calls (account lookups, nonce recovery). Leave empty to use the Zephyr-provided default. |
grpc_url | gRPC endpoint for real-time streaming. Leave empty to use the Zephyr-provided default (Helius LaserStream — NY or Frankfurt region auto-selected at startup based on your machine's location) |
grpc_token | Auth token for the gRPC endpoint. Ignored when grpc_url is empty; an empty token is honored for providers that don't authenticate |
processor | Which processors to use: all or a specific one (jito, helius, etc.) |
WARNING
Never duplicate rpc_url in network.node.urls. rpc_url is the read lane, node.urls is the send lane for the Node processor. Pointing them at the same endpoint turns your read RPC into a hot send target and burns rate limit quickly.
[trade]
These three values act as defaults used to seed each new mint's record in positions.json the first time Zephyr fetches its MintCache. Once a mint has a record, its per-mint values override these defaults, and any change made in the UI while that mint is active is saved to that record, not back to config.toml.
| Field | Description |
|---|---|
entry_pct | Default entry target as % from reference price. Negative = wait for dip (e.g., -12.0 = buy when price drops 12%). |
exit_pct | Default take-profit target as % above buy price (e.g., 5.0 = sell at +5%). Triggers when net holdings (after protocol fees) would be worth amount × (1 + exit_pct/100) SOL. |
target_change_delay | Default ms between reference price updates in auto-rebuy mode. |
Session state not in [trade]:
- Active mint: stored in
positions.jsontop-levelactive_mint, so Zephyr resumes on the same mint after a restart. - Buy amount: kept in UI memory only. Flows to the backend with every
bot_buy/bot_auto_rebuycommand. A backend restart re-seeds amount fromopen_cost_solof the active mint so the take-profit target stays correct mid-Hold.
Buy / sell params
[buy] and [sell] share the same structure.
| Field | Description |
|---|---|
slippage | Slippage tolerance in bps (100 = 1%, 200 = 2%) |
max_block_difference | Transaction freshness window in slots |
nonce_delay | Nonce rotation interval in ms |
panic_slippage | Slippage for panic sell (- key) in bps. Default 1000 = 10% |
Compute unit limit is set per-AMM in
src/bot/execute.rs::cu_limit(PumpFun ~80k, PumpSwap ~120k, Raydium CPMM ~140k, Meteora DAMM ~160k). Values were sized from observed consumption + ~15% headroom so priority fee paid isn't wasted on unused CU budget.
Dynamic fee
| Field | Description |
|---|---|
percentile | Fee percentile (×100). Example: 7500 = p75. Should be one of [general].fee_percentiles; if not, it is snapped to the nearest configured value. |
fee_multiplier | Multiply the fee by this factor. 1.0 = as-is |
fee_cap | Max fee in microlamports. 0 = no cap |
Dynamic tip
Every tip-paying processor (Jito, Nextblock, Helius, Zeroslot, Nozomi, Lunar, Astralane, Stellium, Falcon, all four Bifrost subproviders, and all three Beam subproviders) draws its tip from a single dynamic value sourced from sol-fee-tracker's aggregate tip pool. The legacy per-processor tip = 0.005 field has been removed.
| Field | Description |
|---|---|
percentile | Tip percentile (×100). Should be one of [general].tip_percentiles (snapped to the nearest if not) |
tip_multiplier | Multiply the raw tip by this factor |
tip_floor | Minimum SOL tip (also stream-fallback when pool has fewer than 20 non-dust samples) |
tip_cap | Maximum SOL tip. Set equal to tip_floor to pin a constant |
The tip is the live value at percentile, multiplied by tip_multiplier, then clamped to [tip_floor, tip_cap].
Leader aware sizing
Optionally, [buy.leader_aware] / [sell.leader_aware] tune the tip and fee separately based on whether the upcoming slot leader orders by tip or by priority fee, so you stop paying for a lever that wins nothing. Off by default. See Leader Aware Tip & Fee Sizing.
Pump guard
Optionally, [buy.pump_guard] stops auto-rebuy when it detects a price spike so you do not fill a buy at the top of a pump-and-dump, leaving it to you to re-arm. Off by default. Manual Buy presses are never affected. See Pump Guard.
Processors
Each processor sends your transaction through a different block builder or RPC provider. Enable the ones you have access to.
Two rate-limiting gates
Every send passes through two gates in series:
- Processor-level delay (
buy_delay/sell_delayin ms): minimum interval between consecutive sends from this processor, regardless of which key is used. - Per-key TPS (keyed processors only): each
[[network.<proc>.api_keys]]has its owntpsbudget; the throttle walks the list first-fit and picks the next key whose1000/tps-ms interval has elapsed. Aggregate processor TPS = sum of per-key values, so adding more keys scales throughput horizontally.
Both gates must clear per send. On Too Many Requests / 429 errors, either lower the key's tps or increase the processor's buy_delay / sell_delay.
Direct processors
| Processor | Recommended TPS | Notes |
|---|---|---|
node | n/a | Anonymous RPC sender. Multi-URL via urls = [...], no api_keys block |
jito | 10 | |
nextblock | 50 | |
helius | 50 | Helius Sender. Zephyr provides a default key (no api key required); your per-IP TPS slice is carved from a shared pool. Adding your own [[network.helius.api_keys]] extends capacity (it's used as spillover) |
zeroslot | 40 | 0slot. Zephyr provides 40 TPS by default — no api key required. Adding your own [[network.zeroslot.api_keys]] extends capacity (it's used as spillover) |
nozomi | 40 | Temporal (nozomi.temporal.xyz). Zephyr provides 40 TPS by default (no api key required). Adding your own [[network.nozomi.api_keys]] extends capacity (it's used as spillover) |
lunar | 50 | Lunar Lander. No Zephyr-provided key — add your own [[network.lunar.api_keys]] to use it |
astralane | 50 | Iris2 gateway. Plain-text body (base64 of signed tx, no JSON-RPC envelope); api-key and method go in the URI query string. Shares astraXXX tip accounts with the Bifrost /astralane subroute. Zephyr provides a default key (no api key required). Adding your own [[network.astralane.api_keys]] extends capacity (it's used as spillover) |
stellium | 50 | Per-customer regional endpoints; api key is the trailing path component. Min tip 0.001 SOL (silently dropped below — tip_floor covers it) |
falcon | 40 | corvus-labs.io / falcon.wtf. NYC region only. Min tip 0.001 SOL (tip_floor covers it). Zephyr provides 40 TPS by default — no api key required. Adding your own [[network.falcon.api_keys]] extends capacity (it's used as spillover) |
Standard shape for keyed processors:
toml
[network.<name>]
enabled = true
buy_delay = 100
sell_delay = 100
[[network.<name>.api_keys]]
api_key = "your-api-key"
tps = <see table>Node is slightly different (multi-URL, no api_keys):
toml
[network.node]
enabled = true
urls = ["https://your-rpc-endpoint"]
buy_delay = 100
sell_delay = 100Multiple keys per processor
Each processor accepts any number of [[network.<proc>.api_keys]] blocks. Four 5-TPS Nextblock keys combine to a 20-TPS fleet:
toml
[[network.nextblock.api_keys]]
api_key = "key1"
tps = 5
[[network.nextblock.api_keys]]
api_key = "key2"
tps = 5
[[network.nextblock.api_keys]]
api_key = "key3"
tps = 5
[[network.nextblock.api_keys]]
api_key = "key4"
tps = 5With buy_delay = 50 (matching the 20 TPS total) the processor sends every 50 ms, rotating keys so no single key exceeds its 5 TPS budget.
Bifrost proxies
Bifrost routes through underlying providers. IP-allowlisted, no api_keys needed.
toml
[network.bifrost_bloxroute]
enabled = true
buy_delay = 50
sell_delay = 50
# Same shape for bifrost_astralane, bifrost_blockrazor, bifrost_lunarlanderBeam (RPC Fast)
Beam is one endpoint that routes to three relays — beam_astralane, beam_bloxroute, and beam_falcon — each its own toggleable subprovider. Unlike Bifrost, Beam needs an API key (one Beam account works for all three), so each subprovider takes its own api_keys block:
toml
[network.beam_bloxroute]
enabled = true
buy_delay = 25
sell_delay = 25
[[network.beam_bloxroute.api_keys]]
api_key = "your-beam-key"
tps = 1
# Same shape for beam_astralane, beam_falconBeam's rate limit is per account, not per relay. The three subproviders rate-limit independently, so when you enable more than one with the same key, split your account's TPS budget across their tps values rather than repeating the full amount (e.g. a ~3 TPS account → tps = 1 on each).
Endpoint override
Every processor (including each Bifrost and Beam subprovider) accepts an optional endpoints list to override the baked-in regional fan-out. Empty / missing = use Zephyr's defaults (today's behavior). Non-empty = exact list that fires.
The audience is operators running a non-NY VPS — by default Zephyr fans out to NY-biased endpoints, which adds 80–150 ms WAN latency from Frankfurt or Tokyo. Set endpoints to nearby regions only:
toml
[network.jito]
enabled = true
buy_delay = 100
sell_delay = 100
endpoints = [
"https://frankfurt.mainnet.block-engine.jito.wtf",
"https://amsterdam.mainnet.block-engine.jito.wtf",
"https://london.mainnet.block-engine.jito.wtf",
]Entries are origins only — Zephyr appends each processor's auth scheme (path component, query param, header) the same way it does for the defaults.
Nonce vs blockhash (tx_source)
Each processor accepts an optional tx_source = "nonce" (default) or "blockhash", applied to both buy and sell:
toml
[network.nozomi]
enabled = true
tx_source = "nonce" # durable-nonce txs (never expire)
[network.jito]
enabled = true
tx_source = "blockhash" # recent-blockhash txs (expire ~60s)With a nonce pool configured ([general].nonces), every processor defaults to resting on a durable nonce. Set tx_source = "blockhash" on a processor to send recent-blockhash transactions instead. With no nonce pool configured, a nonce processor is skipped — it sends nothing rather than downgrade to blockhash (which would bleed fees on reverting spam); set tx_source = "blockhash" explicitly to trade without durable nonces. Every variant carries the same on-chain dedup id, so exactly one transaction lands regardless of the mix.
Default endpoints
The lists Zephyr fans out to when endpoints is empty / missing. NY-biased by default. Override per processor as shown above for non-NY VPS deployments.
| Processor | Default origins |
|---|---|
jito | https://{ny,slc,amsterdam,frankfurt,london,singapore,tokyo}.mainnet.block-engine.jito.wtf |
helius | http://{ewr,slc,fra,ams,lon,sg,tyo}-sender.helius-rpc.com |
nextblock | http://{ny,slc,fra,tokyo,london}.nextblock.io |
nozomi | https://{pit1,ewr1,ash1,lax1,ams1,lon1,sgp1}.nozomi.temporal.xyz (tyo1 disabled — unstable) |
zeroslot | http://ny1.0slot.trade |
lunar | http://nyc.lunar-lander.hellomoon.io (NYC only — Lunar concentrates landing there) |
astralane | https://edge.astralane.io, http://ny.gateway.astralane.io, http://la.gateway.astralane.io |
stellium | http://ewr1.flashrpc.com |
falcon | http://nyc.falcon.wtf (NYC region only — additional regions added as Falcon expands) |
bifrost_* | http://bifrost-ny.thornode.io (single Thor deployment; each subprovider — bloxroute, astralane, blockrazor, lunarlander — is a path on this origin) |
beam_* | https://beam.rpcfast.com (single endpoint; the relay — astralane, bloxroute, falcon — is chosen via a query param on this origin) |
If a region you'd expect (Lunar EU/Asia, Bifrost AMS/DE/AP, Astralane FR/AMS/JP) isn't in this list, it's off by default — either underperforming today or requiring per-customer allowlisting. Set endpoints = [...] to opt in.
Nonce pool
Required when multiple processors are enabled. Reduces wasted transaction fees.
Create nonce accounts:
bash
./zephyr --generate-nonces 64Pubkeys are appended to config.toml automatically. Run again to add more. Rent is reclaimable. If you see Nonce pool exhausted warnings, add more.
nonce_delay in [buy] / [sell] controls rotation interval (ms). Nonces are automatically refilled when transactions land on-chain.
