// pragma solidity ^0.5.0; contract Reentrancy { mapping (address => uint) userBalance; function getBalance(address u) view public returns(uint){ return userBalance[u]; } function addToBalance() payable public{ userBalance[msg.sender] += msg.value; } // Should not detect reentrancy in constructor constructor() public { // send userBalance[msg.sender] ethers to msg.sender // if mgs.sender is a contract, it will call its fallback function (bool ret, bytes memory mem) = msg.sender.call.value(userBalance[msg.sender])(""); if( ! ret ){ revert(); } userBalance[msg.sender] = 0; } function withdrawBalance() public{ // send userBalance[msg.sender] ethers to msg.sender // if mgs.sender is a contract, it will call its fallback function (bool ret, bytes memory mem) = msg.sender.call.value(userBalance[msg.sender])(""); if( ! ret ){ revert(); } userBalance[msg.sender] = 0; } function withdrawBalance_fixed() public{ // To protect against re-entrancy, the state variable // has to be change before the call uint amount = userBalance[msg.sender]; userBalance[msg.sender] = 0; (bool ret, bytes memory mem) = msg.sender.call.value(amount)(""); if( ! ret ){ revert(); } } function withdrawBalance_fixed_2() public{ // send() and transfer() are safe against reentrancy // they do not transfer the remaining gas // and they give just enough gas to execute few instructions // in the fallback function (no further call possible) msg.sender.transfer(userBalance[msg.sender]); userBalance[msg.sender] = 0; } function withdrawBalance_fixed_3() public{ // The state can be changed // But it is fine, as it can only occur if the transaction fails uint amount = userBalance[msg.sender]; userBalance[msg.sender] = 0; (bool ret, bytes memory mem) = msg.sender.call.value(amount)(""); if( ! ret ){ userBalance[msg.sender] = amount; } } }