ChainScore Labs
All Guides

How Smart Contract Audits Are Performed

LABS

How Smart Contract Audits Are Performed

Chainscore © 2025

Core Audit Methodologies

A systematic breakdown of the primary techniques used by security professionals to analyze and verify the safety of smart contract code.

Manual Code Review

Line-by-line analysis where auditors manually inspect the source code for logic flaws and vulnerabilities. This involves tracing function calls, checking access controls, and understanding the contract's business logic in depth. It is essential for catching subtle bugs that automated tools miss, such as complex reentrancy or flawed economic incentives, forming the foundation of a thorough audit.

Static Analysis

Automated scanning of source code or bytecode without executing it, using tools like Slither or Mythril. It identifies common vulnerability patterns, such as integer overflows or unchecked return values, across the entire codebase. This provides broad, fast coverage and is crucial for establishing a baseline of security issues before deeper manual review begins.

Dynamic Analysis & Fuzzing

Execution-based testing where the contract is run with a vast number of random or structured inputs (fuzzing) to uncover edge-case failures. Tools like Echidna or Foundry's fuzzer simulate transactions to test invariants. This methodology is critical for discovering unexpected state transitions and input validation errors that static analysis cannot predict.

Formal Verification

Mathematical proof that a smart contract's code satisfies a formal specification of its intended behavior. Using tools like Certora Prover or K-Framework, auditors model the contract as a mathematical system. This provides the highest level of assurance for critical properties, such as "tokens are never created out of thin air," making it vital for high-value DeFi protocols.

Architecture Review

High-level assessment of the system's design, data flows, and integration points before diving into code. Auditors evaluate the upgradeability mechanism, admin privileges, oracle dependencies, and economic model. This identifies systemic risks, like centralization vectors or improper incentive alignment, ensuring the foundational design is sound and secure.

The Standard Audit Workflow

Process overview

1

Engagement and Scoping

Define the audit's objectives, scope, and timeline with the client.

Detailed Instructions

This initial phase establishes the audit's foundation. The auditor and client agree on the specific contracts to be reviewed, the depth of analysis (e.g., automated tools only vs. full manual review), and the project timeline. A formal scope document is created, detailing the repository commit hash (e.g., 0xabc123...), the specific Solidity files, and any excluded components like legacy or forked libraries.

  • Sub-step 1: Client provides the repository link and target commit hash for the audit.
  • Sub-step 2: Auditor reviews the codebase size and complexity to estimate effort.
  • Sub-step 3: Both parties agree on a testing environment, such as forking mainnet at block 18,500,000 or using a custom Hardhat configuration.

Tip: Clearly document any out-of-scope elements, like admin multisig contracts, to prevent scope creep later.

2

Automated Analysis and Tooling

Run static and dynamic analysis tools to identify common vulnerabilities.

Detailed Instructions

Auditors employ a suite of automated security tools to perform an initial, broad sweep of the code. This includes static analysis with Slither or Mythril to detect common patterns like reentrancy, and formal verification tools for mathematical proof of certain properties. Dynamic analysis involves running the test suite and fuzzing with Echidna or Foundry's forge fuzz, specifying invariant tests like totalSupply() never decreasing.

  • Sub-step 1: Execute Slither with custom detectors: slither . --detect reentrancy-eth,unchecked-transfer.
  • Sub-step 2: Run a Foundry fuzzer for 100,000 runs on a core swap function.
  • Sub-step 3: Analyze tool outputs, triaging false positives (e.g., benign unused return values) from true issues.
solidity
// Example invariant test in Foundry function test_supply_invariant() public { assertEq(vault.totalSupply(), vault.totalAssets()); }

Tip: Automated tools are a starting point; they cannot reason about business logic flaws, which require manual review.

3

Manual Code Review

Auditors manually inspect logic, architecture, and compliance with specifications.

Detailed Instructions

