Sending & Receiving Ether

If neither exists (or fallback isn't payable and Ether is sent), the call reverts.

18.1 Receiving: receive and fallback

If neither exists (or fallback isn't payable and Ether is sent), the call reverts. fallback can also take bytes calldata input and return bytes memory: fallback(bytes calldata input) external payable returns (bytes memory).

A contract can receive Ether without executing code: as the block fee recipient (coinbase), via selfdestruct from another contract, or by being pre-funded at a precomputed address. Never assume address(this).balance equals what your code tracked.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Receiver {
    event Received(address from, uint256 amount);
    event FallbackCalled(address from, uint256 amount, bytes data);

    // Called on plain Ether transfers (empty calldata)
    receive() external payable {
        emit Received(msg.sender, msg.value);
    }

    // Called when no function matches the selector (or calldata non-empty and no receive)
    fallback() external payable {
        emit FallbackCalled(msg.sender, msg.value, msg.data);
    }
}
Ether / call sent to contract
                    │
          is msg.data empty?
            ┌───────┴───────┐
           yes              no
            │                │
     receive() exists?   function selector matches? ── yes ──► that function
       ┌────┴────┐               │ no
      yes        no              ▼
       │          │          fallback()
   receive()   fallback()

18.2 Sending: transfer, send, call

avoid (deprecated direction; breaks with smart wallets) returns false a.call{value: x}("") all remaining (or specified) returns (false, data)

recommended, with reentrancy protection The 2,300-gas stipend of transfer/send was a reentrancy defense, but gas costs change across forks (EIP-1884, EIP-2929) and smart-contract wallets need more gas, so use call plus Checks-Effects-Interactions and/or a reentrancy guard.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Sender {
    error TransferFailed();

    function sendViaCall(address payable to) external payable {
        (bool success, ) = to.call{value: msg.value}("");
        if (!success) revert TransferFailed();
    }
}

18.3 Pull over push

Instead of sending Ether to many users in a loop (one failure blocks all), record balances and let each user withdraw:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract PullPayments {
    mapping(address => uint256) public owed;

    function _credit(address user, uint256 amount) internal { owed[user] += amount; }

    function withdraw() external {
        uint256 amount = owed[msg.sender];
        require(amount > 0, "nothing owed");
        owed[msg.sender] = 0;                         // effects before interaction
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "failed");
    }
}