Skip to content

Commit 3357ca7

Browse files
committed
Document function type stability across code changes
1 parent 444103c commit 3357ca7

5 files changed

+58
-0
lines changed

docs/control-structures.rst

+2
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ to limit the amount of gas.
239239
If the creation fails (due to out-of-stack, not enough balance or other problems),
240240
an exception is thrown.
241241

242+
.. _salted-contract-creations:
243+
242244
Salted contract creations / create2
243245
-----------------------------------
244246

docs/introduction-to-smart-contracts.rst

+1
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ operations, loops should be preferred over recursive calls. Furthermore,
537537
only 63/64th of the gas can be forwarded in a message call, which causes a
538538
depth limit of a little less than 1000 in practice.
539539

540+
.. _delegatecall:
540541
.. index:: delegatecall, library
541542

542543
Delegatecall and Libraries

docs/ir-breaking-changes.rst

+2
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ hiding new and different behavior in existing code.
258258
Internals
259259
=========
260260

261+
.. _internal-function-pointers-in-ir:
262+
261263
Internal function pointers
262264
--------------------------
263265

docs/security-considerations.rst

+10
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,16 @@ If your ``mapping`` information must be deleted, consider using a library simila
371371
`iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_,
372372
allowing you to traverse the keys and delete their values in the appropriate ``mapping``.
373373

374+
Internal Function Pointers in Upgradeable Contracts
375+
===================================================
376+
377+
Updating the code of your contract may :ref:`invalidate the values of variables of internal function
378+
types<function-type-value-stability-across-contract-updates>`.
379+
Consider such values ephemeral and avoid storing them in state variables.
380+
If you do, you must ensure that they never persist across code updates and are never used by
381+
other contracts having access to the same storage space as a result of a delegatecall or account
382+
abstraction.
383+
374384
Minor Details
375385
=============
376386

docs/types/value-types.rst

+43
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,49 @@ External (or public) functions have the following members:
851851
respectively. See :ref:`External Function Calls <external-function-calls>` for
852852
more information.
853853

854+
.. _function-type-value-stability-across-contract-updates:
855+
856+
Value stability across contract updates
857+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
858+
859+
An important aspect to consider when using values of function types is whether the value will
860+
remain valid if the underlying code changes.
861+
862+
The state of the blockchain is not completely immutable and there are multiple ways to place
863+
different code under the same address:
864+
865+
- Directly deploying different code using :ref:`salted contract creation<salted-contract-creations>`.
866+
- Delegating to a different contract via :ref:`DELEGATECALL<delegatecall>`
867+
(upgradeable code behind a proxy contract is a common example of this).
868+
- Account abstraction as defined by `EIP-7702 <https://eips.ethereum.org/EIPS/eip-7702>`_.
869+
870+
External function types can be considered as stable as contract's ABI, which makes them very portable.
871+
Their ABI representation always consists of a contract address and a function selector and it is
872+
perfectly safe to store them long-term or pass them between contracts.
873+
While it is possible for the referenced function to change or disappear, a direct external call
874+
would be affected the same way, so there is no additional risk in such use.
875+
876+
In case of internal functions, however, the value is an identifier that is strongly tied to
877+
contract's bytecode.
878+
The actual representation of the identifier is an implementation detail and may change between
879+
compiler versions or even :ref:`between different backends<internal-function-pointers-in-ir>`.
880+
Values assigned under a given representation are deterministic (i.e. guaranteed to remain the same
881+
as long as the source code is the same) but are easily affected by changes such as adding, removing
882+
or reordering of functions.
883+
The compiler is also free to remove internal functions that are never used, which may affect other identifiers.
884+
Some representations, e.g. one where identifiers are simply jump targets, may be affected by
885+
virtually any change, even one completely unrelated to internal functions.
886+
887+
To counter this, the language limits the use of internal function types outside of the context in
888+
which they are valid.
889+
This is why internal function types cannot be used as parameters of external functions (or in any
890+
other way that is exposed in contract's ABI).
891+
However, there are still situations where it is up to the user to decide whether their use is safe or not.
892+
For example long-term storage of such values in state variables is discouraged, but may be safe if
893+
the contract code is never going to be updated.
894+
It is also always possible to side-step any safeguards by using inline assembly.
895+
Such use always needs careful consideration.
896+
854897
Examples
855898
^^^^^^^^
856899

0 commit comments

Comments
 (0)