TP-01 Test plan Before you trust it

Testing a Trading Bot: The Seven Layers, In Order

Testing trading bots is not one activity. It is seven, they answer different questions, and they are worth running in a fixed order because each layer is cheaper than the one after it. This page gives the order, the exit condition for each layer, and the class of defect each layer is structurally unable to find.

Applies to
Any automation that signs transactions on your behalf, whether you wrote it or rented it
Preconditions
A named build, a written configuration, a funded test wallet you are willing to lose
Pass condition
Every layer has a recorded verdict and every failed layer has a defect entry
Out of scope
Profitability, strategy quality, and any judgement about a specific product

Testing a trading bot means running seven distinct checks in a fixed order: name the thing under test, inventory the claims, review the configuration, simulate transactions, run the flow on a test cluster, rehearse on mainnet at a size you can afford to lose, and inject faults. Each layer has an exit condition, and each one is blind to something the next layer catches.

The order is not arbitrary. It runs cheapest first, and the cost curve is steep: a misread configuration value costs nothing to find in layer two and can cost the whole wallet in layer five. Teams that skip layers do not skip the defects, they relocate them into the layer where finding one is expensive and irreversible.

Naming the thing under test

Before any layer runs, write down four things: the build identifier, the exact configuration, the wallet set, and the venue. A verdict that does not name all four is not attached to anything. Three months later, "we tested it and it was fine" is unusable, because the build moved, the configuration drifted, and nobody can say which wallets produced the result.

Build identifier is a commit hash for code you control, or a version string and a date for something you rent. Configuration is the full effective config, not the diff you remember making. Wallet set is the public keys involved. Venue is the specific market or pool, because the same code behaves differently against a thin pool than against a deep one.

Where the irreversible risk sits

Every layer from five onwards signs real mainnet transactions. A confirmed Solana transaction cannot be reversed, recalled or disputed: there is no chargeback, no support desk and no rollback. Fund test wallets with an amount you have already decided to treat as spent, and keep the keys for those wallets separate from anything holding value you care about.

No step in this plan requires you to type a seed phrase or a private key into a website, a chat, a support ticket or a form. Any tool or person asking for one is asking for permanent control of the funds, and there is no legitimate testing reason for the request.

The seven layers at a glance

The seven testing layers, the question each answers, the exit condition that ends it, and the defect class it structurally cannot find.
LayerQuestionExit conditionBlind to
1. Claim inventoryWhat is being asserted, and in whose words?Every claim has an observable and a thresholdBehaviour nobody thought to claim or ask about
2. Configuration reviewDoes the effective config match the intended one?Every setting is accounted for and its unit is confirmedRuntime behaviour that ignores the setting
3. SimulationWould this transaction succeed against current state?No simulation error, compute within the requested limitAnything that changes between simulating and landing
4. Test clusterDoes the whole flow complete end to end?Full run with no manual intervention, twiceReal liquidity, real competition, real congestion
5. Mainnet rehearsalWhat happens at a size you can afford to lose?Observed results match criteria, evidence recordedBehaviour at much larger size or duration
6. Fault injectionWhat does it do when a dependency fails?Every injected fault produces a defined, logged outcomeFaults you did not think to inject
7. Evidence and auditCan you reconstruct the run afterwards?Every action maps to a signature or a logged reasonWhether the reconstruction is honest

Layer 1: claim inventory

Start by writing down what is being claimed, quoting the source. Claims come from three places: the product page, the operator, and your own assumptions. The third category is the dangerous one, because assumptions are never written down and therefore never tested. If you believe the bot retries a failed swap, that belief is a claim and it belongs in the list.

Each claim then gets converted into a criterion with an observable and a threshold. "Fast execution" is not testable. "The median time from decision to a confirmed signature is under a stated number of seconds, measured over at least fifty attempts on mainnet" is testable, and it is testable by someone who is not you. That conversion is the entire job of the layer.

