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:
@@ -437,7 +437,7 @@ New version:
|
||||
function f(uint y) external {
|
||||
x = y;
|
||||
}
|
||||
function() payable external {}
|
||||
receive() payable external {}
|
||||
}
|
||||
|
||||
contract New {
|
||||
|
||||
@@ -39,6 +39,15 @@ This section highlights changes that affect syntax and semantics.
|
||||
that can hold both the type of the base and the type of the exponent, as with symmentric
|
||||
operations. Additionally, signed types are allowed for the base of the exponetation.
|
||||
|
||||
* The unnamed function commonly referred to as "fallback function" was split up into a new
|
||||
fallback function that is defined using the ``fallback`` keyword and a receive ether function
|
||||
defined using the ``receive`` keyword. If present, the receive ether function is called
|
||||
whenever the call data is empty. The new fallback function is called when no
|
||||
other function matches. It can be payable in which case it may accept value
|
||||
or non-payable in which case transactions not matching any other function
|
||||
which send value will revert. If you only implement the receive and not the fallback function, calling a non-existing function on your contract in error is not possible anymore. Unless you are following an upgrade or proxy
|
||||
pattern, you should not need to implement the fallback function.
|
||||
|
||||
|
||||
How to update your code
|
||||
=======================
|
||||
@@ -47,6 +56,8 @@ This section gives detailed instructions on how to update prior code for every b
|
||||
|
||||
* Change ``address(f)`` to ``f.address`` for ``f`` being of external function type.
|
||||
|
||||
* Replace ``function () external [payable] { ... }`` by either ``receive() external payable { ... }``, ``fallback() external [payable] { ... }`` or both. Prefer using a ``receive`` function only, whenever possible.
|
||||
|
||||
* Change ``uint length = array.push(value)`` to ``array.push(value);``. The new length can be
|
||||
accessed via ``array.length``.
|
||||
|
||||
|
||||
+2
-2
@@ -444,8 +444,8 @@ JSON
|
||||
The JSON format for a contract's interface is given by an array of function and/or event descriptions.
|
||||
A function description is a JSON object with the fields:
|
||||
|
||||
- ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function <fallback-function>`).
|
||||
- ``name``: the name of the function.
|
||||
- ``type``: ``"function"``, ``"constructor"``, ``"receive"`` (the :ref:`"receive Ether" function <receive-ether-function>`) or ``"fallback"`` (the :ref:`"default" function <fallback-function>`);
|
||||
- ``name``: the name of the function;
|
||||
- ``inputs``: an array of objects, each of which contains:
|
||||
|
||||
* ``name``: the name of the parameter.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -224,7 +224,7 @@ Now someone tricks you into sending ether to the address of this attack wallet:
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function() external {
|
||||
fallback() external {
|
||||
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-5
@@ -273,6 +273,7 @@ Ordering helps readers identify which functions they can call and to find the co
|
||||
Functions should be grouped according to their visibility and ordered:
|
||||
|
||||
- constructor
|
||||
- receive function (if exists)
|
||||
- fallback function (if exists)
|
||||
- external
|
||||
- public
|
||||
@@ -290,7 +291,11 @@ Yes::
|
||||
// ...
|
||||
}
|
||||
|
||||
function() external {
|
||||
receive() external payable {
|
||||
// ...
|
||||
}
|
||||
|
||||
fallback() external {
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -322,7 +327,10 @@ No::
|
||||
// External functions
|
||||
// ...
|
||||
|
||||
function() external {
|
||||
fallback() external {
|
||||
// ...
|
||||
}
|
||||
receive() external payable {
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -384,20 +392,29 @@ No::
|
||||
y = 2;
|
||||
long_variable = 3;
|
||||
|
||||
Don't include a whitespace in the fallback function:
|
||||
Don't include a whitespace in the receive and fallback functions:
|
||||
|
||||
Yes::
|
||||
|
||||
function() external {
|
||||
receive() external payable {
|
||||
...
|
||||
}
|
||||
|
||||
fallback() external {
|
||||
...
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
function () external {
|
||||
receive () external payable {
|
||||
...
|
||||
}
|
||||
|
||||
fallback () external {
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
Control Structures
|
||||
==================
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ or if the Ether transfer is rejected by the receiving account. The ``transfer``
|
||||
reverts on failure.
|
||||
|
||||
.. note::
|
||||
If ``x`` is a contract address, its code (more specifically: its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception.
|
||||
If ``x`` is a contract address, its code (more specifically: its :ref:`receive-ether-function`, if present, or otherwise its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception.
|
||||
|
||||
* ``send``
|
||||
|
||||
|
||||
Reference in New Issue
Block a user