Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Audit Solidity smart contracts for reentrancy, integer overflow, access control, and oracle manipulation.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/details.md
1# solidity-security — detailed patterns and worked examples23## Critical Vulnerabilities45### 1. Reentrancy67Attacker calls back into your contract before state is updated.89**Vulnerable Code:**1011```solidity12// VULNERABLE TO REENTRANCY13contract VulnerableBank {14mapping(address => uint256) public balances;1516function withdraw() public {17uint256 amount = balances[msg.sender];1819// DANGER: External call before state update20(bool success, ) = msg.sender.call{value: amount}("");21require(success);2223balances[msg.sender] = 0; // Too late!24}25}26```2728**Secure Pattern (Checks-Effects-Interactions):**2930```solidity31contract SecureBank {32mapping(address => uint256) public balances;3334function withdraw() public {35uint256 amount = balances[msg.sender];36require(amount > 0, "Insufficient balance");3738// EFFECTS: Update state BEFORE external call39balances[msg.sender] = 0;4041// INTERACTIONS: External call last42(bool success, ) = msg.sender.call{value: amount}("");43require(success, "Transfer failed");44}45}46```4748**Alternative: ReentrancyGuard**4950```solidity51import "@openzeppelin/contracts/security/ReentrancyGuard.sol";5253contract SecureBank is ReentrancyGuard {54mapping(address => uint256) public balances;5556function withdraw() public nonReentrant {57uint256 amount = balances[msg.sender];58require(amount > 0, "Insufficient balance");5960balances[msg.sender] = 0;6162(bool success, ) = msg.sender.call{value: amount}("");63require(success, "Transfer failed");64}65}66```6768### 2. Integer Overflow/Underflow6970**Vulnerable Code (Solidity < 0.8.0):**7172```solidity73// VULNERABLE74contract VulnerableToken {75mapping(address => uint256) public balances;7677function transfer(address to, uint256 amount) public {78// No overflow check - can wrap around79balances[msg.sender] -= amount; // Can underflow!80balances[to] += amount; // Can overflow!81}82}83```8485**Secure Pattern (Solidity >= 0.8.0):**8687```solidity88// Solidity 0.8+ has built-in overflow/underflow checks89contract SecureToken {90mapping(address => uint256) public balances;9192function transfer(address to, uint256 amount) public {93// Automatically reverts on overflow/underflow94balances[msg.sender] -= amount;95balances[to] += amount;96}97}98```99100**For Solidity < 0.8.0, use SafeMath:**101102```solidity103import "@openzeppelin/contracts/utils/math/SafeMath.sol";104105contract SecureToken {106using SafeMath for uint256;107mapping(address => uint256) public balances;108109function transfer(address to, uint256 amount) public {110balances[msg.sender] = balances[msg.sender].sub(amount);111balances[to] = balances[to].add(amount);112}113}114```115116### 3. Access Control117118**Vulnerable Code:**119120```solidity121// VULNERABLE: Anyone can call critical functions122contract VulnerableContract {123address public owner;124125function withdraw(uint256 amount) public {126// No access control!127payable(msg.sender).transfer(amount);128}129}130```131132**Secure Pattern:**133134```solidity135import "@openzeppelin/contracts/access/Ownable.sol";136137contract SecureContract is Ownable {138function withdraw(uint256 amount) public onlyOwner {139payable(owner()).transfer(amount);140}141}142143// Or implement custom role-based access144contract RoleBasedContract {145mapping(address => bool) public admins;146147modifier onlyAdmin() {148require(admins[msg.sender], "Not an admin");149_;150}151152function criticalFunction() public onlyAdmin {153// Protected function154}155}156```157158### 4. Front-Running159160**Vulnerable:**161162```solidity163// VULNERABLE TO FRONT-RUNNING164contract VulnerableDEX {165function swap(uint256 amount, uint256 minOutput) public {166// Attacker sees this in mempool and front-runs167uint256 output = calculateOutput(amount);168require(output >= minOutput, "Slippage too high");169// Perform swap170}171}172```173174**Mitigation:**175176```solidity177contract SecureDEX {178mapping(bytes32 => bool) public usedCommitments;179180// Step 1: Commit to trade181function commitTrade(bytes32 commitment) public {182usedCommitments[commitment] = true;183}184185// Step 2: Reveal trade (next block)186function revealTrade(187uint256 amount,188uint256 minOutput,189bytes32 secret190) public {191bytes32 commitment = keccak256(abi.encodePacked(192msg.sender, amount, minOutput, secret193));194require(usedCommitments[commitment], "Invalid commitment");195// Perform swap196}197}198```199200## Security Best Practices201202### Checks-Effects-Interactions Pattern203204```solidity205contract SecurePattern {206mapping(address => uint256) public balances;207208function withdraw(uint256 amount) public {209// 1. CHECKS: Validate conditions210require(amount <= balances[msg.sender], "Insufficient balance");211require(amount > 0, "Amount must be positive");212213// 2. EFFECTS: Update state214balances[msg.sender] -= amount;215216// 3. INTERACTIONS: External calls last217(bool success, ) = msg.sender.call{value: amount}("");218require(success, "Transfer failed");219}220}221```222223### Pull Over Push Pattern224225```solidity226// Prefer this (pull)227contract SecurePayment {228mapping(address => uint256) public pendingWithdrawals;229230function recordPayment(address recipient, uint256 amount) internal {231pendingWithdrawals[recipient] += amount;232}233234function withdraw() public {235uint256 amount = pendingWithdrawals[msg.sender];236require(amount > 0, "Nothing to withdraw");237238pendingWithdrawals[msg.sender] = 0;239payable(msg.sender).transfer(amount);240}241}242243// Over this (push)244contract RiskyPayment {245function distributePayments(address[] memory recipients, uint256[] memory amounts) public {246for (uint i = 0; i < recipients.length; i++) {247// If any transfer fails, entire batch fails248payable(recipients[i]).transfer(amounts[i]);249}250}251}252```253254### Input Validation255256```solidity257contract SecureContract {258function transfer(address to, uint256 amount) public {259// Validate inputs260require(to != address(0), "Invalid recipient");261require(to != address(this), "Cannot send to contract");262require(amount > 0, "Amount must be positive");263require(amount <= balances[msg.sender], "Insufficient balance");264265// Proceed with transfer266balances[msg.sender] -= amount;267balances[to] += amount;268}269}270```271272### Emergency Stop (Circuit Breaker)273274```solidity275import "@openzeppelin/contracts/security/Pausable.sol";276277contract EmergencyStop is Pausable, Ownable {278function criticalFunction() public whenNotPaused {279// Function logic280}281282function emergencyStop() public onlyOwner {283_pause();284}285286function resume() public onlyOwner {287_unpause();288}289}290```291292## Gas Optimization293294### Use `uint256` Instead of Smaller Types295296```solidity297// More gas efficient298contract GasEfficient {299uint256 public value; // Optimal300301function set(uint256 _value) public {302value = _value;303}304}305306// Less efficient307contract GasInefficient {308uint8 public value; // Still uses 256-bit slot309310function set(uint8 _value) public {311value = _value; // Extra gas for type conversion312}313}314```315316### Pack Storage Variables317318```solidity319// Gas efficient (3 variables in 1 slot)320contract PackedStorage {321uint128 public a; // Slot 0322uint64 public b; // Slot 0323uint64 public c; // Slot 0324uint256 public d; // Slot 1325}326327// Gas inefficient (each variable in separate slot)328contract UnpackedStorage {329uint256 public a; // Slot 0330uint256 public b; // Slot 1331uint256 public c; // Slot 2332uint256 public d; // Slot 3333}334```335336### Use `calldata` Instead of `memory` for Function Arguments337338```solidity339contract GasOptimized {340// More gas efficient341function processData(uint256[] calldata data) public pure returns (uint256) {342return data[0];343}344345// Less efficient346function processDataMemory(uint256[] memory data) public pure returns (uint256) {347return data[0];348}349}350```351352### Use Events for Data Storage (When Appropriate)353354```solidity355contract EventStorage {356// Emitting events is cheaper than storage357event DataStored(address indexed user, uint256 indexed id, bytes data);358359function storeData(uint256 id, bytes calldata data) public {360emit DataStored(msg.sender, id, data);361// Don't store in contract storage unless needed362}363}364```365366## Common Vulnerabilities Checklist367368```solidity369// Security Checklist Contract370contract SecurityChecklist {371/**372* [ ] Reentrancy protection (ReentrancyGuard or CEI pattern)373* [ ] Integer overflow/underflow (Solidity 0.8+ or SafeMath)374* [ ] Access control (Ownable, roles, modifiers)375* [ ] Input validation (require statements)376* [ ] Front-running mitigation (commit-reveal if applicable)377* [ ] Gas optimization (packed storage, calldata)378* [ ] Emergency stop mechanism (Pausable)379* [ ] Pull over push pattern for payments380* [ ] No delegatecall to untrusted contracts381* [ ] No tx.origin for authentication (use msg.sender)382* [ ] Proper event emission383* [ ] External calls at end of function384* [ ] Check return values of external calls385* [ ] No hardcoded addresses386* [ ] Upgrade mechanism (if proxy pattern)387*/388}389```390