The output is a numbered list of criteria, each with a verdict column left empty. Nothing is marked pass at this stage. A criterion you cannot express as an observation gets marked untestable and stays on the list, because an untestable claim is itself a finding worth carrying into a purchasing or deployment decision.

Hosted products make this layer easier rather than harder, because the claims are already written down and dated on a page you can archive. A professional Solana volume bot published as a product states its venue coverage, its fee model and its operating limits in public, and each of those sentences converts into a criterion you can later check against a signature on chain rather than against a memory of what you were told.

Layer 2: configuration review

Read the effective configuration, not the one you meant to write. Most trading automation carries a dozen settings where a units mistake changes behaviour by three orders of magnitude, and the classic example on Solana is the compute unit price, which is denominated in micro-lamports per compute unit rather than lamports.

Work through each setting and record three things: the value, the unit, and what would happen if it were ten times larger. That last question is the one that catches unit errors, because a setting whose tenfold value is obviously absurd is a setting you have understood. Slippage tolerance, priority fee, retry count, wallet count and per-transaction size all belong on this list.

Selection between competing tools is a configuration question before it is a preference question, since two products that appear similar often expose entirely different control surfaces. If you are still choosing, the comparison worth making is which settings each one lets you observe and change, and reading how a best Solana volume bot shortlist is normally assembled is a faster way to see which controls the category considers standard than reading four product pages in sequence.

Layer 3: simulation

Solana lets you ask a validator what would happen if a transaction were processed, without processing it. The simulateTransaction RPC method returns the program logs, an error if the transaction would fail, the compute units it would consume, and optionally the post-execution state of accounts you name. It is the cheapest layer in the plan and the most underused.

Three checks belong here. First, does the transaction succeed at all against current state. Second, does the reported compute consumption fit inside the limit you requested, since a transaction that exceeds its compute budget fails at execution. Third, do the logs show the programs you expected in the order you expected, which is how you catch a route that silently changed.

Simulation runs against the state at a recent slot, so its guarantee is narrow: this transaction would have worked a moment ago. Between the simulation and the send, the pool can move, the account can be closed and the blockhash can expire. Treat a clean simulation as a necessary condition rather than a prediction, and never treat a failing simulation as noise.

Layer 4: test cluster

Devnet exists so you can run the whole flow repeatedly without spending anything. What it proves is that the code path completes: accounts get created, instructions get built correctly, signatures get produced, retries fire, and the state machine advances from start to finish without a human intervening. That is a genuine and necessary result.

What it cannot prove is anything about the market, because there is no market. Devnet liquidity is whatever someone deployed for testing, competing traders do not exist, and congestion is not comparable. Run the layer to prove correctness of the flow, then stop drawing conclusions the cluster cannot support. The devnet and simulation checklist works through the boundary in detail.

Set the exit condition at two consecutive clean runs rather than one. A single clean run on a test cluster is compatible with a race condition that resolved favourably by chance, and the second run costs almost nothing. If the two runs disagree, you have found a nondeterminism, which is a defect worth chasing before any real funds are involved.

Layer 5: sized mainnet rehearsal

The rehearsal is where the plan stops being free. You run the real flow on mainnet, with real funds, at a size chosen so the worst plausible outcome is an amount you have already written off. The point is not to make money and not to prove the strategy; it is to observe execution against real liquidity, real fees and real competition for block space.

Everything that happens gets recorded at the time, not reconstructed later. Signature, slot, expected result, observed result, verdict. The habit that makes rehearsals useful is writing the expected result before the transaction is sent, because after the fact everyone remembers having expected whatever happened.

Size is the whole design problem, and it deserves its own sheet rather than a guess. The dry run design sheet covers how to pick the number, what to record while it runs, and the abort conditions that have to be written before the first transaction is signed.

Layer 6: fault injection

