A solidity contract can both send and receive ether if the contract satisfies certain interfaces.

Sender side

A contract can send ether by:

  • receiver.call{value: amount}(""), which will return a (bool, bytes memory) tuple to indicate success or failure.
  • receiver.send(amount), which will return a bool to indicate success or failure.
  • receiver.transfer(amount), which will throws error if payment fails.

The send and transfer methods will forward a gas stipend of 2300 gas to the receiving contract since the receive need to execute the receive or fallback function. And the limited gas is only enough to log an event or write to storage to prevent potential attacks.

Warning

send and transfer is NOT Recommended any more after the Istanbul hard fork due to the increased gas cost of certain opcodes. It is recommended to use call instead.

Note: the raw call calling does not have the 2300 gas limitation to prevent reentrancy attacks, you need to take very careful when using it. Check Safe Transfer for the best practice.

Receiver side

flowchart LR
    A["send Ether"] --> B{"msg.data is empty?"}
    B -- "yes" --> C{"receive() exists?"}
    B -- "no" --> E["fallback()"]
    C -- "yes" --> D["receive()"]
    C -- "no" --> E