Security of AI-Generated Smart Contracts
Security analysis of AI-generated Solidity smart contracts covering reentrancy, integer overflow, access control, and automated vulnerability detection.
Overview
AI-generated smart contracts represent one of the highest-risk applications of code generation. Smart contracts are immutable once deployed, handle financial assets directly, and operate in an adversarial environment where any vulnerability is immediately exploitable for financial gain. When an LLM generates a vulnerable smart contract, the consequences are not a data breach to be detected and patched — they are an irreversible loss of funds.
Research has shown that LLMs consistently generate smart contracts with critical vulnerabilities including reentrancy, integer overflow, improper access control, and front-running susceptibility. This article examines these vulnerability patterns, explains why LLMs produce them, and provides detection and prevention strategies.
Why AI-Generated Smart Contracts Are Dangerous
The Immutability Problem
Unlike traditional application code that can be patched, deployed smart contracts cannot be changed. Once a vulnerable contract is deployed to the blockchain, the vulnerability persists forever unless the contract includes an upgrade mechanism. This means the development workflow must catch every vulnerability before deployment — there is no second chance.
Training Data Lag
LLMs are trained on historical code. The Solidity ecosystem evolves rapidly, with new vulnerability classes, compiler changes, and best practices emerging frequently. AI tools may suggest patterns that were considered acceptable in earlier Solidity versions but are now known to be dangerous.
# The gap between LLM training data and current Solidity best practices
SOLIDITY_EVOLUTION = [
{
"era": "Solidity 0.4.x - 0.5.x (2017-2019)",
"common_in_training_data": True,
"known_issues": [
"No built-in overflow protection",
"tx.origin authentication pattern",
"Unrestricted selfdestruct",
"Default function visibility is public",
],
},
{
"era": "Solidity 0.6.x - 0.7.x (2020-2021)",
"common_in_training_data": True,
"known_issues": [
"Still no built-in overflow protection (0.6.x)",
"Abstract contracts not enforced (0.6.x)",
],
},
{
"era": "Solidity 0.8.x+ (2021-present)",
"common_in_training_data": True, # But mixed with older patterns
"improvements": [
"Built-in overflow/underflow protection",
"Custom errors for gas-efficient reverts",
"ABIEncoderV2 by default",
],
"risk": (
"LLMs mix patterns from all eras. May suggest SafeMath "
"usage with 0.8.x (unnecessary) or suggest unchecked "
"blocks without understanding the risk."
),
},
]Critical Vulnerability Patterns
Reentrancy
Reentrancy is the most famous smart contract vulnerability, responsible for the DAO hack ($60M loss). LLMs frequently generate reentrancy-vulnerable code because the vulnerable pattern (send funds before updating state) is more intuitive and more common in training data:
# Reentrancy patterns in AI-generated Solidity
REENTRANCY_EXAMPLES = {
"classic_reentrancy": {
"severity": "critical",
"frequency": "common",
"insecure_solidity": '''
// INSECURE: AI-generated withdrawal function
// This is the classic reentrancy pattern LLMs reproduce
contract VulnerableVault {
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
// VULNERABLE: sends ETH before updating balance
function withdraw() public {
uint256 balance = balances[msg.sender];
require(balance > 0, "No balance");
// BUG: External call before state update
(bool success, ) = msg.sender.call{value: balance}("");
require(success, "Transfer failed");
// State update happens after external call
// Attacker's receive() can re-enter withdraw()
balances[msg.sender] = 0;
}
}
''',
"secure_solidity": '''
// SECURE: Checks-Effects-Interactions pattern + ReentrancyGuard
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract SecureVault is ReentrancyGuard {
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw() public nonReentrant {
uint256 balance = balances[msg.sender];
require(balance > 0, "No balance");
// Effects: update state BEFORE interaction
balances[msg.sender] = 0;
// Interaction: external call AFTER state update
(bool success, ) = msg.sender.call{value: balance}("");
require(success, "Transfer failed");
}
}
''',
},
"cross_function_reentrancy": {
"severity": "critical",
"frequency": "moderate",
"insecure_solidity": '''
// INSECURE: Cross-function reentrancy
// LLMs rarely consider this variant
contract VulnerableToken {
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount) public {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;
balances[to] += amount;
}
function withdraw() public {
uint256 balance = balances[msg.sender];
// External call: attacker can call transfer() during this call
(bool success, ) = msg.sender.call{value: balance}("");
require(success);
balances[msg.sender] = 0;
}
}
''',
},
}Access Control Failures
LLMs often generate smart contracts with inadequate access control:
ACCESS_CONTROL_FAILURES = {
"missing_owner_check": {
"severity": "critical",
"frequency": "common",
"insecure_solidity": '''
// INSECURE: AI-generated admin functions without access control
contract VulnerableAdmin {
address public owner;
bool public paused;
constructor() {
owner = msg.sender;
}
// VULNERABLE: No access control!
function setOwner(address newOwner) public {
owner = newOwner;
}
// VULNERABLE: Anyone can pause/unpause
function setPaused(bool _paused) public {
paused = _paused;
}
// VULNERABLE: Anyone can withdraw
function emergencyWithdraw() public {
payable(msg.sender).transfer(address(this).balance);
}
}
''',
"secure_solidity": '''
// SECURE: Proper access control with OpenZeppelin
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
contract SecureAdmin is Ownable, Pausable {
constructor() Ownable(msg.sender) {}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function emergencyWithdraw() public onlyOwner whenPaused {
payable(owner()).transfer(address(this).balance);
emit EmergencyWithdrawal(owner(), address(this).balance);
}
event EmergencyWithdrawal(address indexed to, uint256 amount);
}
''',
},
"tx_origin_auth": {
"severity": "high",
"frequency": "moderate",
"insecure_solidity": '''
// INSECURE: Using tx.origin for authentication
// LLMs sometimes suggest this because it appears in old tutorials
contract VulnerableAuth {
address public owner;
constructor() {
owner = msg.sender;
}
// VULNERABLE: tx.origin can be manipulated via phishing contracts
function withdraw() public {
require(tx.origin == owner, "Not owner");
payable(msg.sender).transfer(address(this).balance);
}
}
''',
"secure_solidity": '''
// SECURE: Use msg.sender for authentication
contract SecureAuth {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
}
''',
},
}Integer and Arithmetic Issues
ARITHMETIC_ISSUES = {
"unchecked_arithmetic": {
"severity": "high",
"frequency": "moderate",
"description": "LLMs may use unchecked blocks without understanding the risk",
"insecure_solidity": '''
// INSECURE: AI uses unchecked for "gas optimization" without analysis
contract VulnerableToken {
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount) public {
// AI adds unchecked for "gas optimization"
unchecked {
// These can overflow/underflow in unchecked blocks!
balances[msg.sender] -= amount;
balances[to] += amount;
}
}
}
''',
"secure_solidity": '''
// SECURE: Use Solidity 0.8+ built-in checks
contract SecureToken {
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount) public {
require(to != address(0), "Invalid recipient");
require(balances[msg.sender] >= amount, "Insufficient balance");
// Solidity 0.8+ automatically checks for overflow/underflow
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
}
event Transfer(address indexed from, address indexed to, uint256 amount);
}
''',
},
"division_before_multiplication": {
"severity": "medium",
"frequency": "common",
"description": "Precision loss from integer division ordering",
"insecure_solidity": '''
// INSECURE: Division before multiplication loses precision
function calculateFee(uint256 amount, uint256 feePercent) public pure returns (uint256) {
// AI generates: amount / 100 * feePercent
// If amount = 199, this gives 1 * feePercent instead of 199 * feePercent / 100
return amount / 100 * feePercent;
}
''',
"secure_solidity": '''
// SECURE: Multiply before divide to preserve precision
function calculateFee(uint256 amount, uint256 feePercent) public pure returns (uint256) {
return amount * feePercent / 100;
}
''',
},
}Detection and Analysis Tools
Slither Static Analysis
#!/bin/bash
# Analyze AI-generated Solidity with Slither
echo "=== Slither Analysis for AI-Generated Smart Contracts ==="
CONTRACT_DIR="${1:-.}"
if ! command -v slither &>/dev/null; then
echo "Installing Slither..."
pip install slither-analyzer
fi
# Run Slither analysis
slither "$CONTRACT_DIR" \
--json /tmp/slither-results.json \
--exclude-dependencies \
2>/dev/null || true
# Parse results focusing on AI-common vulnerabilities
python3 << 'PYTHON'
import json
with open("/tmp/slither-results.json") as f:
data = json.load(f)
# AI-common vulnerability detectors
AI_COMMON_DETECTORS = {
"reentrancy-eth": "Reentrancy (ETH transfer)",
"reentrancy-no-eth": "Reentrancy (no ETH)",
"unprotected-upgrade": "Unprotected upgrade function",
"arbitrary-send-eth": "Arbitrary ETH send",
"tx-origin": "tx.origin authentication",
"unchecked-transfer": "Unchecked token transfer",
"missing-zero-check": "Missing zero address check",
"uninitialized-state": "Uninitialized state variable",
"shadowing-state": "State variable shadowing",
"controlled-delegatecall": "Controlled delegatecall",
}
detectors = data.get("results", {}).get("detectors", [])
print(f"Total findings: {len(detectors)}")
print()
ai_related = [
d for d in detectors
if d.get("check") in AI_COMMON_DETECTORS
]
if ai_related:
print(f"AI-common vulnerability findings: {len(ai_related)}")
for d in ai_related:
check = d["check"]
impact = d.get("impact", "unknown")
confidence = d.get("confidence", "unknown")
desc = d.get("description", "")[:200]
print(f" [{impact}/{confidence}] {AI_COMMON_DETECTORS.get(check, check)}")
print(f" {desc}")
print()
else:
print("No AI-common vulnerability patterns detected.")
PYTHONMythril Analysis
#!/bin/bash
# Run Mythril symbolic execution on AI-generated contracts
CONTRACT_FILE="${1}"
if [ -z "$CONTRACT_FILE" ]; then
echo "Usage: $0 <contract.sol>"
exit 1
fi
if ! command -v myth &>/dev/null; then
echo "Installing Mythril..."
pip install mythril
fi
echo "=== Mythril Symbolic Execution ==="
echo "Analyzing: $CONTRACT_FILE"
echo ""
# Run Mythril with timeout
myth analyze "$CONTRACT_FILE" \
--execution-timeout 120 \
--solver-timeout 30000 \
-o json \
> /tmp/mythril-results.json 2>/dev/null
python3 << 'PYTHON'
import json
try:
with open("/tmp/mythril-results.json") as f:
data = json.load(f)
issues = data.get("issues", [])
print(f"Vulnerabilities found: {len(issues)}")
for issue in issues:
print(f" [{issue.get('severity', '?')}] {issue.get('title', 'Unknown')}")
print(f" SWC: {issue.get('swc-id', 'N/A')}")
print(f" Description: {issue.get('description', '')[:150]}")
print()
except json.JSONDecodeError:
print("No valid results from Mythril. Contract may have compilation issues.")
PYTHONSmart Contract Audit Workflow for AI-Generated Code
# Comprehensive audit workflow for AI-generated smart contracts
AUDIT_WORKFLOW = {
"phase_1_review": {
"name": "Manual Code Review",
"checks": [
"Verify Solidity version is 0.8.x or later",
"Check for Checks-Effects-Interactions pattern in all external calls",
"Verify ReentrancyGuard on all functions with external calls",
"Check access control on all state-changing functions",
"Verify no use of tx.origin for authentication",
"Check for proper use of unchecked blocks",
"Verify event emission for all state changes",
"Check for front-running susceptibility",
],
},
"phase_2_static": {
"name": "Static Analysis",
"tools": [
{"tool": "Slither", "focus": "Reentrancy, access control, state issues"},
{"tool": "Mythril", "focus": "Symbolic execution for logic bugs"},
{"tool": "Semgrep", "focus": "Pattern matching for known vulnerabilities"},
],
},
"phase_3_testing": {
"name": "Testing",
"activities": [
"Unit tests for all functions with edge cases",
"Fuzz testing with Foundry or Echidna",
"Invariant testing for critical properties",
"Gas optimization review (but not at expense of security)",
],
},
"phase_4_formal": {
"name": "Formal Verification (for high-value contracts)",
"tools": [
{"tool": "Certora Prover", "focus": "Formal property verification"},
{"tool": "KEVM", "focus": "EVM bytecode verification"},
],
},
}
def generate_audit_checklist(contract_name: str) -> str:
"""Generate an audit checklist for an AI-generated smart contract."""
checklist = f"# Smart Contract Audit Checklist: {contract_name}\n\n"
critical_items = [
"[ ] No reentrancy vulnerabilities (CEI pattern + ReentrancyGuard)",
"[ ] All admin functions have proper access control",
"[ ] No use of tx.origin for authentication",
"[ ] No hardcoded addresses or magic numbers",
"[ ] All external calls have proper error handling",
"[ ] Integer arithmetic is safe (no unchecked blocks on user input)",
"[ ] No front-running vulnerabilities in DEX/auction logic",
"[ ] Upgrade mechanism is secure (if applicable)",
"[ ] Emergency pause mechanism exists",
"[ ] All events emitted for state changes",
"[ ] Constructor sets correct initial state",
"[ ] No selfdestruct reachable by unauthorized parties",
]
checklist += "## Critical Security Checks\n"
for item in critical_items:
checklist += f"- {item}\n"
return checklistReferences
- "Large Language Models for Blockchain Security: A Systematic Literature Survey" — Zhou et al., 2024 — https://arxiv.org/abs/2403.14280
- Slither — Solidity static analysis — https://github.com/crytic/slither
- Mythril — EVM symbolic execution — https://github.com/Consensys/mythril
- OpenZeppelin Contracts — Security-audited contract library — https://github.com/OpenZeppelin/openzeppelin-contracts
- SWC Registry — Smart Contract Weakness Classification — https://swcregistry.io/
- CWE-841: Improper Enforcement of Behavioral Workflow — https://cwe.mitre.org/data/definitions/841.html
- OWASP Smart Contract Top 10 — https://owasp.org/www-project-smart-contract-top-10/
- The DAO Hack — Post-mortem Analysis — https://blog.ethereum.org/2016/06/17/critical-update-re-dao-vulnerability