A bot runs out of SOL in the middle of a volatile stretch. It had been running for a month on the same balance.
★Average consumption tells you nothing about the balance you need.★ Costs cluster, and they cluster at exactly the moment you cannot afford to stop.
Size for the Worst Hour
The mistake is budgeting from the mean:
// ★Wrong: fails on the first bad hour.★
const balance = avgCostPerTrade * expectedTradesPerDay;
Three things move together during volatility, and each multiplies the others:
Priority fees rise. Contention is per-account, and it spikes when everyone wants the same pool.
Failure rate rises. More reverts, more expiries, more retries — each attempt paying a fee for nothing.
Trade frequency rises. Your strategy fires more often precisely when conditions are worst.
★A reasonable floor is the cost of an extended run of consecutive failures at elevated fees, measured with getRecentPrioritizationFees on the accounts you contend for — not a day of average activity.★
const reserve =
worstCaseFeePerAttempt * attemptsPerHour * hoursOfAutonomy
+ rentForExpectedNewAccounts
+ rentExemptMinimum;
hoursOfAutonomy is the real parameter — how long the bot must survive without a human. A bot monitored during business hours needs less than one running unattended over a weekend.
Separate the Wallets
One wallet for everything is convenient and makes every problem worse:
| Wallet | Holds | Risk |
|---|---|---|
| ★Hot / trading★ | ★Working capital only★ | ★Assume total loss★ |
| Fee payer | SOL for fees | Wasted fees only |
| Treasury | ★Everything else★ | ★Never online★ |
★The hot wallet's balance is the maximum you can lose to a compromised process.★ That framing sets the number better than any efficiency argument — it should hold what the strategy needs to operate and nothing more.
Separating the fee payer from the trading authority adds a second bound: a leaked fee-payer key can waste fees but cannot move positions.
If your balance is sized correctly and transactions still miss, a free BoltTx key is one line to test the submission path.
Top-Ups Must Not Race
The naive automatic top-up double-sends the first time it runs concurrently:
// ★Broken: check-then-act.★
if (await getBalance(hot) < threshold) {
await transferFromTreasury(amount);
}
Two instances, or one instance with overlapping timers, both read a low balance before either transfer lands.
// ★Atomic claim, plus a pending check.★
const claimed = await db.query(
`INSERT INTO topups (wallet, hour_bucket) VALUES ($1, $2)
ON CONFLICT DO NOTHING RETURNING id`,
[hot.toBase58(), currentHourBucket],
);
if (!claimed.rowCount) return;
if (await hasPendingTopUp(hot)) return; // ★one in flight is enough★
★A top-up in flight has not landed yet, so the balance still reads low.★ Without tracking pending transfers, a bot below threshold will keep topping up every cycle until the first one confirms.
Alert on the rate, not just the event. Frequent top-ups mean the reserve is undersized; a top-up that fails means the bot is about to stop.
Reclaim the Rent You Are Sitting On
A bot trading many tokens accumulates empty token accounts, each holding rent-exempt lamports that do nothing:
const accounts = await connection.getParsedTokenAccountsByOwner(
wallet, { programId: TOKEN_PROGRAM_ID },
);
const empty = accounts.value.filter(
(a) => a.account.data.parsed.info.tokenAmount.uiAmount === 0,
);
// createCloseAccountInstruction per account, batched.
★This is recovering your own capital, not earning anything.★ For a bot that has traded hundreds of tokens it is a meaningful sum, and closing is one instruction per account so it batches cheaply.
Two cautions: do not close an account you are about to use again, and remember wrapped SOL — closing that account unwraps it back to native SOL, which is sometimes exactly what you want and sometimes a surprise.
Monitor Runway, Not Balance
A balance number tells you nothing without a burn rate:
const burnPerHour = spentLastDay / 24;
const runwayHours = (balance - rentExemptMinimum) / burnPerHour;
if (runwayHours < 12) alert(`runway ${runwayHours.toFixed(1)}h`);
★Alert on hours remaining, not on lamports.★ A threshold in lamports is wrong at least half the time — too sensitive when fees are cheap, too late when they are not. Runway self-adjusts because the burn rate moves with conditions.
Worth tracking alongside it:
Rent locked versus SOL spent. Locked rent is recoverable capital, and treating it as an expense understates what you actually have.
Cost per successful trade. Rising while the success rate holds means fees are climbing, and your reserve assumption is drifting stale.
Stop Cleanly, Not Abruptly
A bot that hits zero mid-flight leaves positions open and transactions unresolved. Decide the stopping behaviour in advance:
Reserve for exits. ★Keep enough to close every open position at elevated fees, and never spend it on entries.★ Running out of SOL while holding positions you cannot exit is the expensive version of this failure.
Degrade before stopping. Below a soft threshold, stop opening and continue managing what is open.
Alert before it matters. A warning at twelve hours of runway is actionable; one at zero is an incident.
What Landing Looks Like
Real transactions through our delivery nodes: median confirmation 336ms — under one slot.
★Landing reliability feeds directly into how much SOL you need.★ Every expiry means a rebuild and another fee, so a wide tail raises your cost per successful trade and shortens the runway a given balance buys.
Where BoltTx Fits
We handle submission. Wallet structure and funding stay entirely yours.
Submissions route through our own delivery nodes in four regions with stake-weighted routing and no public mempool exposure, so a transaction is not observable in transit before it lands.
You sign locally. We never hold funds, never sign, and never modify transaction contents. ★The tip travels inside the transaction and is paid on chain from your own wallet, so it belongs in the reserve you size for★ — and it reverts with the transaction if it fails, because that is how Solana handles atomic transactions. There is no monthly fee, and you pay only on transactions that reach the chain.
const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");
FAQ
How much SOL should a trading bot hold? Enough to survive an extended run of consecutive failures at elevated fees, plus rent for accounts it will create, plus the rent-exempt minimum. Sizing from average cost fails on the first bad hour.
Why does my bot run out of SOL during volatility? Because fees, failure rate, and trade frequency all rise together, and each multiplies the others. A balance sized from average conditions is undersized exactly when it matters.
Should a bot use one wallet or several? Several. The hot wallet should hold only working capital, since its balance is the maximum a compromised process can lose. Separating the fee payer bounds the damage further.
How do I automate SOL top-ups safely? With an atomic claim in shared storage plus a check for transfers already in flight. A plain balance check races, and a pending top-up still reads as a low balance.
Why does my bot top up repeatedly? Because the pending transfer has not landed, so the balance still looks low. Track in-flight top-ups explicitly, or every cycle triggers another one until the first confirms.
How do I reclaim rent from empty token accounts? Close them. Each returns its rent-exempt lamports, and closing is one instruction per account so it batches cheaply. A bot that has traded many tokens accumulates a real sum.
Is rent an expense or an asset? An asset. It is locked rather than spent and returns when the account closes, so track it separately from fees or you will understate the capital you actually hold.
What should trigger a low-balance alert? Hours of runway, not a lamport threshold. Runway adjusts automatically with the burn rate, while a fixed threshold is too sensitive in calm conditions and too late in busy ones.
How do I calculate runway? Spendable balance divided by recent burn per hour, where spendable excludes the rent-exempt minimum. Recompute it from a rolling window so it reflects current fee conditions.
Should I reserve SOL for closing positions? Yes, and never spend it on entries. Running out while holding positions you cannot exit is far more costly than missing an entry you could not afford.
What happens if the fee payer runs out mid-trade? The transaction fails before execution. If it happens between related transactions, you can be left in a partial state, which is why a reserve for exits matters.
Does closing a wrapped SOL account return my SOL? Yes, it unwraps back to native SOL. That is sometimes exactly what you want during cleanup and sometimes a surprise, so exclude it from an automated sweep unless intended.
How much should the fee buffer be? Enough for many attempts at elevated priority fees, not one at the base rate. During congestion a single attempt can cost many times what it does when the network is quiet.
Should the treasury wallet be online? No. It should hold everything the bot does not need to operate, and top-ups should be the only path between it and the hot wallet.
How do I know my reserve assumption is still valid? Track cost per successful trade over time. Rising while the success rate holds means fees have climbed and your original sizing has quietly gone stale.
What is a safe degraded mode? Stop opening new positions below a soft threshold while continuing to manage open ones. That preserves the ability to exit, which is the part you cannot afford to lose.