I keep a folder of ten small Solidity contracts with bugs I planted myself: a classic reentrancy in a withdraw function, a missing access modifier on an initializer, an ERC4626 vault that rounds in the depositor's favor, a signature check that never validates the signer, a few subtler logic bugs. Whenever I'm deciding which local model earns a slot on my disk, I run every candidate against all ten with the same prompts and compare notes. It's not a benchmark, there's no percentage at the end, but after doing this for months the patterns are consistent enough to be worth writing down.
This round: qwen2.5-coder against deepseek-coder, both running through Ollama on WSL2, both at the sizes that actually fit comfortably on my hardware, meaning 7B and below. If you can run 33B models locally, your conclusions will differ and you should mostly stop reading benchmarks of small models anyway.
Methodology, such as it is
Same ten contracts, none over 150 lines. Same three prompts per contract: an open "review this contract for security issues", a targeted "check specifically for reentrancy and access control problems", and a structured one demanding findings as a severity-tagged list. Each combination run a few times because small models are noisy, one lucky sample proves nothing. Temperature low but not zero. I score by hand: did it find the planted bug, did it describe it correctly, and how much noise came along for the ride.
Two honest caveats. First, these contracts are small and self-contained, which flatters every model. Real review means cross-contract call graphs and 2000-line diffs, and no 7B model survives contact with that regardless of family. Second, my planted bugs skew toward canonical patterns, the kind that appear in training data constantly. Both facts mean my setup measures "useful assistant on digestible code", not "auditor replacement". That's fine, it's the only job I'd give these models anyway.
Where qwen2.5-coder wins
Instruction following. This is the biggest practical gap and it has nothing to do with security knowledge. When I demand output like:
Report findings as a list. For each:
SEVERITY: HIGH | MEDIUM | LOW
LINE: <number or range>
ISSUE: <one sentence>
Report at most 5 findings. If none, say NONE.
qwen at 7B complies almost every run. deepseek at comparable size complies most of the time but drifts more, adding preambles, ignoring the cap, or wrapping the list in commentary I have to strip. If you're piping model output into a pipeline, which I am (this comparison started as model selection for spectr-ai, my open-source auditor), format discipline is worth more than a marginally better eye for bugs, because a finding you can't parse is a finding you don't have.
Targeted checks. Ask qwen specifically about reentrancy and it stays on reentrancy. deepseek more often wanders into gas remarks and style nits mid-answer, which pollutes the signal when I asked a narrow question.
Explanation quality. When both models find the same bug, qwen's explanation more often names the actual attack sequence (call withdraw, reenter through the fallback before the balance update) while deepseek at small sizes tends toward the generic textbook sentence about external calls. Both are "correct", one is more useful in a report.
Where deepseek-coder holds its own or wins
Raw pattern recall on the classics. On the bluntest bugs, unguarded selfdestruct-style privileged functions, missing modifiers, the crude reentrancy, deepseek is every bit as reliable and occasionally phrases the risk more concretely. Its training clearly included plenty of Solidity.
Skepticism. deepseek flags slightly more, which cuts both ways. On one of my subtler contracts, a logic bug where a reward calculation uses a stale balance snapshot, deepseek raised a suspicious eyebrow at the right function more often than qwen, even though it rarely articulated the exact bug. As a "look here, human" pointer, that has real value. The cost is more noise on clean code: deepseek invented plausible-sounding problems on my one deliberately clean contract more often than qwen did.
Hallucination flavor. Both hallucinate, differently. qwen's failure mode is confident omission, a clean bill of health on buggy code. deepseek's is confident invention, citing a reentrancy that structurally cannot happen because there's no external call. I mildly prefer deepseek's failure mode for security triage (false alarms cost minutes, misses cost everything) but prefer qwen's for automation, where noise erodes trust in every alert.
Size beats family, and it's not close
The most consistent finding across every round of this exercise: the gap between 1.5B and 7B within the same family dwarfs the gap between qwen and deepseek at the same size.
qwen2.5-coder:1.5b, which I use daily and like a lot for quick classification and triage, is simply not a code reviewer. On these ten contracts it catches only the most textbook patterns, misses anything requiring two steps of reasoning, and frequently mangles the structured output format. The subtler planted bugs, the stale snapshot, the rounding direction, might as well be invisible to it. Same story for deepseek's smallest variants. Below roughly 7B, "which family" is the wrong question, both are too small for multi-step reasoning about state and control flow.
At 7B, both families cross a threshold where they reliably nail the canonical top-ten-style bugs in small contracts and sometimes surprise you on harder ones. Neither crosses the next threshold: consistent detection of business logic bugs, the ones that require understanding what the code is supposed to do rather than matching a known-dangerous shape. That's exactly where real audit findings live, which is why I keep saying these are triage assistants, not auditors.
The practical corollary: if you're choosing a local model for Solidity work, spend your effort getting the biggest model your hardware runs comfortably, and only then compare families. Family choice is a tiebreaker.
What I actually run
qwen2.5-coder:7b is my default for anything structured: review passes, classification, the pre-commit and repo-triage pipelines I've written about before. The instruction following is what earns it the slot. qwen2.5-coder:1.5b stays installed for fast, low-stakes filtering where a wrong answer costs nothing. deepseek-coder stays on disk as a second opinion: when a contract feels off and qwen shrugs, I run deepseek and read what it points at, treating it as a suspicion generator rather than a truth source. Two differently-wrong models disagreeing is often exactly where I should look harder, and both together still cost me zero dollars and zero data leaving my machine.
Would a wrong-but-suspicious model or a quiet-but-precise one fit your workflow better?
Top comments (0)