The core of the audit is the line-by-line manual review. Auditors examine the code's architecture, data flow, and adherence to the intended specification or whitepaper. They focus on business logic errors, privilege escalation, economic incentives, and integration risks. This involves tracing function calls, checking access controls (e.g., onlyOwner modifiers), and verifying that state changes are handled correctly under all conditions, including edge cases like zero-value transfers or maximum uint256 values.

  • Sub-step 1: Map out all state variables and their mutability, noting which functions are public/external.
  • Sub-step 2: Review mathematical operations for overflow/underflow and precision loss, even with Solidity 0.8.x.
  • Sub-step 3: Analyze external calls to other contracts (e.g., Oracle feeds, LP tokens) for trust assumptions.

Tip: Create a mental or documented model of the contract's intended state machine and challenge it with unexpected user flows.

4

Reporting and Remediation

Document findings, assign severity, and work with the client on fixes.

Detailed Instructions

Findings are compiled into a detailed audit report. Each issue is categorized by severity level (Critical, High, Medium, Low, Informational) following a standardized framework like the DASP Top 10 or SWC Registry. The report includes a clear description, code snippets with line numbers, a proof-of-concept exploit where possible, and a recommended fix. The client then addresses the issues, and the auditor performs a remediation review on the updated code (e.g., commit 0xdef456...) to verify the fixes are correct and do not introduce new vulnerabilities.

  • Sub-step 1: For a Critical reentrancy bug, provide a PoC showing how to drain the contract.
  • Sub-step 2: Client deploys fixes and provides a diff for the auditor's review.
  • Sub-step 3: Auditor re-runs targeted tests and manual review on the patched functions.
solidity
// Example of a mitigated reentrancy guard function withdraw() external nonReentrant { // Using OpenZeppelin's ReentrancyGuard // ... safe logic }

Tip: A good report is actionable, providing specific code changes rather than just describing the problem.

Manual vs. Automated Analysis

Understanding the Two Approaches

Smart contract audits use two main methods to find bugs. Manual analysis involves human experts reading the code line-by-line to find complex logic errors and business logic flaws. Automated analysis uses specialized software tools to scan code for known patterns of vulnerabilities, like reentrancy or integer overflows.

Key Differences

  • Depth vs. Speed: Manual review is slower but finds subtle, unique issues. Automated tools are fast but can only detect predefined vulnerability patterns.
  • Cost and Scalability: Automated scanning is cheaper and scales easily for initial checks. Manual review is more expensive but is essential for high-value contracts.
  • Context Awareness: Human auditors understand the contract's intended purpose, catching errors in business logic that a tool would miss.

Real-World Analogy

Think of building a house. An automated tool is like a checklist inspector verifying the wiring meets code. A manual auditor is the architect walking through, noticing if the room layout is impractical or unsafe in a way the checklist didn't cover.

Common Vulnerability Categories

Comparison of major vulnerability types identified during smart contract audits.

Vulnerability CategoryTypical SeverityCommon Detection MethodExample Impact

Reentrancy

Critical

Static Analysis, Manual Review

Drainage of contract funds

Access Control Flaws

High

Manual Review, Symbolic Execution

Unauthorized privileged actions

Arithmetic Issues

High

Static Analysis, Fuzzing

Integer overflow/underflow leading to incorrect balances

Logic Errors

Medium/High

Manual Review, Formal Verification

Incorrect state transitions or business logic

Oracle Manipulation

High

Manual Review, Scenario Testing

Price feed manipulation for unfair liquidations

Front-Running

Medium

Transaction Analysis, MEV Simulation

Value extraction by inserting transactions

Gas Limit Issues

Low/Medium

Gas Profiling, Load Testing

Transaction failure due to out-of-gas errors

Post-Audit Remediation and Verification

Process for addressing audit findings and confirming their resolution before deployment.

1

Prioritize and Triage Findings

Categorize audit report issues by severity and plan remediation.

Detailed Instructions

