All articles
Comparisons·June 11, 2026·6 min read

Manual Audit vs Automated Scanner: What Tools Actually Catch

Manual audit vs automated scanner for smart contracts: what Slither and Echidna catch, what only a human auditor finds, and when to use each.

The Same Contract, Two Reviews

Run this staking function through Slither and it will pass clean, no warnings:

function stake(uint256 amount) external {
    token.transferFrom(msg.sender, address(this), amount);
    stakedAmount[msg.sender] += amount;
    stakeTime[msg.sender] = block.timestamp;
}

function calculateReward(address user) public view returns (uint256) {
    uint256 duration = block.timestamp - stakeTime[user];
    return (stakedAmount[user] * rewardRate * duration) / PRECISION;
}

Nothing here trips a detector. There's no reentrancy, no unchecked call, no obvious overflow. But stake() overwrites stakeTime[msg.sender] on every deposit without paying out or checkpointing the reward that already accrued. A user who stakes 100 tokens, waits 30 days, then "tops up" with 1 wei resets their clock and forfeits nothing they care about — but a user who was supposed to get slashed for early exit just laundered their reward window. Depending on how withdrawal is gated elsewhere in the contract, this is either a minor accounting bug or a direct way to double-dip rewards. A scanner has no model of what "correct" reward accrual looks like for your protocol. A person reading the spec next to the code catches it in about ninety seconds.

That's the whole story of manual audit vs automated scanner for smart contracts, compressed into one function. Tools are pattern matchers. Auditors carry a mental model of what the contract is supposed to do and check the code against that model, not against a signature database.

What Slither Actually Catches

Slither runs static analysis over the AST and control-flow graph, and it's genuinely good at what it targets: reentrancy across public/external functions, unprotected selfdestruct, missing zero-address checks, uninitialized storage pointers, incorrect ERC-20 return-value handling, shadowed state variables, and tx.origin misuse. It's fast — seconds per contract — and it's free. Any team shipping Solidity without running it first is skipping a step that costs nothing.

Take the classic reentrancy pattern:

function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount, "insufficient balance");
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "transfer failed");
    balances[msg.sender] -= amount;
}

Slither flags this instantly as a checks-effects-interactions violation — external call before state update. This is exactly the bug class detectors were built for: syntactically visible, no business context required, matches a known template from a decade of exploited contracts.

What Echidna Adds

Property-based fuzzing catches a different layer. Where Slither reads code, Echidna executes it — thousands of randomized transaction sequences against invariants you write, like "total supply never exceeds cap" or "sum of balances always equals contract balance." It'll find integer edge cases, unexpected state transitions from weird call orderings, and invariant breaks that only show up after a specific sequence of five or six calls a human wouldn't think to trace by hand. It's excellent at input-space exploration and useless if you never wrote the invariant in the first place — Echidna can't guess what your contract is supposed to guarantee.

What Only a Human Catches

The bugs that cost protocols eight figures are almost never in the reentrancy-detector category anymore. They're in the gap between "code that executes correctly" and "code that does what the business intended":

  • Access control that's syntactically fine but logically wrong. A function correctly checks onlyOwner, but the owner role can be transferred through a separate, unaudited path that bypasses timelock — nothing flags because both functions are individually correct.
  • Price/oracle assumptions baked into math. A liquidation formula that's arithmetically sound but assumes an oracle updates every block, which breaks the moment that chain has a quiet period.
  • Economic incentive misalignment. A fee-on-transfer token interacting with a vault that assumes 1:1 deposit accounting — no revert anywhere, just slow value leakage.
  • Governance and upgrade paths. A proxy pattern where the initializer can be called twice because the guard was added to the wrong contract in the inheritance chain.
  • Cross-function state assumptions. Function A assumes a state that function B can violate under specific ordering, and neither function alone looks wrong.

None of these produce a detector signature. They require reading the whitepaper or spec, understanding what the protocol promises users, and then asking "under what sequence of legitimate calls does this promise break." That's a reasoning task, not a pattern-match task, and it's why teams that want this depth typically pair automated scanning with a full smart contract audit rather than treating a clean Slither report as sign-off.

Side by Side

Bug class Slither Echidna (fuzzing) Manual review
Reentrancy, unchecked calls Catches Sometimes catches Catches
Integer edge cases, invariant breaks Misses Catches (if invariant written) Catches
Access-control logic gaps Misses Misses Catches
Business-logic / accounting exploits Misses Rarely catches Catches
Oracle & economic assumptions Misses Misses Catches
Upgrade/proxy initialization bugs Sometimes catches Misses Catches
Speed Seconds Minutes to hours Days
Cost Free / near-free Low (engineer time to write invariants) Highest

Which to Pick When

Run static analysis and fuzzing on every commit — there's no reason not to, and CI-gating a PR on a clean Slither pass is standard practice at this point. But treat that as the floor, not the ceiling. If the contract touches user funds, has an admin key, or handles anything with economic value beyond a testnet toy, budget for manual review before mainnet. A code review and audit pass on top of tooling output is where the actual risk reduction happens, especially for logic-heavy contracts like staking, lending, or anything with a reward curve.

If you're choosing between chains or frameworks and want to know how audit depth differs across ecosystems, our comparisons on FunC vs Tact security auditing and formal verification with Certora vs manual review cover where each approach's blind spots show up on EVM, TON, and Solana specifically. And if you're deciding whether to build an internal review process or bring in outside eyes, in-house review vs third-party audit for TON contracts walks through the tradeoff in more detail.

The Real Workflow

The teams that ship without incidents don't pick one approach — they layer them. Static analysis on every PR, fuzzing on state-changing functions with written invariants, and a manual pass before any mainnet deploy or upgrade. If you're unsure how much of that you actually need for your contract's risk profile, a short strategy consultation is usually faster than guessing, and it's the same triage we'd apply before scoping a bot or Solana sniper contract for adversarial environments where the attack surface is bigger than the code itself.

Tools will keep getting better at pattern matching. They still can't read your spec and ask whether the code actually does what you promised users — that part stays human. If you want that gap closed before mainnet, talk to us about a smart contract audit.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#smart contract audit#automated scanning#manual code review#solidity security#Slither