Reentrancy & Checks-Effects-Interactions
Harden contracts against the most common smart-contract attack.
Code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract SafeVault {
mapping(address => uint256) public balances;
bool private locked; // reentrancy guard
modifier noReentrant() {
require(!locked, "reentrant");
locked = true;
_;
locked = false;
}
function deposit() external payable {
balances[msg.sender] += msg.value;
}
// VULNERABLE pattern (DO NOT USE):
// function withdrawBad() external {
// uint256 bal = balances[msg.sender];
// (bool ok,) = msg.sender.call{value: bal}("");
// require(ok);
// balances[msg.sender] = 0; // state update AFTER external call
// }
function withdraw() external noReentrant {
uint256 bal = balances[msg.sender];
require(bal > 0, "nothing");
// 1) Checks (require)
// 2) Effects (mutate state FIRST)
balances[msg.sender] = 0;
// 3) Interactions (external call LAST)
(bool ok, ) = payable(msg.sender).call{value: bal}("");
require(ok, "transfer failed");
}
}Explanation
Reentrancy happens when an external call re-enters your function before state is updated, letting the caller drain funds. Defense in depth: (1) follow Checks-Effects-Interactions — update state before any external call; (2) use a reentrancy guard (mutex); (3) prefer pull-over-push payments (let users withdraw their own share); (4) bound loops and gas; (5) use OpenZeppelin's ReentrancyGuard and SafeERC20 in production. Also use pragma with a caret (^0.8.24) and run slither/mythril before deploying.
More Solidity Snippets
Contract Basics
Define a basic smart contract with state and functions.
Functions and Visibility
Function visibility, payable, and return values.
Modifiers and Access Control
Reuse validation logic with modifiers.
Events and Logs
Emit events for off-chain listeners.
Mappings, Structs & Nested Storage
Group related data with structs and look it up by key with mappings.
Inheritance, Abstract & Interfaces
Reuse logic via inheritance; define contracts with abstract and interface.