Review the audit report and classify each finding based on its severity level (Critical, High, Medium, Low, Informational). Create a remediation plan that addresses Critical and High-severity issues first, as they pose the greatest risk to user funds or contract logic. For each finding, assign a developer and set a target resolution date.

  • Sub-step 1: Map each finding to its corresponding function and line numbers in your codebase.
  • Sub-step 2: Assess the complexity and potential side effects of each fix. Some fixes may require architectural changes.
  • Sub-step 3: Log all decisions in a tracking document (e.g., spreadsheet or project management tool) to maintain accountability.
solidity
// Example: A Critical reentrancy finding in a withdrawal function function withdraw() public { // Vulnerable original code (bool success, ) = msg.sender.call{value: balances[msg.sender]}(""); require(success, "Transfer failed"); balances[msg.sender] = 0; // State update AFTER external call }

Tip: Engage the auditing team to clarify any ambiguous findings before beginning implementation to avoid misinterpretation.

2

Implement Code Fixes

Modify the smart contract code to resolve the identified vulnerabilities.

Detailed Instructions

Apply the specific code changes required to mitigate each vulnerability. This often involves implementing established security patterns. For reentrancy, use the Checks-Effects-Interactions pattern or ReentrancyGuard. For access control issues, implement explicit role checks with modifiers. For integer overflows/underflows, use SafeMath libraries or Solidity 0.8.x's built-in checks.

  • Sub-step 1: Apply the fix in a dedicated feature branch, making minimal, focused changes.
  • Sub-step 2: Add comprehensive NatSpec comments explaining the security fix and referencing the audit finding ID.
  • Sub-step 3: Ensure the fix does not break existing contract functionality or introduce new gas inefficiencies.
solidity
// Example: Fixed withdrawal function using Checks-Effects-Interactions function withdraw() public nonReentrant { // Added modifier uint256 amount = balances[msg.sender]; require(amount > 0, "No balance"); // Effects: Update state BEFORE external call balances[msg.sender] = 0; // Interactions: Perform the call last (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); }

Tip: Use git diff to generate a patch file of all changes for easy review by auditors during verification.

3

Update and Run Test Suite

Expand unit and integration tests to validate fixes and prevent regressions.

Detailed Instructions

