Re-Entrancy
// Vulnerable Contract
contract EtherStore {
mapping(address => uint) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw() public {
uint bal = balances[msg.sender];
require(bal > 0);
(bool sent, ) = msg.sender.call{value: bal}("");
require(sent, "Failed to send Ether");
balances[msg.sender] = 0;
}
// Helper function to check the balance of this contract
function getBalance() public view returns (uint) {
return address(this).balance;
}
}
// Fixed Contract
contract EtherStore {
mapping(address => uint) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw() public {
// check
uint bal = balances[msg.sender];
require(bal > 0);
// effect
balances[msg.sender] = 0;
// external calls
(bool sent, ) = msg.sender.call{value: bal}("");
require(sent, "Failed to send Ether");
}
// Helper function to check the balance of this contract
function getBalance() public view returns (uint) {
return address(this).balance;
}
}
Two Rights Might Make A Wrong - Paradigm
function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results) {
successes = new bool[](calls.length);
results = new bytes[](calls.length);
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(calls[i]);
require(success || !revertOnFail, _getRevertMsg(result));
successes[i] = success;
results[i] = result;
}
}
Good Security Blogs