Reentrancy
An external call made before updating state lets an attacker re-enter the function before balances are decremented. Follow checks-effects-interactions and use a reentrancy guard.
// vulnerable
function withdraw(uint a) public {
require(bal[msg.sender] >= a);
(bool ok,) = msg.sender.call{value:a}("");
bal[msg.sender] -= a; // too late
}
// fixed
function withdraw(uint a) public nonReentrant {
require(bal[msg.sender] >= a);
bal[msg.sender] -= a; // effects first
(bool ok,) = msg.sender.call{value:a}("");
require(ok);
}
tx.origin authentication
Using tx.origin for authorization allows an intermediary contract to act on the user's behalf. Use msg.sender.
Oracle / price manipulation
Reading a spot price from a single pool makes it manipulable by a large swap, often funded with a flash loan. Prefer a time-weighted average price (TWAP) or an aggregated oracle.
Missing access control
A sensitive function without a restricting modifier can be called by anyone. Gate privileged functions with onlyOwner or role checks.
Unchecked external call
Ignoring a low-level call's return value lets the contract continue after a failed transfer. Always check the boolean result.
Business logic flaws
An inverted comparison or wrong share math breaks an invariant. These flaws are contract-specific, escape generic detectors, and are often the costliest. Describe the intended behavior and compare it to the code.
Want to practice exploiting these safely? See Learn. Ready to scan your own contract? Open the scanner.