Post-Audit Remediation: Fixing Solana Findings the Right Way
A field guide to the Solana audit remediation process: fix, verify, re-audit, and how to negotiate cost and timeline for closing findings.
Why remediation, not the audit itself, is where programs actually get secured
An audit report is a snapshot. It tells you what was wrong on the commit hash the auditor reviewed. The program that actually ships is whatever comes after the team patches those findings — and that patched version is, by definition, code the auditor never looked at. If nobody re-verifies it, you've paid for a security review of software that doesn't exist anymore. That gap is where a surprising number of "audited" Solana programs still get drained.
The fix here isn't complicated in theory: fix the finding, prove the fix works, get someone independent to confirm it. In practice, teams blow through this in a weekend sprint and call it done. A proper remediation cycle takes 1-3 weeks for a mid-sized program and involves real back-and-forth with the audit firm, not a single "we fixed everything, please confirm" message.
What a findings report actually hands you
A typical Solana program audit — an AMM, a lending pool, a staking vault — comes back with somewhere between 12 and 30 findings. Most engagements bucket them Critical / High / Medium / Low / Informational. The distribution matters more than the total count: a report with 25 informational nits and zero critical findings is a very different remediation job than one with 3 criticals and 20 low-severity items.
Common critical/high findings on Anchor programs, in rough order of frequency:
- Missing signer or owner checks on accounts passed into an instruction
- PDA seed collisions that let an attacker substitute a different account of the same type
- Integer overflow in fee or interest calculations not caught by
checked_*arithmetic - Reentrancy through CPI callbacks that mutate state after an external call
- Oracle price data consumed without staleness or confidence-interval checks
If any of this sounds abstract, the missing-signer-check class is worth reading in detail — it's the single most common way a Solana program gets silently drained, and we've written a full breakdown of the missing signer and owner check vulnerability with real exploit paths.
Step 1: triage before you touch code
Not every finding gets fixed the same way, and not every finding gets fixed at all. Some Medium/Low findings are accepted risk — a gas-optimization suggestion that would require an architecture change nobody wants to make six weeks before mainnet. Write down which findings you're fixing, which you're accepting, and why, in a document the auditor signs off on. This "remediation plan" is what turns a findings list into an audit trail you can show investors or a listing exchange later.
Triage order should follow blast radius, not report order. A Critical that requires a privileged multisig key already leaked is arguably lower urgency than a High that's exploitable by any wallet holding the token. Good auditors will help you re-rank if you ask — that's a normal part of the relationship, not a favor.
Step 2: patch diffs the right way
The mistake we see constantly: a fix that closes the specific exploit path the auditor demonstrated but leaves the underlying pattern open elsewhere in the codebase. If the finding is "missing signer check on withdraw," check every other instruction handler for the same class of bug before you submit for re-verification. Auditors will grep for the pattern too, and finding it again in round two burns a cycle.
A real example. Say the finding was: withdraw accepts a user_token_account without verifying its owner matches the signer, letting anyone drain a vault by passing their own token account alongside someone else's authority.
// Before — no ownership binding between signer and the account being debited
#[derive(Accounts)]
pub struct Withdraw<'info> {
pub authority: Signer<'info>,
#[account(mut)]
pub vault: Account<'info, Vault>,
#[account(mut)]
pub user_token_account: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
}
// After — Anchor constraint ties the token account's owner field to the signer
#[derive(Accounts)]
pub struct Withdraw<'info> {
pub authority: Signer<'info>,
#[account(mut, has_one = authority)]
pub vault: Account<'info, Vault>,
#[account(
mut,
constraint = user_token_account.owner == authority.key() @ VaultError::InvalidOwner
)]
pub user_token_account: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
}
The constraint line is the actual fix; the has_one on the vault closes a second instance of the same bug class the team hadn't reported because it wasn't in the finding text. That second fix is exactly the kind of thing that turns a two-round re-audit into a one-round re-audit.
Every patch needs a regression test that fails against the pre-patch code and passes against the post-patch code. "I fixed it, trust me" is not evidence. If your test suite doesn't already have negative-path tests (wrong signer, wrong PDA, zero-amount edge cases), this is the moment to add them — a fuzzer like Trident catches a class of these automatically and is worth running before you resubmit, not after.
Step 3: re-verification scope — and why "just check the diff" isn't enough
Re-verification is not re-reading the whole program. A responsible auditor scopes it to: the changed lines, any function that calls the changed function, and a scan for the same bug pattern elsewhere in scope. That's usually 20-40% of the original engagement's line count, and it should be priced and timed accordingly — a full re-audit rate for a partial re-verification is a red flag worth pushing back on.
Get the re-verification scope in writing before the firm starts. "We will re-review findings #3, #7, #11-14, and run our automated tooling against the full diff between commit A and commit B" is a scope. "We'll take another look" is not, and it's how firms end up disagreeing later about whether something was covered.
If your program touches infrastructure beyond the on-chain code — indexers, keeper bots, oracle feeds — remediation there needs the same discipline even though it's outside the audit firm's normal scope. That's usually a separate infrastructure and data pipeline hardening pass rather than something bundled into the smart contract audit.
Negotiating cost and timeline
Re-verification is usually priced one of three ways: bundled into the original audit quote as one free re-check (common for fixed-price engagements under $25k), billed at a discounted day rate (40-60% of the original rate, since scope is narrower), or billed separately per finding for firms that only closed a small number of items. Ask which model applies before you sign the original engagement — it's a much easier conversation before you've committed than after.
Timeline pressure is the real risk. Founders under a launch deadline push auditors to compress a 5-day re-verification into 2, and that compression is exactly where a second missed finding slips through. If you're choosing a firm and cost/timeline flexibility matters to you, our guide on how to pick a Solana smart contract auditor covers what to ask about re-audit terms before you sign, and if you haven't decided whether you need a full audit at all yet, this rundown of when a pre-launch audit is actually necessary is a reasonable place to start. Programs with unusual execution patterns — MEV-sensitive arbitrage bots or latency-critical sniper bots — tend to generate more re-verification rounds because timing-dependent bugs are harder to close in one pass, so budget an extra week if that's your architecture.
What sign-off should actually look like
The deliverable at the end isn't a Slack message saying "looks good." It's a re-verification letter listing each original finding, its resolution status (Resolved / Partially Resolved / Acknowledged / Won't Fix), and the commit hash it was verified against. That commit hash is your audit's actual scope boundary — anything shipped after it is, again, unaudited code, and the cycle starts over the next time you touch the vault logic.
Treat that letter as a floor, not a ceremony. If you want a second set of eyes on the remediation before mainnet, talk to our audit team about scoping a re-verification pass.
Need a bot like this built?
We design, build and run trading bots on Solana, Hyperliquid and Polymarket.
Start a projectMore from the blog
Public vs Paid RPC: How to Run a Fair Benchmark
A fair comparison of public and paid RPC endpoints must account for quotas, workload, freshness and support guarantees.
Read articleRPC Benchmark Percentiles Explained: p50, p95 and p99
Why averages hide the latency spikes that break trading bots, wallets and production blockchain applications.
Read articleHyperliquid REST vs WebSocket Benchmark: What to Measure
A benchmark plan for Hyperliquid market data that separates request latency from streaming freshness and recovery.
Read article