EIP-7761 - EXTCODETYPE instruction

Created 2024-09-01
Status Draft
Category Core
Type Standards Track
Authors
Requires

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:

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

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:

  1. Extra status code for EXT*CALL instruction - allowing to discriminate a result coming from calling an EOA
  2. Extra argument for EXT*CALL (a "fail if EOA" flag)
  3. Two return values from EXT*CALL (status code + whether it was EOA)
  4. EXT*CALL setting a new callstatus register (+ a new CALLSTATUS instruction)
  5. 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:

  1. 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)
  2. Continuing to use legacy EVM themselves. This is suboptimal, since EVM compilers are likely to at some point deprecate legacy EVM as compilation target
  3. 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