close

DEV Community

Cover image for 5 Smart Contract Vulnerabilities That AI Catches Better Than Static Analyzers
Pavel Espitia
Pavel Espitia

Posted on

5 Smart Contract Vulnerabilities That AI Catches Better Than Static Analyzers

Static analysis tools like Slither and Mythril are essential for smart contract security. But they work by pattern matching — they can only find what they've been programmed to look for. LLMs reason about code differently. They understand intent, context, and business logic.

Here are 5 vulnerability classes where AI consistently outperforms traditional static analyzers.

1. Business Logic Flaws

Static analyzers check for known patterns: reentrancy, integer overflow, unchecked return values. But they can't understand what your contract is supposed to do.

function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount;
    payable(msg.sender).transfer(amount);
}
Enter fullscreen mode Exit fullscreen mode

A static analyzer sees this as safe — checks-effects-interactions pattern is followed. But an AI auditor can ask: "Should there be a minimum withdrawal? A cooldown period? A daily limit?" It reasons about the business context, not just the code pattern.

2. Access Control Gaps Across Multiple Functions

Slither will flag a function that's missing onlyOwner. But it won't notice that setFeeRecipient() and withdrawFees() together create a privilege escalation path — even if each function individually looks fine.

AI can analyze the interaction between functions:

// AI catches: anyone can set themselves as fee recipient, then withdraw
function setFeeRecipient(address _recipient) external {
    feeRecipient = _recipient;
}

function withdrawFees() external {
    require(msg.sender == feeRecipient);
    payable(feeRecipient).transfer(address(this).balance);
}
Enter fullscreen mode Exit fullscreen mode

The AI output: "These two functions together allow any address to drain the contract. setFeeRecipient has no access control, and withdrawFees only checks the caller matches the recipient — which they just set to themselves."

3. Incorrect Event Parameters

Static analyzers verify that events exist. They don't verify that the emitted values are correct.

event Transfer(address indexed from, address indexed to, uint256 amount);

function transfer(address to, uint256 amount) external {
    balances[msg.sender] -= amount;
    balances[to] += amount;
    emit Transfer(msg.sender, to, balances[to]); // Bug: emits balance, not amount
}
Enter fullscreen mode Exit fullscreen mode

An AI catches this because it understands that the Transfer event should emit the amount transferred, not the resulting balance. No static rule covers this — it requires understanding what the event means.

4. Inconsistent Decimal Handling

DeFi protocols interact with tokens that have different decimal places (USDC has 6, WETH has 18). Static analyzers don't track decimal context across function calls.

function swap(uint256 usdcAmount) external {
    uint256 ethAmount = usdcAmount * getEthPrice() / 1e18;
    // Bug: usdcAmount is 6 decimals, but division assumes 18
}
Enter fullscreen mode Exit fullscreen mode

AI recognizes that USDC uses 6 decimals and flags the math: "The division by 1e18 assumes 18-decimal precision, but USDC has 6 decimals. This will return values 1e12 times smaller than expected."

5. Missing Edge Case Handlers

What happens when the array is empty? When the balance is zero? When the deadline has already passed? Static analyzers check for specific known edge cases. AI reasons about all of them.

function getAveragePrice(uint256[] memory prices) public pure returns (uint256) {
    uint256 sum;
    for (uint256 i = 0; i < prices.length; i++) {
        sum += prices[i];
    }
    return sum / prices.length; // Division by zero if empty array
}
Enter fullscreen mode Exit fullscreen mode

Beyond the obvious division-by-zero, AI also asks: "What if one price is extremely large and causes sum to overflow? Should there be a maximum array length to prevent gas exhaustion?"

The Bottom Line

Static analyzers are necessary — they're fast, deterministic, and catch the obvious stuff. But AI auditors add a layer that reasons about intent, context, and cross-function interactions.

The best approach: run both. Use Slither/Mythril for deterministic checks, then use an AI auditor for the things only reasoning can catch.

If you want to try this yourself:

# Free, local, no API key needed
ollama pull qwen2.5-coder:1.5b
npx spectr-ai --model ollama:qwen2.5-coder:1.5b your-contract.sol
Enter fullscreen mode Exit fullscreen mode

spectr-ai is open source and works with Claude or local models via Ollama.

Top comments (0)