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 aboolto 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
sendandtransferis NOT Recommended any more after the Istanbul hard fork due to the increased gas cost of certain opcodes. It is recommended to usecallinstead.Note: the raw
callcalling 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