Receiving Ether
A contract can receive ETH in three ways: via a payable function, a receive() function, or a fallback() function.
solidity
contract EtherReceiver {
// Called when ETH is sent with no calldata (plain ETH transfer)
receive() external payable {
// msg.value is the amount of ETH received
emit Received(msg.sender, msg.value);
}
// Called when ETH is sent with calldata that doesn't match any function
fallback() external payable {
// Handle unexpected calls
}
// Regular payable function
function deposit() external payable {
require(msg.value > 0, 'Send ETH');
}
event Received(address indexed from, uint256 amount);
}Sending Ether out
There are three methods to send ETH from a contract. Use call() — the other two are outdated.
solidity
// RECOMMENDED: call() — forwards all available gas, returns success flag
(bool success, ) = recipient.call{value: amount}('');
require(success, 'Transfer failed');
// DEPRECATED: transfer() — hard-coded 2300 gas limit, throws on failure
payable(recipient).transfer(amount);
// DEPRECATED: send() — hard-coded 2300 gas limit, returns bool (easy to misuse)
bool sent = payable(recipient).send(amount);The 2300 gas limit on transfer() and send() was introduced as a reentrancy protection, but Istanbul and later EIPs changed gas costs in ways that can cause legitimate contracts to fail with this limit. Always use call() and implement reentrancy protection explicitly.
A safe withdrawal pattern
The recommended way to handle ETH withdrawals is the "pull over push" pattern: users withdraw their own funds rather than the contract pushing ETH to them.
solidity
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
contract SafeBank is ReentrancyGuard {
mapping(address => uint256) public pendingWithdrawals;
function deposit() external payable {
pendingWithdrawals[msg.sender] += msg.value;
}
function withdraw() external nonReentrant {
uint256 amount = pendingWithdrawals[msg.sender];
require(amount > 0, 'Nothing to withdraw');
// Zero out before sending — critical for reentrancy safety
pendingWithdrawals[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}('');
require(success, 'Transfer failed');
}
}Checking contract balance
solidity
// ETH balance of this contract
uint256 contractBalance = address(this).balance;
// ETH balance of any address
uint256 someBalance = address(someAddress).balance;