After implementing fixes, you must update your test suite. Write new tests that specifically target the patched vulnerabilities to prove they are resolved. Also, run the full existing test suite to ensure no regressions were introduced. Use a framework like Foundry or Hardhat with high coverage goals.

  • Sub-step 1: Create a test for each audit finding that simulates the exploit path and asserts it now fails.
  • Sub-step 2: Run fuzz tests (e.g., with Foundry's forge fuzz) on modified functions to uncover edge cases.
  • Sub-step 3: Execute invariant tests to ensure core contract properties (e.g., "total supply is constant") still hold.
solidity
// Foundry test example for the fixed reentrancy vulnerability function test_ReentrancyAttackFails() public { AttackerContract attacker = new AttackerContract(address(vault)); vm.deal(address(attacker), 1 ether); attacker.deposit{value: 1 ether}(); // The attack function attempts a reentrant call during withdrawal vm.expectRevert(); // Expect the transaction to revert attacker.attack(); // Assert the attacker did not drain extra funds assertEq(address(vault).balance, 1 ether); }

Tip: Aim for 100% branch coverage on the modified code paths. Use coverage reports to identify untested lines.

4

Request Verification from Auditors

Submit the remediated code for the auditing firm's final review.

Detailed Instructions

Formally submit the patched codebase to the original auditing team for verification. The deliverable should include the updated source code, the diff/patch file, the updated test suite, and a detailed document mapping each audit finding to its resolution. The auditors will perform a focused review to confirm fixes are correct and complete.

  • Sub-step 1: Provide clear access to the code repository (e.g., a specific git tag or branch) and deployment artifacts.
  • Sub-step 2: Include a summary of all changes made and any decisions not to fix certain findings (e.g., acknowledged informational issues).
  • Sub-step 3: The auditors will re-run their static analysis, manual review, and possibly custom exploit scripts against the new code.
bash
# Example command auditors might run for differential analysis # Compare the pre-audit and post-remediation bytecode diff <(solc --bin OldContract.sol) <(solc --bin NewContract.sol)

Tip: A successful verification typically results in an updated audit report or a formal letter stating all critical/high issues are resolved, which is crucial for community trust.

5

Deploy and Monitor

Safely deploy the audited contracts and establish ongoing monitoring.

Detailed Instructions

Once verification is complete, proceed with deployment. Use a staged rollout if possible, deploying to a testnet first for final community testing. For mainnet deployment, employ a multisig wallet or timelock controller for the deployer address. After deployment, implement monitoring and incident response plans.

  • Sub-step 1: Deploy the verified contracts to a testnet (e.g., Sepolia) and run a final suite of on-chain integration tests.
  • Sub-step 2: For mainnet, execute the deployment transaction from a multisig wallet requiring multiple signatures.
  • Sub-step 3: Set up monitoring tools (e.g., Tenderly, OpenZeppelin Defender) to alert on unusual transactions, function failures, or gas usage spikes.
javascript
// Example: Hardhat deployment script using a multisig executor async function main() { const MyContract = await ethers.getContractFactory("MyContract"); // The 'multisig' signer is configured in hardhat.config.js const contract = await MyContract.connect(multisig).deploy(); await contract.deployed(); console.log("Contract deployed to:", contract.address); // Propose ownership transfer to a timelock contract await contract.proposeTransferOwnership(timelockAddress); }

Tip: Consider engaging a bug bounty program (e.g., on Immunefi) post-deployment as an additional continuous security layer.

Key Audit Deliverables

The final audit report is a comprehensive package of documents and data. It provides a detailed technical assessment, a clear roadmap for remediation, and formal verification of the security posture for stakeholders and users.

Detailed Audit Report

The final report is the core deliverable, documenting all findings.

  • Vulnerability Descriptions with severity ratings (Critical, High, Medium, Low).
  • Proof of Concept (PoC) code snippets demonstrating exploitability.
  • Code Snippets referencing specific lines in the source code.
  • This matters as it provides developers with an actionable, technical blueprint for fixing security flaws.

Executive Summary

A high-level summary document for non-technical stakeholders like project leads and investors.

  • Risk Overview summarizing the overall security posture.
  • Key Findings highlighting the most critical vulnerabilities.
  • Remediation Status indicating which issues were fixed during the audit.
  • This matters as it translates technical risks into business and investment decisions.

Remediation Review

A follow-up verification report after the development team addresses the initial findings.

  • Re-audit of the patched code to confirm fixes are correct and complete.
  • Analysis for potential new issues introduced by the fixes.
  • Closure of previously reported vulnerabilities.
  • This matters for users as it provides assurance that identified risks have been properly mitigated before deployment.

Formal Verification Report

An advanced, mathematical proof of correctness for critical contract properties, often using tools like Certora or K-Framework.

  • Mathematical Specifications defining intended behaviors (e.g., "no loss of funds").
  • Proof that the code logically adheres to these specifications under all conditions.
  • Assumptions and Limitations of the verification model.
  • This matters for high-value DeFi protocols where logical bugs can lead to catastrophic financial loss.

Threat Model & Assumptions

A document outlining the security context and scope of the audit.

  • Trust Boundaries defining what actors and components are considered trusted or adversarial.
  • System Architecture diagrams and data flow analysis.
  • Explicit Assumptions about the operational environment and external dependencies.
  • This matters as it sets the foundation for the audit's focus and clarifies what risks were and were not assessed.
SECTION-FAQ

Frequently Asked Questions

Ready to Start Building?

Let's bring your Web3 vision to life.

From concept to deployment, ChainScore helps you architect, build, and scale secure blockchain solutions.