EIP-7761: EXTCODETYPE instruction
Add EXTCODETYPE instruction to EOF to address common uses of EXTCODE* instructions
Abstract
Allow EOF contracts to discriminate between EOAs (Externally Owned Accounts) and contract accounts by introducing an EXTCODETYPE
instruction.
Motivation
EOFv1 as scoped in EIP-7692 removes code introspection capabilities from the EVM, including the EXTCODESIZE
instruction (in EIP-3540). This makes it hard for ERC-721 and ERC-1155 standard contracts to be implemented, as they rely on discovering whether a token's safeTransfer
call target was an EOA or a contract account:
safeTransfers
to EOAs succeedsafeTransfers
to contract accounts call anonERC721Received
(onERC1155Received
) on them and expect to get a special magic return value, otherwise the transfer reverts (on the assumption that such a recipient may not be able to interact with the token)
Application and library developers are also concerned about dynamic proxies written in EOF accidentally pointing to legacy accounts which cannot safely be called with EXTDELEGATECALL
. They would like a way to differentiate between EOF and legacy contracts.
EXTCODETYPE
is aimed to fill this gap and bring back the possibility to easily implement ERC-721 and ERC-1155 standard contracts in EOF as well as preserve EOF proxy contract safety.
Specification
Parameters
Constant | Value |
---|---|
FORK_BLKNUM | tbd |
GAS_COLD_ACCOUNT_ACCESS | Defined as 2600 in the [Ethereum Execution Layer Spec Constants] |
GAS_WARM_ACCESS | Defined as 100 in the [Ethereum Execution Layer Spec Constants] |
TYPE_NONE | 0 |
TYPE_LEGACY_CONTRACT | 1 |
TYPE_EOF_CONTRACT | 2 |
We introduce a new EOFv1 instruction on the block number FORK_BLKNUM
: EXTCODETYPE
(0xe9
)
EOF code which contains this instruction before FORK_BLKNUM
is considered invalid. Beginning with block FORK_BLKNUM
0xe9
is added to the set of valid EOFv1 instructions.
Execution Semantics
EXTCODETYPE
- deduct
GAS_WARM_ACCESS
gas - pop 1 argument
target_address
from the stack - if
target_address
has any of the high 12 bytes set to a non-zero value (i.e. it does not contain a 20-byte address), then halt with an exceptional failure- Notice: Future expansion of the EVM address space may remove or reduce the number of bytes in this check.
- deduct
GAS_COLD_ACCOUNT_ACCESS - GAS_WARM_ACCESS
iftarget_address
is not inaccessed_addresses
and addtarget_address
toaccessed_addresses
- Load the code from
target_address
and refer to it asloaded_code
- if
loaded_code
is empty (does not exist or has zero length), pushTYPE_NONE
onto the stack and stop processing the operation - If
loaded_code
indicates a delegation designator (for example,0xef0100
as defined in EIP-7702),- replace
loaded_code
with the delegated code. - deduct gas in accordance with the delegation designation rules
- replace
- If
loaded_code
indicates an EOFv1 packaged contract (starts with the bytes0xef0001
) pushTYPE_EOF_CONTRACT
onto the stack and stop processing the operation - Otherwise, push
TYPE_LEGACY_CONTRACT
onto the stack and stop processing the operation
Note: if there is not enough gas to deduct for delegation designation the whole message frame will halt, making updating the accessed_addresses
irrelevant.
Note: If target_address
or the delegation designator points to an account with a contract mid-creation then the code is empty and returns 0
(TYPE_NONE
). This is aligned with similar behavior of instructions like EXTCODESIZE
.
Rationale
Alternative solutions
There have been other solutions proposed to alleviate the problems related to lack of code introspection required for ERC-721 and ERC-1155 standards:
- Extra status code for
EXT*CALL
instruction - allowing to discriminate a result coming from calling an EOA - Extra argument for
EXT*CALL
(a "fail if EOA" flag) - Two return values from
EXT*CALL
(status code + whether it was EOA) EXT*CALL
setting a newcallstatus
register (+ a newCALLSTATUS
instruction)- Re-enable
EXTCODESIZE
in EOF, keeping its behavior same as in legacy
EXTCODETYPE
has been chosen as the most elegant and minimal solution satisfying the requirements at hand and still able to be introduced in EOFv1.
Reuse the 0x3b
(EXTCODESIZE
) opcode for EXTCODETYPE
A new opcode is preferred by a general policy to not reuse opcodes. Also EXTCODETYPE
can be rolled out in legacy EVM if desired.
Keep code introspection banned
Removing code introspection is one of the tenets of EOF and EXTCODETYPE
would be an exception from the principle. Without EXTCODETYPE
, ERC-721 and ERC-1155 standard implementations have to resort to either:
- Leveraging a "booster contract" which would be legacy and would call
EXTCODESIZE
for them. This has been deemed inelegant and inconvenient from the point of view of library implementers, requiring them to hard code an address of such a contract (with the usual address-related problems arising on different EVM chains) - Continuing to use legacy EVM themselves. This is suboptimal, since EVM compilers are likely to at some point deprecate legacy EVM as compilation target
- Updating the standards to not rely on code introspection patterns in
safeTransfer
safeguards. This can be accomplished for example by leveraging ERC-165, leaving out only contracts which do not implement ERC-165, and at the same time have non-throwing fallback functions, as indistinguishable from EOAs. This is not easy to achieve since ERC-721 and ERC-1155 are Final and adopted in practice.
"Endgame Account Abstraction" issues
EXTCODETYPE
(and earlier EXTCODESIZE
available in legacy EVM) are claimed to slow down AA adoption, because they encourage patterns which discriminate between smart contract and EOA accounts, e.g. not allowing the former to interact. However, there are counterarguments that it is up to other factors which slow down AA adoption (assumption that accounts can produce ECDSA signatures, and the lack of adoption of smart contract signatures).
Including safeguarding against proxy bricking
In parallel to the ERC-721 / ERC-1155 problem, another potential risk has been brought to attention. Since EOFv1 prohibits EXTDELEGATECALL
targeting legacy contracts, there exists a scenario where an EOF proxy contract accidentally upgrades its implementation to a legacy EVM one. Since reverting this or upgrading again (using current proxy standards) requires the implementation contract to be called, it would effectively render the contract unusable.
For this reason it was decided to have a generalized EXTCODETYPE
instruction instead of a HASCODE
instruction. This provides the means to safeguard against such a scenario that a simpler instruction could not.
Relation to EIP-7702 "Set EOA account code"
After EIP-7702 is activated, the discrimination between EOAs and contract accounts using EXTCODESIZE
(or EXTCODETYPE
) has an edge case: Whenever an EOA sets its code to a contract account which does not respond as expected to an onERC721Received
(onERC1155Received
) callback, transfers to it will revert, despite the recipient being able to interact with the token. This has been deemed unlikely to be a problem, as for the intended real-world uses of EIP-7702, those callbacks will be implemented by designator codes.
Because of the requirement that EOF proxy contracts be able to determine if the new target is safe we cannot return a value for indicating that the account itself is delegated without also providing the delegated address. Instead, we return the code for the account that has the delegated code, applying the delegation. This is because the executable code is what matters for proxy updates.
This differs from EXTCODECOPY
and EXTCODEHASH
behavior which do return a hash and short code indicating the proxy. The behavior of EXTCODETYPE
is closer to a "code executing operation" in purpose as it is meant to describe the execution behavior of the account.
Backwards Compatibility
EXTCODETYPE
at 0xe9
can be introduced in a backwards compatible manner into EOFv1 (no bump to version), because 0xe9
has been rejected by EOF validation before FORK_BLKNUM
and there are no EOF contracts on-chain with a 0xe9
which would have their behavior altered.
Security Considerations
Needs discussion
Copyright
Copyright and related rights waived via CC0
[Ethereum Execution Layer Spec Constants] :https://github.com/ethereum/execution-specs/blob/1adcc1bfe774798bcacc685aebc17bd9935078c3/src/ethereum/cancun/vm/gas.py#L65-L66