Smart Contract Security in Practice
Lessons from Building on Solana with Anchor
Solana has become the go-to ecosystem for high-performance on-chain applications - from liquidity protocols to NFT marketplaces and advanced token mechanics. But building secure programs on Solana isn’t trivial. With a massively parallel runtime, shared global state, and strict account rules, security is a discipline, not just a checklist.
At KROK, we’ve delivered numerous Solana-based products - complex token systems, liquidity market makers, multi-account state machines, marketplaces, gaming logic, and more. Over time, our team developed a practical, repeatable security workflow that has helped us ship safely and confidently.
This article summarizes some rules we use on every production Solana project. Semi-technical, battle-tested, and grounded in experience.
1. Security Starts Before Code: Spec + Pre-Audit
Most Solana exploits are architectural. Not a Rust bug. Not a missing check. But incomplete thinking at the specification stage. Before development begins, every KROK project includes:
1.1 Detailed Technical Specification
Covering:
PDA derivation + namespace design
Account models, sizes, mutability rules
Access control & roles
State machine diagrams
Invariants (“this value can never decrease”, etc.)
Gas/compute considerations
This ensures everyone understands how the system must behave before anyone writes an instruction handler.
1.2 Pre-Audit Threat Analysis
Before coding, we map:
possible attack surfaces
privilege escalation paths
reinitialization vectors
PDA collisions
state desynchronization risks
Thinking like an attacker early can help reduce some potential future issues.
2. Getting Account Handling Right (The #1 Source of Bugs)
Solana is unique: accounts are the program’s entire world. Which means most vulnerabilities come from improper account usage. Some of the practices we enforce internally at KROK:
2.1. Pay Special Attention to Accounts Passed Into Instructions
Every instruction can be called by anyone, with any accounts, in any order. This is why account validation is the most important part of every handler.
Anchor helps with:
correct deserialization
correct owner checks
preventing account substitution
signer validation
ensuring writable vs. read-only contracts
But Anchor constraints don’t cover everything, so we always reinforce them with logic checks.
Example:
#[account(
mut,
has_one = authority,
seeds = [b"vault", authority.key().as_ref()],
bump
)]
pub vault: Account<'info, Vault>;
And then:
let (expected, _) = Pubkey::find_program_address(
&[b"vault", authority.key().as_ref()],
ctx.program_id,
);
require!(expected == vault.key(), CustomError::InvalidVaultPDA);
Rule:
Always assume the caller is malicious. Never trust the accounts they pass you.
3. Use Safe Math — Always
Solana programs operate on:
fixed-point decimals
big integers
token amounts
fee multipliers
liquidity math
bit shifts and ratios
A single overflow can break your invariant and freeze your protocol. We enforce use of:
checked_add
checked_sub
checked_mul
checked_div
checked_shl
Example:
let new_balance = vault
.balance
.checked_sub(amount)
.ok_or(CustomError::Underflow)?;
Why this matters:
Arithmetic safety is one of the easiest ways to prevent catastrophic logic breaks - especially in DeFi-like programs.
4. Minimize Serialization & Deserialization Risk
Incorrect serialization is a silent killer in Solana development. To prevent this:
we rely entirely on Anchor for serialization/deserialization
we avoid manual byte manipulation unless absolutely necessary
we prioritize fixed-size layouts over variable ones
Dynamic layouts easily lead to:
misaligned offsets
corrupted state after upgrades
unexpected data padding
hidden attack vectors
Using stable, fixed layouts makes the entire system more predictable and auditable.
5. Validate All Inputs Extensively
Inputs are untrusted. Accounts are untrusted. Everything is untrusted.
After decoding state, the program must verify all fields:
Examples:
amounts must be positive and within expected bounds
timestamps must satisfy logical constraints
accounts must be in the correct relationship to each other
fee values must follow protocol rules
states must be valid for the current instruction
If your program processes user funds, assume every input is designed to break you.
6. Defensive Logical Design: State Machines & Rigid Flows
A defensive mindset prevents a whole category of vulnerabilities. At KROK, we use patterns such as:
State machine pattern
Each object can only be in one valid state at a time.
Example:
enum SaleState {
Created,
Active,
Completed,
Cancelled,
}
Instructions must enforce valid transitions only:
require!(sale.state == SaleState::Active, CustomError::InvalidState);
Inversion of defaults
Start with the assumption that nothing is valid until proven valid.
Zero-trust instruction design
Treat every instruction call as hostile unless verified otherwise.
7. Test Everything: Unit, Integration, Fuzzing
Our internal rule:
> 90% code coverage
100% instruction coverage
All branches tested
Negative tests mandatory
Fuzz Testing
Anchor provides fuzzing tools that help identify:
arithmetic boundary failures
unintuitive state transitions
invalid orderings of accounts
branching logic anomalies
If it touches state, it must be tested.
8. AI-Assisted Exploit Discovery
One of our internal innovations at KROK is structured AI-driven testing.
We use AI to attempt:
invalid account graphs
PDA collision attempts
reordered accounts
privilege escalation scenarios
edge-case liquidity/math attacks
AI doesn’t replace audits -it helps discover high-entropy attack attempts humans wouldn’t think of.
9. External Audit Before Any Mainnet Launch
Every contract undergoes a fresh external audit done by 3rd part company not included into development process..
Auditors check:
spec + implementation mismatch
invalid or missing invariants
account substitution vectors
access control & signer logic
financial attack surfaces
PDA namespace collisions
rent/space inconsistencies
upgradeability risks
Only after all findings are fixed do we deploy. Even simple projects get this treatment - because attackers don’t care about scope.
10. A Continuous Security Mindset
Security isn’t a stage. It’s a philosophy.
On Solana - a high-performance, low-latency chain - even a small oversight can lead to a catastrophic exploit. A safe program requires:
Secure design
Secure coding
Aggressive testing
Independent auditing
Continuous monitoring
This mindset has allowed us at KROK to build and maintain multiple Solana production systems with strong reliability and zero critical incidents.
Whether you're building a token system, liquidity engine, NFT infrastructure, or any other on-chain logic, our team at KROK brings deep Solana/Anchor experience, strict internal processes, and production-tested engineering practices.