Skip to content

Setup

From a licence key to a running bot. Read the whole page once before you start — particularly Configure, where the numbers that govern real money live.

Requirements

  • Node.js 18 or newer. Check with node --version.
  • A Solana wallet you control, funded with SOL for trading and fees.
  • Your own Solana RPC endpoint. A public RPC will rate-limit this bot almost immediately — use a provider.
  • A machine that stays on. The bot only finds opportunities while it is running.
  • Your BOTSOL licence key.
Use a dedicated trading wallet. Fund it with only what you intend to trade. Do not put your main holdings behind a key that sits in a config file on a machine that runs unattended.

Install

Node.js 18+ is required for both. Windows and Linux: use the zip.

Unpack the download, then install dependencies:

cd solana-arb-bot npm install

Create your configuration from the supplied example:

cp .env.example .env
.env holds your private key. It is excluded from version control. Never paste its contents into a chat, an issue tracker, a support request, or any website — including this one.

Configure .env

Every variable the bot reads, with its real default. Defaults are taken from the shipped .env.example.

Required

VariableDefaultWhat it does
PRIVATE_KEY Your wallet key, base58 or a JSON byte array. Used locally to sign. Never transmitted.
RPC_URL Your Solana RPC endpoint.
LICENSE_KEY The key from your purchase.

Safety

VariableDefaultWhat it does
DRY_RUN true Runs the complete path — detect, validate, build, sign — and stops immediately before submitting. Nothing is broadcast and no funds move.
Leave DRY_RUN=true until you have watched it run. Let it operate for a while and read what it says it would have done. Set it to false only when you agree with those decisions. This is the single most useful safety habit with this software.

Trading

VariableDefaultWhat it does
TRADING_PAIRS unset Comma-separated SYMBOL/SYMBOL. Unset means exactly SOL/USDC and nothing else. Known symbols: SOL, USDC, USDT, JUP. Unknown symbols are skipped with a startup warning, not a crash. The base leg must be SOL.
MAX_TRADE_SIZE_SOL 0.1 Size of each attempt, in SOL.
MIN_PROFIT_THRESHOLD_PCT 0.5 Minimum round-trip profit, in percentage points, before a trade is considered. Computed from real quote amounts, which already net out AMM swap fees.
POLL_INTERVAL_MS 3000 How often to look. Lower means more RPC and Jupiter calls — check your rate limits.
SLIPPAGE_BPS 50 Slippage tolerance passed to Jupiter. 50 = 0.5%.
Profit does not include network costs. MIN_PROFIT_THRESHOLD_PCT is measured from swap quotes, which account for AMM fees but not Solana network fees, priority fees, or Jito tips. Set it high enough to cover those, or you can win trades on paper and still lose balance.

Pre-execution validation

These tune the re-check that runs immediately before every trade. The check itself cannot be disabled — only these numbers are configurable.

VariableDefaultWhat it does
MAX_PRICE_IMPACT_PCT 1.0 Maximum acceptable price impact on the worse of the two legs. Guards against thin liquidity.
MAX_PROFIT_DEGRADATION_PCT 0.2 Maximum drop, in percentage points, between the profit seen at detection and at the final re-check. A larger drop blocks the trade even if profit still clears the threshold — it signals an opportunity that is closing.

Jupiter API

VariableDefaultWhat it does
JUPITER_API_BASE lite-api.jup.ag/swap/v1 Keyless free tier. Reduced rate limits, intended for testing.
JUPITER_API_KEY unset Required if you move to the production host.

For sustained use, Jupiter's own guidance is to use https://api.jup.ag/swap/v1 with an API key from their developer portal. Depending on your poll interval and pair count that may require a paid plan — a recurring cost you pay Jupiter directly, not us.

First run

npm start

On startup it prints what it is about to do. Check each line:

  • Your wallet address and SOL balance — confirm it is the wallet you meant
  • The pairs being watched — with TRADING_PAIRS unset this must read exactly SOL/USDC
  • Any skipped pairs, with the reason
  • Your thresholds
  • Whether Jito is on or off
  • Whether DRY_RUN is on
Skips are the bot working. Lines marked SKIP mean an opportunity was found and then rejected at the final check — the spread closed, liquidity was too thin, or profit degraded. A long run of skips is normal and healthy. A bot that never skips is one that is not checking.

Dashboard

While the bot runs it serves a local dashboard. Open it at:

http://127.0.0.1:4747

Change the port with DASHBOARD_PORT in your .env.

It is local only. The dashboard binds to 127.0.0.1 and is never exposed to your network. That is not configurable. Anyone else on your network cannot reach it.

Status tab

  • Whether the bot is running or paused, and a pause/resume control
  • Your wallet's public address and SOL balance — never your private key
  • The pairs actually being watched, plus any that were skipped and why
  • Your thresholds, read-only
  • Realised profit and loss for the session, labelled as such
  • Skipped opportunities, grouped by reason
  • A live activity log you can filter and copy for support

This tab works with no internet connection. If the bot stops sending updates the page dims and says so, rather than showing old numbers as though they were live.

Buy Licence and Builder Pro tabs

These talk to the licence server, so they need LICENSE_SERVER_BASE_URL set. If it is not set, they say so and name the variable. The Status tab is unaffected either way.

Jito bundle execution Optional

Off by default. When off, trades are submitted through your own RPC exactly as they otherwise would be.

VariableDefaultWhat it does
ENABLE_JITO_BUNDLES false Submit each leg as a Jito bundle instead of a plain RPC transaction.
JITO_TIP_LAMPORTS 100000 Tip per attempt. 100000 lamports = 0.0001 SOL.
The tip is a recurring cost, not a setup fee. It is paid on every attempt, whether the trade wins or loses. A round trip is two legs, so it pays the tip twice. The default of 100000 lamports is a starting point for testing and is unlikely to win against serious competition without tuning upward — which raises your per-attempt cost proportionally.

Tip accounts are fetched live from Jito on every submission and never hardcoded. The tip is always a separate transaction alongside the swap; the swap transaction itself is never modified.

Remote viewing Optional

Not yet available. The relay that lets you view a running bot from another machine is still in development. When it ships it will be opt-in, outbound-only, and carry status and pause/resume controls only — never keys, never funds.

Troubleshooting

PRIVATE_KEY is not set

Your .env is missing or the variable is empty. Confirm the file is named exactly .env, sits in the same folder as index.js, and that you copied it from .env.example.

PRIVATE_KEY could not be parsed

The value is not a valid base58 string or JSON byte array. Re-export it from your wallet. The bot never logs the value itself, so the error will not show you what it read.

RPC_URL is not set, or constant rate-limit errors

You are on a public RPC or have none configured. Get an endpoint from a provider. This is the single most common cause of a bot that appears to do nothing.

It finds opportunities but never trades

Expected, and usually correct. Check whether DRY_RUN is still true — in that mode it never submits. Otherwise read the SKIP reasons: the spread closed, price impact exceeded your limit, or profit degraded past your tolerance.

It never finds anything at all

Most likely your threshold is above what the market is offering. Real round-trip spreads on major pairs are frequently a small fraction of a percent. Lowering the threshold finds more — but remember it must still cover network fees and any tips, or the trades lose money.

A pair I configured is not being watched

Check the startup warnings. Unknown symbols, malformed entries, and pairs whose base is not SOL are all skipped with a stated reason.

Risk. Arbitrage is competitive and profitability is not guaranteed. This software finds and executes opportunities; it does not create them. Never trade funds you cannot afford to lose.