---
eip: 7761
title: EXTCODETYPE instruction
description: Add EXTCODETYPE instruction to EOF to address common uses of EXTCODE* instructions
author: Andrei Maiboroda (@gumb0), Piotr Dobaczewski (@pdobacz), Danno Ferrin (@shemnon)
discussions-to: https://ethereum-magicians.org/t/eip-7761-is-contract-instruction/20936
status: Stagnant
type: Standards Track
category: Core
created: 2024-09-01
requires: 3540, 7692
---

## 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 succeed
- `safeTransfers` 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.



## 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` if `target_address` is not in `accessed_addresses` and add `target_address` to `accessed_addresses`
- Load the code from `target_address` and refer to it as `loaded_code` 
- if `loaded_code` is empty (does not exist or has zero length), push `TYPE_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
- If `loaded_code` indicates an EOFv1 packaged contract (starts with the bytes `0xef0001`) push `TYPE_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:

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 <!-- TODO -->

## Copyright

Copyright and related rights waived via [CC0]

[CC0]: ../LICENSE.md
[ERC-165]: ./eip-165.md
[ERC-721]: ./eip-721.md
[ERC-1155]: ./eip-1155.md
[EIP-3540]: ./eip-3540.md
[EIP-7702]: ./eip-7702.md
[EIP-7692]: ./eip-7692.md
[Ethereum Execution Layer Spec Constants] :https://github.com/ethereum/execution-specs/blob/1adcc1bfe774798bcacc685aebc17bd9935078c3/src/ethereum/cancun/vm/gas.py#L65-L66
