Static Analysis for Solidity with Aderyn and Semgrep
Solidity static analysis means examining your contract source code or compiled artifacts for security and correctness issues without executing any transactions. Slither is the most widely known tool in this space, but two others — Aderyn from Cyfrin and Semgrep with its Solidity ruleset — cover distinct ground that Slither can miss. Understanding what each tool does, how they differ architecturally, and how to combine them gives you a meaningfully stronger pre-audit baseline.
Why One Static Analyzer Is Not Enough
Every static analyzer embeds assumptions about which patterns are dangerous and how to detect them. Those assumptions are encoded differently depending on whether the tool parses source code into its own AST, compiles to an intermediate representation, or matches raw text patterns. A finding that one tool surfaces trivially may be invisible to another because of how its internal representation works. Running multiple analyzers in parallel — rather than sequentially hoping the first one catches everything — is the most practical way to close those gaps.
The three tools discussed here also operate at different layers of abstraction. Slither compiles your contracts with solc and analyzes a rich IR called SlithIR, which makes it excellent at data-flow and control-flow reasoning. Aderyn works directly on the compiler's AST output. Semgrep operates on source text transformed into a generic AST and is primarily a pattern matcher. Each approach has strengths and blind spots.
Aderyn: Rust-Based AST Analysis by Cyfrin
Aderyn is an open-source static analyzer written in Rust and maintained by Cyfrin. It ingests the JSON AST that the Solidity compiler emits (via solc --ast-compact-json) and traverses that tree to find known vulnerability patterns. Because it works on the compiler's own AST rather than recompiling through a custom framework, it tends to handle newer Solidity versions and unusual project layouts more gracefully than tools that ship their own compilation pipeline.
What Aderyn Detects That Slither May Not
Aderyn's detector library is intentionally focused on a narrower, high-confidence set of findings. Some categories where it adds coverage include:
- Weak PRNG patterns — use of
block.timestamporblockhashas a randomness source, even inside helper functions that Slither sometimes misses when inlining is incomplete. - Unsafe casting — downcasts from
uint256to smaller integer types without an explicit bounds check, flagged with precise AST node location. - Unindexed event parameters — events with more than three parameters where none are indexed, which is a usability and off-chain monitoring issue that sometimes has security implications in systems relying on event-driven logic.
- Public functions that could be external — reducing the attack surface and saving gas.
- Centralization risk via
Ownable— functions gated by a single owner key with no timelock or multisig, surfaced as an informational finding with enough detail to act on.
Aderyn produces a Markdown report by default, which integrates naturally into pull-request workflows via CI. Running it is straightforward once Foundry is installed:
cargo install aderyn
aderyn ./src --output report.md
The --output flag also accepts json for machine-readable results that can feed into dashboards or custom gates in your pipeline.
Extending Aderyn with Custom Detectors
Aderyn's detector trait is defined in Rust, which means writing a custom detector requires Rust knowledge. Each detector implements a visitor over AST node types. A minimal detector that flags every call to a function named selfdestruct would implement ASTConstVisitor, match on FunctionCall nodes, and check the callee name. The barrier is higher than Slither's Python detectors but the type system prevents entire classes of detector bugs.
Semgrep: Pattern Matching Across the Source Text
Semgrep is a general-purpose static analysis engine that supports dozens of languages, including Solidity. It parses source into a language-specific AST and then matches declarative YAML rules against that tree. The key distinction from Aderyn and Slither is that Semgrep rules are data, not code — you write YAML, not Python or Rust, to express a pattern.
The Solidity Rule Ecosystem
Semgrep maintains an official registry of Solidity rules at semgrep.dev/r. Categories relevant to smart contract security include:
- Reentrancy: calls to external addresses before state updates.
- Signature replay: missing nonce or chain-id checks in
ecrecoverusage. - Dangerous delegatecall:
delegatecallwith a user-controlled target. - Unchecked low-level calls:
.call{}return value not checked. - Timestamp dependence:
block.timestampused in conditional logic affecting fund transfers.
Running the official Solidity ruleset against a project looks like this:
semgrep --config p/solidity ./src
Writing Custom Semgrep Rules for Project-Specific Patterns
The real power of Semgrep in a team workflow is custom rules. Suppose your codebase uses an internal library function unsafeTransfer that should never be called in contexts where the recipient is derived from user input. You can encode that invariant as a rule:
# unsafe-transfer-user-input.yaml
rules:
- id: unsafe-transfer-user-input
patterns:
- pattern: $CONTRACT.unsafeTransfer($USER_INPUT, ...)
- pattern-not: $CONTRACT.unsafeTransfer($TRUSTED_ADDRESS, ...)
message: >-
unsafeTransfer called with a value that may originate from
user-controlled input. Verify recipient is a trusted address.
languages: [solidity]
severity: ERROR
Semgrep's metavariable system ($X, ... for variadic arguments) makes these rules concise. They run in milliseconds and can be checked into the repository alongside the contracts they govern, giving reviewers and auditors explicit documentation of project-specific invariants.
Where Semgrep Falls Short
Because Semgrep is a syntactic pattern matcher rather than a semantic analyzer, it cannot reason about data flow across function boundaries, storage slot aliasing, or integer overflow in arithmetic expressions without explicit intermediate values. For a vulnerable pattern that only manifests when a value flows through three functions before reaching a .call(), Semgrep will miss it unless the rule explicitly encodes all three steps. Slither's taint analysis handles this class of problem better. Semgrep's strength is breadth and customizability, not depth.
Comparing the Three Tools Side by Side
- Slither: Deep IR-level analysis, excellent data-flow and taint tracking, large built-in detector library, Python custom detectors. Occasionally struggles with complex Foundry or Hardhat project layouts on bleeding-edge Solidity versions.
- Aderyn: AST-based, Rust, high-confidence detectors, good version compatibility, Markdown CI reports. Smaller built-in library, custom detectors require Rust.
- Semgrep: YAML rules, fast, highly customizable, great for encoding team-specific invariants. No semantic analysis; misses cross-function data-flow issues.
Where Static Analysis Fits in the Security Workflow
Solidity static analysis is a first-pass filter, not a complete security evaluation. Running all three tools in a pre-commit or CI hook catches a large fraction of known vulnerability classes before code review happens. That shifts code review toward architecture and business logic rather than mechanical pattern checking, which is a better use of human attention.
What static analysis cannot do is find logical errors that require understanding of protocol economics, governance edge cases, or cross-contract interactions that only manifest at runtime. For those, you need symbolic execution tools like Mythril, fuzzing with Echidna or Foundry, and ultimately a manual review. The relationship between automated tools and human reviewers is complementary, not competitive — as explained in detail in Automated Scanners vs Manual Audits: What Is the Difference?.
A practical workflow looks like this:
- Run Aderyn and Semgrep in pre-commit hooks for fast feedback during development.
- Run the full tool suite including Slither, Mythril, and Echidna in CI on every pull request.
- Triage automated findings before engaging a manual auditor so the auditor's time is spent on issues automated tools cannot reach.
- Re-run after any change to security-sensitive logic.
If you want to see all three tools running against your contracts without configuring each one individually, you can run an automated scan on VRF7, which executes Slither, Aderyn, Semgrep, and four other tools in parallel and labels every finding with its source tool.
A Concrete Example: Unchecked Return Value
Consider a contract that calls an ERC-20 token's transfer function without checking the boolean return value. This is a well-known vulnerability class. Here is the vulnerable pattern and a corrected version:
// Vulnerable: return value ignored
function withdraw(address token, address to, uint256 amount) external {
IERC20(token).transfer(to, amount);
}
// Fixed: use SafeERC20 or check return value explicitly
function withdraw(address token, address to, uint256 amount) external {
bool success = IERC20(token).transfer(to, amount);
require(success, "Transfer failed");
}
Slither's unchecked-transfer detector flags this with high confidence. Aderyn flags it too. A Semgrep rule for the same pattern looks for a bare IERC20(...).transfer(...) statement not wrapped in a require or assigned to a variable. All three tools catch this; for a subtler variant buried inside an internal function, their detection rates diverge — which is exactly why using more than one matters.
Summary
Aderyn brings high-confidence, compiler-AST-level Solidity static analysis with good Solidity version compatibility and clean CI integration. Semgrep brings rapid, customizable pattern matching that lets teams codify project-specific invariants as executable rules. Neither replaces Slither for deep data-flow analysis, and none of the three replaces manual review for protocol-level logic. Used together in a layered workflow, they form a reliable and cost-effective first line of defense before code reaches a human auditor.
Scan your contract before you ship
Run an automated, transparent security scan — seven industry tools in parallel, every finding labeled with its source tool. It is not a substitute for a full manual audit, but it is a fast first line of defense.
Scan a contractFrequently asked questions
What is the difference between Aderyn and Slither for Solidity static analysis?
Slither compiles your contracts through its own pipeline and analyzes a custom intermediate representation called SlithIR, giving it deep data-flow and taint-tracking capabilities. Aderyn uses the Solidity compiler's native AST output and is written in Rust, which gives it better compatibility with newer Solidity versions and unusual project setups. Aderyn's built-in detector library is smaller but focused on high-confidence findings; Slither's library is larger and covers more vulnerability classes, especially those requiring cross-function analysis.
Can Semgrep detect reentrancy vulnerabilities in Solidity?
Semgrep can detect simple reentrancy patterns where an external call appears before a state update within the same function body, because that pattern is visible syntactically. It cannot detect reentrancy that spans multiple functions or requires tracing data flow through storage reads and writes across call boundaries. For that level of analysis, Slither's reentrancy detectors or symbolic execution with Mythril are more appropriate.
How do I write a custom Semgrep rule for a Solidity invariant?
Write a YAML file with a top-level `rules` key. Each rule needs an `id`, a `pattern` or list of `patterns`, a `message`, `languages: [solidity]`, and a `severity`. Semgrep uses metavariables like `$X` to match arbitrary expressions and `...` to match variadic arguments. Save the file and run `semgrep --config your-rule.yaml ./src`. Custom rules are best stored in your repository so they serve as living documentation of project-specific security constraints.
Is static analysis enough to secure a Solidity smart contract?
No. Static analysis catches a large fraction of known vulnerability patterns quickly and cheaply, making it a valuable first-pass filter. However, it cannot find logical errors in protocol economics, governance edge cases, complex cross-contract interactions, or vulnerabilities that only manifest under specific runtime conditions. A complete security process also includes fuzzing, symbolic execution, and manual review by experienced auditors. Static analysis reduces the noise that manual reviewers must wade through, making their time more effective.
Does Aderyn support custom detectors, and do I need to know Rust?
Yes, Aderyn supports custom detectors, and yes, writing one requires Rust. Custom detectors implement Aderyn's `ASTConstVisitor` trait and traverse compiler AST nodes. The Rust type system prevents certain classes of detector bugs that are possible in Python-based frameworks, but the barrier to entry is higher. Teams without Rust expertise are better served by Semgrep's YAML-based custom rules for project-specific patterns, reserving Aderyn for its built-in detector library.