A contract holds ether and must pay it out — say an owner withdrawing funds. How do the three ways to send, transfer, send, and call, differ?
transfer forwards a fixed 2300-gas stipend and reverts on failure; send forwards the same stipend but only returns false; call forwards all remaining gas and returns a success flag you must check yourself — call is the generally recommended choice today.
* Same payout, three mechanisms — the stipend-bound pair differ only in how they fail; call forwards the gas and leaves the checking to you *
payable(msg.sender).transfer(address(this).balance); // reverts on failure
bool sent = payable(msg.sender).send(address(this).balance);
require(sent, "send failed"); // must check yourself
(bool ok, ) = payable(msg.sender).call{value: address(this).balance}("");
require(ok, "call failed"); // recommended
| Method | Gas forwarded | On failure |
|---|---|---|
transfer |
2300 (fixed) | reverts automatically |
send |
2300 (fixed) | returns false — ignoring it swallows the error |
call |
all remaining (adjustable) | returns false — require it |
The 2300-gas stipend of transfer/send is only enough for the recipient to log an event; if the receiver is a contract whose receive() does real work, the payment fails — and because the gas costs of EVM operations get repriced over time, hardcoding that stipend assumption is fragile. call avoids both problems by forwarding whatever gas is left, which is why it is the recommended pattern — paired with the discipline of always checking its success flag, and of updating your own state before the send (forwarding all gas means the recipient can run code — the checks-effects-interactions habit).
Go deeper:
Solidity docs: members of addresses — the formal definitions of
transfer,send, andcallon a payable address.Solidity docs: security considerations — the 2300-gas stipend, re-entrancy, and the checks-effects-interactions pattern.