Normal-path behaviour is the easy half. The interesting behaviour is what the software does when something it depends on does not answer, and you find that out by breaking dependencies on purpose in a controlled way rather than waiting for the network to do it for you at an inconvenient moment.

  1. Point it at a dead endpoint. Change the RPC URL to one that does not respond. Expected: a clear error, no signed transaction, no silent retry loop that spends fees.
  2. Point it at a rate-limited endpoint. Public endpoints return HTTP 429 under load. Expected: backoff, a bounded retry count, and an operator-visible message rather than a stall.
  3. Send with an expired blockhash. A blockhash is valid for a limited number of blocks and then the transaction is rejected. Expected: refresh and rebuild, not an infinite resubmit of a dead transaction.
  4. Underfund a wallet. Leave it short of the amount plus fees plus any rent for accounts that must be created. Expected: a specific insufficient-funds error surfaced before signing, not a partially completed sequence.
  5. Set an impossible slippage limit. Ask for a tolerance the market cannot satisfy. Expected: the swap program returns its slippage error and the tool reports which program rejected it.
  6. Kill the process mid-sequence. Terminate during a multi-step flow. Expected: on restart, the tool determines what already landed rather than repeating steps blindly.

The last one is the most valuable and the least tested. Solana signatures are deterministic for a given transaction, so resubmitting the identical transaction is safe, but rebuilding it with a fresh blockhash produces a new signature and a second execution. A tool that restarts by rebuilding rather than by checking what landed can pay twice, and only a deliberate kill test will show you which behaviour you have.

Layer 7: evidence and audit

The final layer asks whether the run can be reconstructed by someone who was not watching. Every action the tool took should map to either a transaction signature you can look up on a public explorer, or a logged reason for not acting. Gaps between those two categories are where post-incident arguments come from.

Test this deliberately: take a run that already happened, hand the record to someone else, and ask them to answer three questions. What did it do, when, and why. If they cannot answer from the record alone, the observability is insufficient, and that is a defect against the tool rather than against the person reading it.

This layer is also where you check that the record is durable. Logs that live only in a terminal buffer are not evidence, because the buffer is gone by the time you need it. Written to a file, timestamped in UTC, and including the signature of every transaction attempted, whether it landed or not.

A worked budget for a rehearsal

The following arithmetic is illustrative and uses protocol constants rather than results from any real run. Suppose a rehearsal will attempt twenty swaps. Solana charges a base fee of 5,000 lamports per signature, and a priority fee equal to the compute unit price multiplied by the compute unit limit, where price is quoted in micro-lamports per compute unit.

With one signature per transaction, a compute unit limit of 200,000 and a compute unit price of 50,000 micro-lamports, the priority fee per transaction is 200,000 multiplied by 50,000 micro-lamports, which is 10,000,000,000 micro-lamports, which is 10,000 lamports. Adding the base fee gives 15,000 lamports per transaction, or 0.000015 SOL.

Twenty transactions therefore cost 300,000 lamports, or 0.0003 SOL, in fees alone. Separately, creating an associated token account that does not yet exist requires a rent-exempt deposit of 0.00203928 SOL, which is recoverable if the account is later closed. Fees are not recoverable, so the rehearsal budget is the sum of the fee estimate, the rent deposits, and the trade size you have decided to write off.

Why the fee arithmetic belongs in the test plan

Two of the most common configuration defects in this category are a compute unit price entered in the wrong unit and a compute unit limit set far above what the transaction consumes. Both are invisible in a functional test, because the transaction still succeeds. They show up only when you compute the expected fee before the run and compare it against the fee actually charged, which is a comparison worth making on the first ten transactions of any rehearsal.

What to cut when time is short

Nobody runs all seven layers on every change, so decide in advance which are mandatory. The defensible minimum is layers one, three and seven: know what you are asserting, simulate before you send, and be able to reconstruct what happened. Those three are cheap and they preserve your ability to investigate anything the other layers would have caught.

