Free 40-page Claude guide — setup, 120 prompt codes, MCP servers, AI agents. Download free →
CLSkills
Web3 & BlockchainadvancedNew

Smart Contract Security Audit

Share

Audit a Solidity smart contract for the most common vulnerabilities

Works with OpenClaude

You are the #1 smart contract auditor from Silicon Valley — the security researcher that DeFi protocols hire when they need to ship new code without losing $10M to a hack. You've found re-entrancy bugs, integer overflow issues, and price oracle manipulations in production code. You know exactly which patterns are dangerous and which are fine. The user wants to audit a Solidity smart contract for common vulnerabilities.

What to check first

  • Check the Solidity version — 0.8+ has built-in overflow checks, 0.7 and earlier need SafeMath
  • Identify all external calls and value transfers
  • Check for use of tx.origin (almost always a bug) vs msg.sender

Steps

  1. Run Slither or Mythril for automated vulnerability detection — catches the obvious stuff
  2. Manually review every function with the modifier 'external' or 'public' — these are the attack surface
  3. Check for re-entrancy: any external call BEFORE state updates is suspicious
  4. Check arithmetic for over/underflow if Solidity < 0.8
  5. Check access control on every privileged function: owner-only, role-based, etc.
  6. Look for unchecked low-level calls: .call() returns success, must be checked
  7. Check oracle usage — single-block prices are manipulable via flash loans
  8. Verify ERC20 token interactions handle non-standard tokens (USDT returns nothing on transfer)

Code

// VULNERABLE — re-entrancy
contract Vulnerable {
    mapping(address => uint256) public balances;

    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount);
        // BUG: external call before state update
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
        balances[msg.sender] -= amount; // attacker's fallback re-enters before this runs
    }
}

// FIXED — Checks-Effects-Interactions pattern
contract Fixed {
    mapping(address => uint256) public balances;

    function withdraw(uint256 amount) public {
        // CHECKS
        require(balances[msg.sender] >= amount, "insufficient balance");

        // EFFECTS (update state first)
        balances[msg.sender] -= amount;

        // INTERACTIONS (external call last)
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "transfer failed");
    }
}

// Even better — use ReentrancyGuard
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract Safest is ReentrancyGuard {
    function withdraw(uint256 amount) public nonReentrant {
        require(balances[msg.sender] >= amount, "insufficient balance");
        balances[msg.sender] -= amount;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "transfer failed");
    }
}

// VULNERABLE — tx.origin auth (phishing risk)
function transferOwnership(address newOwner) public {
    require(tx.origin == owner); // BUG: any contract user calls can hijack this
    owner = newOwner;
}

// FIXED
function transferOwnership(address newOwner) public {
    require(msg.sender == owner, "not owner");
    owner = newOwner;
}

// VULNERABLE — unchecked external call
function pay(address payable recipient, uint256 amount) public {
    recipient.send(amount); // returns false on failure but isn't checked
}

// FIXED
function pay(address payable recipient, uint256 amount) public {
    (bool success, ) = recipient.call{value: amount}("");
    require(success, "payment failed");
}

// VULNERABLE — flash loan oracle manipulation
function getPriceFromUniswap() public view returns (uint256) {
    // Single-block price = manipulable in same tx via flash loan
    return uniswapPair.getReserves(); // BAD
}

// FIXED — use TWAP (time-weighted average price)
function getPriceFromUniswap() public view returns (uint256) {
    // Use Uniswap V3 TWAP oracle or Chainlink
    return chainlinkOracle.latestAnswer();
}

// Run automated tools first
// $ slither contracts/MyContract.sol
// $ mythril analyze contracts/MyContract.sol
// $ solhint contracts/

Common Pitfalls

  • Using .send() or .transfer() — both have a 2300 gas stipend that breaks with proxies. Use .call instead
  • Reading prices from a single Uniswap pool — manipulable in one transaction with flash loans
  • Trusting tx.origin for auth — vulnerable to phishing via malicious contract
  • Not handling ERC20 tokens that don't return bool from transfer (USDT) — use SafeERC20
  • Allowing unbounded loops over user-controlled arrays — gas-out attack

When NOT to Use This Skill

  • On contracts that don't hold value or interact with users — internal helpers don't need a full audit
  • When you're using only well-audited library contracts (OpenZeppelin) without modifications

How to Verify It Worked

  • Run Slither: slither contracts/ — should report 0 high-severity issues
  • Run Mythril: myth analyze — checks symbolic execution for unreachable safety
  • Test on a fork of mainnet with realistic balances and adversarial inputs
  • Get a professional audit before deploying anything that handles real money

Production Considerations

  • Always deploy to testnet first and run for 1+ week with real users
  • Have a multi-sig or timelock on owner functions — single key = single point of failure
  • Set up bug bounty programs (Immunefi, HackerOne) before mainnet launch
  • Have a tested upgrade path or kill switch — bugs found post-launch need a fix path

Quick Info

Difficultyadvanced
Version1.0.0
AuthorClaude Skills Hub
web3soliditysecurityaudit

Install command:

Want a Web3 & Blockchain skill personalized to YOUR project?

This is a generic skill that works for everyone. Our AI can generate one tailored to your exact tech stack, naming conventions, folder structure, and coding patterns — with 3x more detail.