
SolidityAderynStatic AnalysisSmart Contract SecurityRust
How to Write a Detector in Aderyn Step by Step
15 de mayo de 2025•Josephine
Creating the division_before_multiplication Detector: Our Process Explained
In Solidity, a common but costly error is dividing before multiplying.
Because Solidity uses integer division by default, any decimal result gets silently truncated, often down to zero. This can introduce serious precision errors, especially in financial calculations, staking logic, and other math-heavy contracts. And the worst part? The compiler won’t throw any warnings.
That is exactly the kind of issue static analysis tools like Aderyn are built to catch.
In this guide, you’ll learn how to build a custom detector in Aderyn, a Rust-based static analyzer for Solidity. We’ll walk through everything step by step, from writing a vulnerable contract and inspecting its AST, to implementing and testing the division_before_multiplication detector in Rust, so you can follow along or use it as a base for writing your own.
The detector’s job is simple: flag any case where division happens before multiplication in a single expression, so it never slips past code review again.
Let’s get started.
What is Aderyn?
Aderyn is an open-source Rust-based static analyzer for Solidity smart contracts. It helps protocol engineers and security researchers catch bugs before they reach production. Aderyn highlights potential issues by inspecting the contract’s Abstract Syntax Tree (AST) and makes it easy to build your own custom detectors. It runs quickly from the command line and is simple to integrate into your development or auditing workflow.
Now that you know what Aderyn is, let’s get to work and build your first custom detector.
Step-by-Step Guide to Developing the division_before_multiplication
Detector
Step One: Knowing Which Vulnerability to Detect
In this section, we need to identify the vulnerability we want to detect and understand how to identify it in our AST (Abstract Syntax Tree).
An AST is a structured representation of code that compilers primarily use to read code and generate target binaries. It is often stored as JSON.
In this case, we are going to automate the detection of a mathematical vulnerability in Solidity, which involves performing divisions before multiplications. This is a significant error due to the loss of precision in mathematical operations caused by the compiler's handling of decimals in the results.
Step Two: Write Our Own Contract Containing the Vulnerability
In this step, we will create a smart contract that includes the specific vulnerability we want to detect. This will allow us to test and refine our detector. Here is an example of a Solidity contract with the division-before-multiplication issue:
1pragma solidity 0.8.20;23contract DivisionBeforeMultiplication {4 uint public result;56 function calculateWrong(uint a, uint b, uint c, uint d) external {7 result = a * d + b / c * b / d;8 }910 function calculateAlsoWrong(uint a, uint b, uint c) external {11 result = (a + b / c * b) * c;12 }1314 function calculateAl(uint a, uint b, uint c) external {15 result = (a / b * c);16 }1718 function calculateStillWrong(uint a, uint b, uint c) external {19 result = a + b / c * b * c;20 }2122 function calculateCorrect(uint a, uint b, uint c) external {23 result = a + b * b / c + b * c;24 }2526 function calculateAlsoCorrect(uint a, uint b, uint c, uint d) external {27 result = (a + ((b * d) / (c * b))) * d;28 }29}
Step Three: Understand the .json AST File Result
To begin understanding how we can extract information about the problem and write our detector to identify it, we need to analyze the .json AST file.
The AST (Abstract Syntax Tree) provides a structured representation of the Solidity code, which can be used to pinpoint where the vulnerability occurs. By examining the AST, we can determine how divisions and multiplications are represented and identify patterns that indicate the division-before-multiplication issue.
Generate the .json AST File: Use the Solidity compiler to generate the AST for your contract.
Examine the AST Structure: Open the generated DivisionBeforeMultiplication.json file and look for the nodes representing division and multiplication operations. These nodes will contain information about the operation type and the order of execution.
Identify Relevant Nodes: Look for BinaryOperation nodes in the AST. Each BinaryOperation node will have details about the operation (/ for division and * for multiplication) and the operands involved.
Determine the Pattern: Identify the pattern where a division operation is followed by a multiplication operation without proper handling to ensure precision. This pattern will be the basis for your detector.
Step Four: Writing the Detector
Let's break down the implementation of the
DivisionBeforeMultiplication
Detector step by step.- Define the Detector Structure We define a structure to hold instances of the detected issue:
1pub struct DivisionBeforeMultiplicationDetector {2 found_instances: BTreeMap<(String, usize, String), NodeID>,3}
This structure uses a
BTreeMap
to store instances of the vulnerability. The keys are tuples consisting of the source file name, line number, and a description of the issue. NodeID
represents the node in the AST where the issue was found.- Implement
theIssueDetector
Trait We implement thetheIssueDetector
trait for our detector. This trait requires adetect
method that performs the analysis.
1 fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {2 // Iterate over all binary operations in the context and filter for multiplication operations (*)3 for op in context.binary_operations().iter().filter(|op| op.operator == "*") {4 // Check if the left operand of the multiplication is a binary operation (i.e., another operation)5 if let Expression::BinaryOperation(left_op) = op.left_expression.as_ref() {6 // Check if this left operation is a division7 if left_op.operator == "/" {8 // Capture the instance of the vulnerability9 capture!(self, context, left_op)10 }11 }12 }13 // Return true if any instances were found, otherwise false14 Ok(!self.found_instances.is_empty())15 }16}
- Detailed Logic
- Iterate Over Binary Operations: We use
context.binary_operations()
to get all binary operations andfilter
to select only multiplication operations. - Check the Left Operand: This checks if the left operand of the multiplication is another binary operation.
- Identify Division Operation: We then check if this left binary operation is a division.
Capture the Instance: If both conditions are met, we capture this instance as a potential vulnerability.
Step Five: Add your Detector to the mod.rs file

Step Six: Register your detector

Step Seven: Run your custom detector locally
Code complete:
1use std::error::Error;23use crate::ast::NodeID;45use crate::capture;6use crate::detect::detector::IssueDetectorNamePool;7use crate::{8 ast::Expression,9 context::workspace_context::WorkspaceContext,10 detect::detector::{IssueDetector, IssueSeverity},11};12use eyre::Result;1314#[derive(Default)]15pub struct DivisionBeforeMultiplicationDetector {16 // Keys are source file name, line number, and description17 found_instances: BTreeMap<(String, usize, String), NodeID>,18}1920impl IssueDetector for DivisionBeforeMultiplicationDetector {21 fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {22 for op in context23 .binary_operations()24 .iter()25 .filter(|op| op.operator == "*")26 {27 if let Expression::BinaryOperation(left_op) = op.left_expression.as_ref() {28 if left_op.operator == "/" {29 capture!(self, context, left_op)30 }31 }32 }3334 Ok(!self.found_instances.is_empty())35 }3637 fn severity(&self) -> IssueSeverity {38 IssueSeverity::Low39 }4041 fn title(&self) -> String {42 String::from("Incorrect Order of Division and Multiplication")43 }4445 fn description(&self) -> String {46 String::from("Division operations followed directly by multiplication operations can lead to precision loss due to the way integer arithmetic is handled in Solidity.")47 }4849 fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {50 self.found_instances.clone()51 }5253 fn name(&self) -> String {54 format!("{}", IssueDetectorNamePool::DivisionBeforeMultiplication)55 }56}5758#[cfg(test)]59mod division_before_multiplication_detector_tests {60 use super::DivisionBeforeMultiplicationDetector;61 use crate::detect::detector::{detector_test_helpers::load_contract, IssueDetector};6263 #[test]64 fn test_template_detector() {65 let context = load_contract(66 "../tests/contract-playground/out/DivisionBeforeMultiplication.sol/DivisionBeforeMultiplication.json",67 );6869 let mut detector = DivisionBeforeMultiplicationDetector::default();70 let found = detector.detect(&context).unwrap();71 assert!(found);72 assert_eq!(detector.instances().len(), 4);73 assert_eq!(74 detector.severity(),75 crate::detect::detector::IssueSeverity::Low76 );77 assert_eq!(78 detector.title(),79 String::from("Incorrect Order of Division and Multiplication")80 );81 assert_eq!(82 detector.description(),83 String::from("Division operations followed directly by multiplication operations can lead to precision loss due to the way integer arithmetic is handled in Solidity.")84 );85 }86}