Stop Using Solidity's transfer() Now | ConsenSys Diligence
Function | Amount of Gas Forwarded | Exception Propagation | Safety |
---|---|---|---|
send | 2300 (not adjustable) | false on failure |
against re-entrancy |
call.value | all remaining gas (adjustable) | false on failure |
flexibility but vulnerable |
transfer | 2300 (not adjustable) | throws on failure | against re-entrancy |
<aside>
💡 Use call.value
+ patterns that eliminate reentrancy
</aside>
A contract receiving Ether must have at least one of the functions below
receive() external payable
fallback() external payable
receive()
is called if msg.data
is empty, otherwise fallback()
is called.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract ReceiveEther {
/*
Which function is called, fallback() or receive()?
send Ether
|
msg.data is empty?
/ \\
yes no
/ \\
receive() exists? fallback()
/ \\
yes no
/ \\
receive() fallback()
*/
// Function to receive Ether. msg.data must be empty
receive() external payable {}
// Fallback function is called when msg.data is not empty
fallback() external payable {}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}
contract SendEther {
function sendViaTransfer(address payable _to) public payable {
// This function is no longer recommended for sending Ether.
_to.transfer(msg.value);
}
function sendViaSend(address payable _to) public payable {
// Send returns a boolean value indicating success or failure.
// This function is not recommended for sending Ether.
bool sent = _to.send(msg.value);
require(sent, "Failed to send Ether");
}
function sendViaCall(address payable _to) public payable {
// Call returns a boolean value indicating success or failure.
// This is the current recommended method to use.
(bool sent, bytes memory data) = _to.call{value: msg.value}("");
require(sent, "Failed to send Ether");
}
}
make sure that all your interactions (external calls) happen at the end.
contract Fixed {
...
function withdraw() external {
// 1. Checks
require(balanceOf[msg.sender] > 0);
// 2. Effects
uint256 amount = balanceOf[msg.sender];
balanceOf[msg.sender] = 0;
// 3. Interactions
(bool success, ) = msg.sender.call.value(amount)("");
require(success, "Transfer failed.");
}
}