Allow EOF contracts to discriminate between EOAs (Externally Owned Accounts) and contract accounts by introducing an EXTCODETYPE
instruction.
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 an onERC721Received
(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.
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.
EXTCODETYPE
GAS_WARM_ACCESS
gastarget_address
from the stacktarget_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 failureGAS_COLD_ACCOUNT_ACCESS - GAS_WARM_ACCESS
if target_address
is not in accessed_addresses
and add target_address
to accessed_addresses
target_address
and refer to it as loaded_code
loaded_code
is empty (does not exist or has zero length), push TYPE_NONE
onto the stack and stop processing the operationloaded_code
indicates a delegation designator (for example, 0xef0100
as defined in EIP-7702), loaded_code
with the delegated code.loaded_code
indicates an EOFv1 packaged contract (starts with the bytes 0xef0001
) push TYPE_EOF_CONTRACT
onto the stack and stop processing the operationTYPE_LEGACY_CONTRACT
onto the stack and stop processing the operationNote: 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
.
There have been other solutions proposed to alleviate the problems related to lack of code introspection required for ERC-721 and ERC-1155 standards:
EXT*CALL
instruction - allowing to discriminate a result coming from calling an EOAEXT*CALL
(a "fail if EOA" flag)EXT*CALL
(status code + whether it was EOA)EXT*CALL
setting a new callstatus
register (+ a new CALLSTATUS
instruction)EXTCODESIZE
in EOF, keeping its behavior same as in legacyEXTCODETYPE
has been chosen as the most elegant and minimal solution satisfying the requirements at hand and still able to be introduced in EOFv1.
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.
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:
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)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.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).
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.
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.
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.
Needs discussion
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