mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Split fallback function and introduce "fallback()" and "receive()" syntax.
This commit is contained in:
@@ -222,23 +222,23 @@ This behaviour is also in line with the ``STATICCALL`` opcode.
|
||||
not do state-changing operations, but it cannot check that the contract that will be called
|
||||
at runtime is actually of that type.
|
||||
|
||||
.. index:: ! fallback function, function;fallback
|
||||
.. index:: ! receive ether function, function;receive ! receive
|
||||
|
||||
.. _fallback-function:
|
||||
.. _receive-ether-function:
|
||||
|
||||
Fallback Function
|
||||
=================
|
||||
Receive Ether Function
|
||||
======================
|
||||
|
||||
A contract can have exactly one unnamed function. This function cannot have
|
||||
arguments, cannot return anything and has to have ``external`` visibility.
|
||||
It is executed on a call to the contract if none of the other
|
||||
functions match the given function identifier (or if no data was supplied at
|
||||
all).
|
||||
|
||||
Furthermore, this function is executed whenever the contract receives plain
|
||||
Ether (without data). To receive Ether and add it to the total balance of the contract, the fallback function
|
||||
must be marked ``payable``. If no such function exists, the contract cannot receive
|
||||
Ether through regular transactions and throws an exception.
|
||||
A contract can have at most one ``receive`` function, declared using ``receive() external payable``.
|
||||
This function cannot have arguments, cannot return anything and must have
|
||||
``external`` visibility and ``payable`` state mutability. It is executed on a
|
||||
call to the contract with empty calldata. This is the function that is executed
|
||||
on plain Ether transfers (e.g. via `.send()` or `.transfer()`). If no such
|
||||
function exists, but a payable :ref:`fallback function <fallback-function>`
|
||||
exists, the fallback function will be called on a plain Ether transfer. If
|
||||
neither a receive Ether nor a payable fallback function is present, the
|
||||
contract cannot receive Ether through regular transactions and throws an
|
||||
exception.
|
||||
|
||||
In the worst case, the fallback function can only rely on 2300 gas being
|
||||
available (for example when `send` or `transfer` is used), leaving little
|
||||
@@ -250,33 +250,64 @@ will consume more gas than the 2300 gas stipend:
|
||||
- Calling an external function which consumes a large amount of gas
|
||||
- Sending Ether
|
||||
|
||||
.. warning::
|
||||
Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``)
|
||||
but do not define a receive Ether function or a payable fallback function
|
||||
throw an exception, sending back the Ether (this was different
|
||||
before Solidity v0.4.0). So if you want your contract to receive Ether,
|
||||
you have to implement a receive Ether function (using payable fallback functions for receiving Ether is
|
||||
not recommended, since it would not fail on interface confusions).
|
||||
|
||||
|
||||
.. warning::
|
||||
A contract without a receive Ether function can receive Ether as a recipient of a `coinbase transaction` (aka `miner block reward`)
|
||||
or as a destination of a ``selfdestruct``.
|
||||
|
||||
A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it.
|
||||
|
||||
It also means that ``address(this).balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the receive Ether function).
|
||||
|
||||
Below you can see an example of a Sink contract that uses function ``receive``.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.7.0;
|
||||
|
||||
// This contract keeps all Ether sent to it with no way
|
||||
// to get it back.
|
||||
contract Sink {
|
||||
receive() external payable { }
|
||||
}
|
||||
|
||||
.. index:: ! fallback function, function;fallback
|
||||
|
||||
.. _fallback-function:
|
||||
|
||||
Fallback Function
|
||||
=================
|
||||
|
||||
A contract can have at most one ``fallback`` function, declared using ``fallback () external [payable]``.
|
||||
This function cannot have arguments, cannot return anything and must have ``external`` visibility.
|
||||
It is executed on a call to the contract if none of the other
|
||||
functions match the given function signature, or if no data was supplied at
|
||||
all and there is no :ref:`receive Ether function <receive-ether-function>`.
|
||||
The fallback function always receives data, but in order to also receive Ether
|
||||
it must be marked ``payable``.
|
||||
|
||||
In the worst case, if a payable fallback function is also used in place of a receive function, it can only rely on 2300 gas being
|
||||
available (see :ref:`receive Ether function <receive-ether-function>` for a brief description of the implications of this).
|
||||
|
||||
Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it.
|
||||
|
||||
.. warning::
|
||||
The fallback function is also executed if the caller meant to call
|
||||
a function that is not available. If you want to implement the fallback
|
||||
function only to receive ether, you should add a check
|
||||
like ``require(msg.data.length == 0)`` to prevent invalid calls.
|
||||
|
||||
.. warning::
|
||||
Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``)
|
||||
but do not define a fallback function
|
||||
throw an exception, sending back the Ether (this was different
|
||||
before Solidity v0.4.0). So if you want your contract to receive Ether,
|
||||
you have to implement a payable fallback function.
|
||||
|
||||
.. warning::
|
||||
A contract without a payable fallback function can receive Ether as a recipient of a `coinbase transaction` (aka `miner block reward`)
|
||||
or as a destination of a ``selfdestruct``.
|
||||
A ``payable`` fallback function is also executed for plain Ether transfers, if no :ref:`receive Ether function <receive-ether-function>`
|
||||
is present. It is recommended to always define a receive Ether function as well, if you define a payable fallback function
|
||||
to distinguish Ether transfers from interface confusions.
|
||||
|
||||
.. note::
|
||||
Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve
|
||||
any payload supplied with the call.
|
||||
|
||||
A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it.
|
||||
|
||||
It also means that ``address(this).balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the fallback function).
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.7.0;
|
||||
@@ -287,15 +318,23 @@ Like any function, the fallback function can execute complex operations as long
|
||||
// Sending Ether to this contract will cause an exception,
|
||||
// because the fallback function does not have the `payable`
|
||||
// modifier.
|
||||
function() external { x = 1; }
|
||||
fallback() external { x = 1; }
|
||||
uint x;
|
||||
}
|
||||
|
||||
contract TestPayable {
|
||||
// This function is called for all messages sent to
|
||||
// this contract, except plain Ether transfers
|
||||
// (there is no other function except the receive function).
|
||||
// Any call with non-empty calldata to this contract will execute
|
||||
// the fallback function (even if Ether is sent along with the call).
|
||||
fallback() external payable { x = 1; y = msg.value; }
|
||||
|
||||
// This contract keeps all Ether sent to it with no way
|
||||
// to get it back.
|
||||
contract Sink {
|
||||
function() external payable { }
|
||||
// This function is called for plain Ether transfers, i.e.
|
||||
// for every call with empty calldata.
|
||||
receive() external payable { x = 2; y = msg.value; }
|
||||
uint x;
|
||||
uint y;
|
||||
}
|
||||
|
||||
contract Caller {
|
||||
@@ -305,14 +344,27 @@ Like any function, the fallback function can execute complex operations as long
|
||||
// results in test.x becoming == 1.
|
||||
|
||||
// address(test) will not allow to call ``send`` directly, since ``test`` has no payable
|
||||
// fallback function. It has to be converted to the ``address payable`` type via an
|
||||
// intermediate conversion to ``uint160`` to even allow calling ``send`` on it.
|
||||
address payable testPayable = address(uint160(address(test)));
|
||||
// fallback function.
|
||||
// It has to be converted to the ``address payable`` type to even allow calling ``send`` on it.
|
||||
address payable testPayable = payable(address(test));
|
||||
|
||||
// If someone sends ether to that contract,
|
||||
// If someone sends Ether to that contract,
|
||||
// the transfer will fail, i.e. this returns false here.
|
||||
return testPayable.send(2 ether);
|
||||
}
|
||||
|
||||
function callTestPayable(TestPayable test) public returns (bool) {
|
||||
(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
|
||||
require(success);
|
||||
// results in test.x becoming == 1 and test.y becoming 0.
|
||||
(success,) = address(test).call.value(1)(abi.encodeWithSignature("nonExistingFunction()"));
|
||||
require(success);
|
||||
// results in test.x becoming == 1 and test.y becoming 1.
|
||||
|
||||
// If someone sends Ether to that contract, the receive function in TestPayable will be called.
|
||||
require(address(test).send(2 ether));
|
||||
// results in test.x becoming == 2 and test.y becoming 2 ether.
|
||||
}
|
||||
}
|
||||
|
||||
.. index:: ! overload
|
||||
|
||||
Reference in New Issue
Block a user