Carriere·13 min de lecture·Par Solingo

Building Your First Smart Contract Audit Report — A Step-by-Step Guide

Learn how to structure and write professional smart contract audit reports, including severity classification, finding templates, and real examples.

# Building Your First Smart Contract Audit Report — A Step-by-Step Guide

Whether you're transitioning into security auditing or a developer preparing for an audit, understanding how to structure and write an audit report is essential. This guide walks you through creating a professional-grade audit report from scratch.

What Is an Audit Report?

A smart contract audit report is a comprehensive document that:

  • Identifies vulnerabilities in smart contract code
  • Assesses their severity and potential impact
  • Provides remediation recommendations
  • Documents the audit scope and methodology
  • Serves as a deliverable to the client and community
  • Top audit firms like Trail of Bits, OpenZeppelin, and Cyfrin publish reports that become public trust signals.

    Report Structure

    A professional audit report follows this structure:

    1. Executive Summary
    
  • Scope & Methodology
  • Severity Classification
  • Findings
  • - Critical

    - High

    - Medium

    - Low

    - Informational / Gas Optimizations

  • Recommendations
  • Appendices
  • - Tools Used

    - Test Results

    - Code Quality Assessment

    Let's build each section.

    1. Executive Summary

    The executive summary is for non-technical stakeholders (founders, investors, community).

    Example:

    ## Executive Summary
    
    

    Project: DeFiVault Protocol

    Audit Date: March 15-25, 2026

    Auditor: [Your Name / Firm]

    Commit Hash: abc123def456

    Overview

    DeFiVault is a yield aggregation protocol that automatically compounds rewards across multiple DeFi protocols. The audit covered 8 smart contracts totaling 1,247 lines of Solidity code.

    Key Findings

    • Critical: 1 finding (reentrancy in withdraw function)
    • High: 2 findings (access control, oracle manipulation)
    • Medium: 4 findings (gas griefing, precision loss)
    • Low: 6 findings (missing events, naming conventions)
    • Informational: 12 gas optimizations

    Recommendations

  • Immediate Action Required: Fix critical reentrancy vulnerability before deployment
  • Before Mainnet: Address all High severity findings
  • Post-Deployment: Implement Medium findings in next version
  • Future Improvements: Consider gas optimizations for user experience
  • Risk Assessment

    Current Risk Level: HIGH

    Risk After Fixes: MEDIUM

    The protocol should NOT be deployed until the Critical and High severity issues are resolved.

    2. Scope & Methodology

    Define what was audited and how.

    Example:

    ## Scope
    
    

    In-Scope Contracts

    | Contract | Lines of Code | Purpose |

    |----------|---------------|---------|

    | Vault.sol | 234 | Main vault logic |

    | Strategy.sol | 189 | Strategy interface |

    | RewardDistributor.sol | 156 | Reward distribution |

    | GovernanceToken.sol | 98 | Protocol governance |

    | ... | | |

    Total: 1,247 lines of Solidity

    Solidity Version: 0.8.20

    Repository: https://github.com/project/defi-vault

    Commit: abc123def456

    Out of Scope

    • Frontend / off-chain components
    • Third-party integrations (assumed secure)
    • Economic attack vectors
    • Centralization risks (acknowledged by team)

    Methodology

    Manual Review

    • Line-by-line code review
    • Architecture analysis
    • Access control verification
    • Business logic validation

    Automated Analysis

    • Slither: Static analysis
    • Aderyn: Rust-based analyzer
    • Mythril: Symbolic execution
    • Echidna: Fuzzing

    Testing

    • Foundry test suite review (92% coverage)
    • Custom exploit POCs for findings
    • Mainnet fork testing

    Timeline

    • March 15-18: Automated scanning & test review
    • March 19-22: Manual review
    • March 23-24: POC development
    • March 25: Report writing

    3. Severity Classification

    Establish a clear severity system.

    Example:

    ## Severity Classification
    
    

    We use the CVSS-inspired classification system:

    Critical

    • Impact: Direct loss of funds or protocol brick
    • Likelihood: Easily exploitable
    • Examples: Reentrancy allowing fund drainage, unauthorized mint

    High

    • Impact: Significant fund loss or protocol disruption
    • Likelihood: Exploitable with moderate effort
    • Examples: Access control bypass, oracle manipulation

    Medium

    • Impact: Limited fund loss or degraded functionality
    • Likelihood: Requires specific conditions
    • Examples: Precision loss, gas griefing, MEV vulnerabilities

    Low

    • Impact: Minimal user impact
    • Likelihood: Edge cases or theoretical
    • Examples: Missing events, poor error messages

    Informational

    • Impact: Code quality, gas optimization, best practices
    • Likelihood: N/A
    • Examples: Magic numbers, redundant checks

    4. Findings Format

    Each finding should follow a consistent template:

    ## [C-1] Reentrancy Vulnerability in withdraw() Allows Fund Drainage
    
    

    Severity: Critical

    Status: Unresolved

    File: src/Vault.sol

    Lines: 145-152

    Description

    The withdraw() function makes an external call before updating the user's balance, allowing a reentrancy attack that can drain all vault funds.

    Vulnerable Code

    solidity

    function withdraw(uint256 amount) external {

    require(balances[msg.sender] >= amount, "Insufficient balance");

    // VULNERABLE: external call before state update

    (bool success, ) = msg.sender.call{value: amount}("");

    require(success, "Transfer failed");

    // State updated AFTER external call

    balances[msg.sender] -= amount;

    totalDeposits -= amount;

    }

    ### Attack Scenario
    
    
  • Attacker deposits 1 ETH
  • Attacker calls withdraw(1 ether)
  • In the receive function, attacker calls withdraw(1 ether) again
  • Since balance hasn't been updated yet, the check passes
  • Repeat until vault is drained
  • Proof of Concept

    solidity

    contract ReentrancyAttack {

    Vault public vault;

    uint256 public attackCount;

    function attack() external payable {

    vault.deposit{value: 1 ether}();

    vault.withdraw(1 ether);

    }

    receive() external payable {

    if (attackCount < 10 && address(vault).balance >= 1 ether) {

    attackCount++;

    vault.withdraw(1 ether);

    }

    }

    }

    Test Output:

    Vault balance before: 100 ETH

    Vault balance after: 0 ETH

    Attacker profit: 100 ETH

    ### Impact
    
    

    Complete loss of all funds in the vault. Given the vault currently holds $2.5M in TVL on testnet, this represents total protocol failure.

    Recommendation

    Implement the checks-effects-interactions pattern:

    solidity

    function withdraw(uint256 amount) external {

    require(balances[msg.sender] >= amount, "Insufficient balance");

    // Update state BEFORE external call

    balances[msg.sender] -= amount;

    totalDeposits -= amount;

    // External call AFTER state updates

    (bool success, ) = msg.sender.call{value: amount}("");

    require(success, "Transfer failed");

    emit Withdrawal(msg.sender, amount);

    }

    Alternatively, use OpenZeppelin's ReentrancyGuard:
    solidity

    import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

    contract Vault is ReentrancyGuard {

    function withdraw(uint256 amount) external nonReentrant {

    // ... rest of code

    }

    }

    ### References
    
    
    
    
    

    Client Response

    [To be filled after remediation]

    Real-World Finding Examples

    High Severity: Access Control Bypass

    ## [H-1] Missing Access Control Allows Anyone to Update Oracle Price
    
    

    Severity: High

    File: src/PriceOracle.sol

    Lines: 67-72

    Description

    The updatePrice() function lacks access control, allowing any address to manipulate price feeds.

    Vulnerable Code

    solidity

    function updatePrice(address token, uint256 price) external {

    prices[token] = price;

    lastUpdate[token] = block.timestamp;

    emit PriceUpdated(token, price);

    }

    ### Impact
    
    

    Attacker can:

  • Set artificially low prices to drain vault during withdrawals
  • Set artificially high prices to borrow beyond collateral value
  • Cause liquidations by manipulating collateral prices
  • Recommendation

    Add onlyOwner or role-based access control:

    solidity

    function updatePrice(address token, uint256 price) external onlyOracle {

    require(price > 0, "Invalid price");

    require(block.timestamp >= lastUpdate[token] + MIN_UPDATE_DELAY, "Too soon");

    prices[token] = price;

    lastUpdate[token] = block.timestamp;

    emit PriceUpdated(token, price);

    }

    Medium Severity: Precision Loss

    ## [M-1] Integer Division Truncation Causes Reward Loss
    
    

    Severity: Medium

    File: src/RewardDistributor.sol

    Lines: 89

    Description

    Reward calculation uses integer division, causing precision loss for users with small stakes.

    Vulnerable Code

    solidity

    function calculateReward(address user) public view returns (uint256) {

    uint256 userShare = balances[user] / totalStaked;

    return rewardPool * userShare;

    }

    ### Impact
    
    

    For small stakes:

    User stake: 100 tokens

    Total staked: 1,000,000 tokens

    userShare = 100 / 1,000,000 = 0 (truncated!)

    Reward = rewardPool * 0 = 0

    Users lose 100% of rewards if stake < 0.01% of total.
    
    

    Recommendation

    Use fixed-point math:

    solidity

    uint256 constant PRECISION = 1e18;

    function calculateReward(address user) public view returns (uint256) {

    uint256 userShare = (balances[user] * PRECISION) / totalStaked;

    return (rewardPool * userShare) / PRECISION;

    }

    Low Severity: Missing Events

    ## [L-1] Critical State Changes Lack Event Emission
    
    

    Severity: Low

    File: Multiple

    Description

    Several critical functions don't emit events, making off-chain tracking difficult.

    Examples

    solidity

    // src/Governance.sol:45

    function updateQuorum(uint256 newQuorum) external onlyOwner {

    quorum = newQuorum;

    // Missing: emit QuorumUpdated(oldQuorum, newQuorum);

    }

    // src/Vault.sol:123

    function pause() external onlyOwner {

    paused = true;

    // Missing: emit Paused(msg.sender);

    }

    ### Recommendation
    
    

    Add events for all state changes:

    solidity

    event QuorumUpdated(uint256 oldQuorum, uint256 newQuorum);

    event Paused(address indexed by);

    function updateQuorum(uint256 newQuorum) external onlyOwner {

    uint256 oldQuorum = quorum;

    quorum = newQuorum;

    emit QuorumUpdated(oldQuorum, newQuorum);

    }

    5. Tools to Use

    Static Analyzers

    # Slither
    

    slither . --exclude-dependencies

    # Aderyn

    aderyn . --output slither-report.md

    # Mythril

    myth analyze src/Vault.sol

    Fuzzing

    # Foundry
    

    forge test --fuzz-runs 10000

    # Echidna

    echidna-test . --contract VaultTest --config echidna.yaml

    Coverage

    forge coverage

    Manual Review Checklist

    • [ ] Reentrancy vulnerabilities
    • [ ] Access control on all state-changing functions
    • [ ] Integer overflow/underflow (pre-0.8)
    • [ ] Unchecked external calls
    • [ ] Frontrunning / MEV vulnerabilities
    • [ ] Oracle manipulation
    • [ ] Denial of service vectors
    • [ ] Gas griefing attacks
    • [ ] Centralization risks
    • [ ] Upgrade mechanism safety (if upgradeable)

    6. Example Report Template

    Here's a minimal complete report:

    # Smart Contract Audit Report
    
    

    Project: [Name]

    Date: [Date Range]

    Auditor: [Your Name]

    Commit: [hash]

    ---

    Executive Summary

    [2-3 paragraphs summarizing findings]

    Severity Breakdown:

    • Critical: X
    • High: X
    • Medium: X
    • Low: X

    ---

    Scope

    [List of contracts and LOC]

    ---

    Findings

    Critical

    [C-1] Title

    [Finding details using template above]

    High

    [H-1] Title

    [...]

    Medium

    [M-1] Title

    [...]

    Low

    [L-1] Title

    [...]

    ---

    Recommendations

  • [Priority 1]
  • [Priority 2]
  • [Priority 3]
  • ---

    Appendix

    Tools Used

    • Slither v0.9.3
    • Foundry 2026-03-20
    • Manual review

    Test Coverage

    92% line coverage

    Code Quality: B+

    • Well-structured
    • Good documentation
    • Some areas need refactoring

    Tips for Your First Audit

  • Start small: Audit toy contracts or CTF challenges
  • Study published reports: Read reports from Trail of Bits, OpenZeppelin, Cyfrin
  • Use checklists: Don't rely on memory
  • Write POCs: Prove exploitability
  • Be specific: "Reentrancy" is vague; "Reentrancy in withdraw() via fallback" is clear
  • Think like an attacker: What's the worst that could happen?
  • Document everything: Screenshots, tx hashes, test outputs
  • Career Path

  • Practice (1-3 months): Audit open-source projects, write reports
  • Capture the Flag (CTFs): Ethernaut, Damn Vulnerable DeFi, Paradigm CTF
  • Bug Bounties (3-6 months): Code4rena, Immunefi, HackenProof
  • Junior Auditor (6-12 months): Join audit firm or independent
  • Senior Auditor (1-2 years): Lead audits, train juniors
  • Top auditors earn $200k-$500k+ annually. The barrier to entry is skill, not credentials.

    Conclusion

    Writing audit reports is a learnable skill. Start with simple contracts, use the templates in this guide, and iterate. Every report you write makes you a better security researcher.

    Your first audit report won't be perfect — but it will be the start of a valuable career in blockchain security.

    Happy auditing.

    ```

    ---

    Resources

    Prêt à mettre en pratique ?

    Applique ces concepts avec des exercices interactifs sur Solingo.

    Commencer gratuitement