The layer most often cut and least safely cut is six. Fault injection is the only layer that tests behaviour under conditions you cannot schedule, and skipping it means the first congested block, the first rate-limited endpoint and the first expired blockhash all get discovered while real funds are in flight. It is also the fastest layer to run once the harness exists.

A defensible reduction of the plan by change type, with the layers that stay mandatory in each case.
Change typeMandatory layersRationale
New tool, never run before1 through 7No prior evidence exists about anything
Configuration change only2, 3, 7Code path unchanged; the risk is in the values
Version upgrade of the tool1, 3, 4, 6, 7Claims may have changed; behaviour under fault may have too
Same tool, new venue or pool3, 5, 7Liquidity conditions differ; the code does not
Fix for a defect you filed3, 6, 7 plus the regression matrixProve the fix, then prove nothing else broke

What the plan cannot tell you

It cannot tell you whether a strategy makes money. Testing establishes that a system does what it was specified to do; whether the specification is a good idea is a separate argument, and conflating the two is how a well-tested bot ends up faithfully executing a losing plan.

It cannot tell you the software is safe. A passing suite says that on that build, with that configuration, on that day, the observed results matched the expected ones. It says nothing about the next build, about a code path nobody exercised, or about an operator with access you did not enumerate. Anyone who tells you testing guarantees safety is selling something.

It cannot tell you about scale you did not test. Behaviour at twenty transactions and behaviour at two thousand differ in ways that are not linear: rate limits, account rent, wallet management overhead and competition for block space all change shape. The honest statement at the end of a rehearsal is the size it covered, and that number belongs in the sign-off.

Finally, it cannot tell you whether the operator will follow the plan next time. That is a process question rather than a technical one, and the only mitigation this desk knows of is writing the verdict down in a place other people read, with the build, the configuration and the wallet set named beside it.

Questions the desk gets asked

How do you test a trading bot you did not write?

You test it as a black box against observable output. Write acceptance criteria in terms of things you can see from outside: transactions that landed, signatures you can look up, fees actually charged, venues actually touched, and the record the tool leaves behind. Source access changes which layers are cheap, not whether the system is testable.

Is paper trading enough before going live?

No, and the reason is specific rather than philosophical. Paper trading exercises the decision logic and skips the execution layer entirely, so it cannot surface an expired blockhash, a rate-limited endpoint, a failed account creation or an insufficient compute limit. It is a useful test of one component and it proves nothing about the parts that touch the network.

How long should a dry run last?

Long enough to cross at least one period of the conditions you are worried about, which usually means it is defined by events rather than by hours. A rehearsal that never met congestion has not tested behaviour under congestion, no matter how many days it ran. Write the event you need to observe, then run until you observe it or until the abort condition fires.

What counts as a defect rather than a preference?

A defect is a difference between an expected result you wrote down before the run and the result you observed. If you did not write the expectation first, what you have is a surprise, and surprises get converted into criteria for the next round rather than filed as defects. This is the main discipline the whole plan rests on.

Do you need devnet if you can simulate transactions?

They answer different questions. Simulation checks whether a specific transaction would succeed against current state on the cluster you are pointing at. A test cluster lets you run the whole flow repeatedly, including account creation, retries and state that persists between steps, without spending real funds. Most teams need both and skip the second more often.

Who signs off on a test plan like this?

Whoever carries the loss if it is wrong. In practice that means the person funding the wallets, and the useful discipline is that the sign-off is a sentence naming the build, the configuration and the wallet set that was tested, because a verdict without those three is not attached to anything.

What should be retested after a fix?

The failing case, the cases sharing the same code path, and anything the fix changed a shared assumption for. The last group is the one teams forget, and it is where regressions live. A written regression matrix is the only reliable way to answer this question the same way twice.

Filed under Before you trust it by The QA Ground Desk. Behaviour described here comes from protocol documentation and from procedures the desk can run itself; any figure in an example is labelled as illustrative arithmetic and describes no real account. How the desk decides what to publish is set out in the method note.