
Security transforms from a local problem (securing your code) into a systemic problem (securing your dependencies).
uint256). Unlike Python or Java, early Solidity allowed arithmetic wrapping without errors.uint8 variable holds 0, the operation x - 1 underflows to 255. Similarly, 255 + 1 overflows to 0—like points on a circular clock.unchecked {...} blocks.
.transfer() to send ETH and the recipient reverts, a single malicious user can halt the entire system for all participants.mapping(address => uint) public pendingWithdrawals;
function withdraw() public {
uint amount = pendingWithdrawals[msg.sender];
require(amount > 0);
pendingWithdrawals[msg.sender] = 0; // Zero BEFORE sending
payable(msg.sender).transfer(amount);
}splitDAO exit mechanism.splitDAO recursively before token balances were updated.function withdraw() public {
uint balance = userBalances[msg.sender];
require(balance > 0);
// INTERACTION: Sending ETH
msg.sender.call{value: balance}("");
// EFFECT: Balance update (TOO LATE!)
userBalances[msg.sender] = 0;
}fallback() external payable {
if (address(vault).balance >= 1 ether) {
vault.withdraw(); // Recursive
}
}
function attack() external payable {
vault.deposit{value: 1 ether}();
vault.withdraw(); // Starts the loop
}require statements to ensure prerequisites are metfunction withdraw() public {
uint balance = userBalances[msg.sender];
// 1. CHECKS
require(balance > 0, "Insufficient balance");
// 2. EFFECTS (Update state FIRST)
userBalances[msg.sender] = 0;
// 3. INTERACTIONS (External call LAST)
(bool success, ) = msg.sender.call{value: balance}("");
require(success, "Transfer failed");
}nonReentrant modifier) using mutex flags to lock functions during execution.The vulnerability: protocols calculating collateral value by querying current "spot price" on a DEX like Uniswap or Curve.
get_virtual_price(), calculating one LP token's value relative to pool assets:
remove_liquidity, LP tokens burn immediately (reducing Total Supply), but internal balances update after ETH transfer. This creates an exploit window where:remove_liquidity@nonreentrant guards failed.@nonreentrant('lock') modifier appeared correct in source code but compiled to bytecode that never enforced mutual exclusion, enabling classic reentrancy attacks on Curve pools (CRV/ETH, aLETH/ETH) that drained over $69 million. This issue affected 3 Vyper versions.mint(), pause(), upgrade()) protected by modifiers like onlyOwner.The "Rug Pull": Malicious developers intentionally leave backdoors or use privileges to drain user funds.

nonReentrant on state-changing functions?