Skip to content

Commit

Permalink
Add EIP: IS_CONTRACT instruction
Browse files Browse the repository at this point in the history
  • Loading branch information
pdobacz committed Aug 29, 2024
1 parent c046f72 commit 8f785fb
Showing 1 changed file with 106 additions and 0 deletions.
106 changes: 106 additions & 0 deletions EIPS/eip-is-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
title: IS_CONTRACT instruction
description: Introduce IS_CONTRACT instruction
author: Andrei Maiboroda (@gumb0), Piotr Dobaczewski (@pdobacz), Danno Ferrin (@shemnon)
discussions-to: <URL>
status: Draft
type: Standards Track
category: Core
created: 2024-09-01
requires: 3540, 7692
---

## Abstract

Allow EOF contracts to discriminate between EOA (Externally Owned Accounts) and smart contract accounts by introducing an `IS_CONTRACT` instruction.

## Motivation

EOFv1 as scoped in [EIP-7692](./eip-7692.md) removes code introspection capabilities from the EVM, including the `EXTCODESIZE` instruction. 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 smart contract:

Check failure on line 19 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 19 | EOFv1 as scoped in [EIP-7692](./eip-7692.md) removes code introspection capabilities from the EVM, including the `EXTCODESIZE` instruction. 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 smart contract: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+` = help: see https://ethereum.github.io/eipw/markdown-link-first/

Check failure on line 19 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 19 | EOFv1 as scoped in [EIP-7692](./eip-7692.md) removes code introspection capabilities from the EVM, including the `EXTCODESIZE` instruction. 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 smart contract: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 19 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 19 | EOFv1 as scoped in [EIP-7692](./eip-7692.md) removes code introspection capabilities from the EVM, including the `EXTCODESIZE` instruction. 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 smart contract: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+` = help: see https://ethereum.github.io/eipw/markdown-link-first/

Check failure on line 19 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 19 | EOFv1 as scoped in [EIP-7692](./eip-7692.md) removes code introspection capabilities from the EVM, including the `EXTCODESIZE` instruction. 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 smart contract: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

- `safeTransfers` to EOA succeed
- `safeTransfers` to smart contracts 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 receipient may not be able to interact with the token)

`IS_CONTRACT` is aimed to fill this gap and bring back the possibility to easily implement ERC-721 and ERC-1155 standard contracts in EOF.

Check failure on line 24 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 24 | `IS_CONTRACT` is aimed to fill this gap and bring back the possibility to easily implement ERC-721 and ERC-1155 standard contracts in EOF. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 24 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 24 | `IS_CONTRACT` is aimed to fill this gap and bring back the possibility to easily implement ERC-721 and ERC-1155 standard contracts in EOF. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 24 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 24 | `IS_CONTRACT` is aimed to fill this gap and bring back the possibility to easily implement ERC-721 and ERC-1155 standard contracts in EOF. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 24 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 24 | `IS_CONTRACT` is aimed to fill this gap and bring back the possibility to easily implement ERC-721 and ERC-1155 standard contracts in EOF. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

## Specification

### Parameters

| Constant | Value |
| - | - |
| `FORK_BLKNUM` | tbd |
| `GAS_COLD_ACCOUNT_ACCESS` | Defined as `2600` in the [Ethereum Execution Layer Specs](https://github.com/ethereum/execution-specs/blob/fcd12750edd4443a91f138728689a1d0a503a7c1/src/ethereum/cancun/vm/gas.py#L64) |
| `GAS_WARM_ACCESS` | Defined as `100` in the [Ethereum Execution Layer Specs](https://github.com/ethereum/execution-specs/blob/fcd12750edd4443a91f138728689a1d0a503a7c1/src/ethereum/cancun/vm/gas.py#L65) |

We introduce a new EOFv1 instruction on the block number `FORK_BLKNUM`: `IS_CONTRACT` (`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

#### `IS_CONTRACT`

- 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
- deduct `GAS_COLD_ACCOUNT_ACCESS - GAS_WARM_ACCESS` if `target_address` is not in `accessed_addresses` and add `target_address` to `accessed_addresses`
- push `1` onto the stack if `target_address.code` is not empty, `0` otherwise

If `target_address.code` contains an [EIP-7702](./eip-7702.md) delegation, the result of `IS_CONTRACT` should follow the delegation and return a result according the delegation designator account. Additional gas costs and rules concering `accessed_addresses` apply as specified in EIP-7702 for code reading instructions like `EXTCODESIZE`.

If `target_address` points to an account with a contract mid-creation, it behaves aligned with similar instructions like `EXTCODESIZE` and returns `0`.

## 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:

Check failure on line 58 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 58 | There have been other solutions proposed to alleviate the problems related to lack of code introspection required for ERC-721 and ERC-1155 standards: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 58 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 58 | There have been other solutions proposed to alleviate the problems related to lack of code introspection required for ERC-721 and ERC-1155 standards: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 58 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 58 | There have been other solutions proposed to alleviate the problems related to lack of code introspection required for ERC-721 and ERC-1155 standards: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 58 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 58 | There have been other solutions proposed to alleviate the problems related to lack of code introspection required for ERC-721 and ERC-1155 standards: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

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. Reenable `EXTCODESIZE` in EOF, keeping its behavior same as in legacy

`IS_CONTRACT` 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 `IS_CONTRACT`

A new opcode is prefered by a general policy to not reuse opcodes. Also `IS_CONTRACT` can be rolled out in legacy EVM if desired.

### Keep code introspection banned

Removing code introspection is one of the tenets of EOF and `IS_CONTRACT` would be an exception from the principle. Without `IS_CONTRACT`, ERC-721 and ERC-1155 standard implementations have to resort to either:

Check failure on line 74 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 74 | Removing code introspection is one of the tenets of EOF and `IS_CONTRACT` would be an exception from the principle. Without `IS_CONTRACT`, ERC-721 and ERC-1155 standard implementations have to resort to either: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 74 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 74 | Removing code introspection is one of the tenets of EOF and `IS_CONTRACT` would be an exception from the principle. Without `IS_CONTRACT`, ERC-721 and ERC-1155 standard implementations have to resort to either: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 74 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 74 | Removing code introspection is one of the tenets of EOF and `IS_CONTRACT` would be an exception from the principle. Without `IS_CONTRACT`, ERC-721 and ERC-1155 standard implementations have to resort to either: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 74 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 74 | Removing code introspection is one of the tenets of EOF and `IS_CONTRACT` would be an exception from the principle. Without `IS_CONTRACT`, ERC-721 and ERC-1155 standard implementations have to resort to either: | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

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 sub-optimal, 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 (`safeTransfer` safe guards) patterns, also unlikely to happen as ERC-721 and ERC-1155 are Final and adopted in practice.

Check failure on line 78 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 78 | 3. Updating the standards to not rely on code introspection (`safeTransfer` safe guards) patterns, also unlikely to happen as ERC-721 and ERC-1155 are Final and adopted in practice. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 78 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 78 | 3. Updating the standards to not rely on code introspection (`safeTransfer` safe guards) patterns, also unlikely to happen as ERC-721 and ERC-1155 are Final and adopted in practice. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 78 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 78 | 3. Updating the standards to not rely on code introspection (`safeTransfer` safe guards) patterns, also unlikely to happen as ERC-721 and ERC-1155 are Final and adopted in practice. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 78 in EIPS/eip-is-contract.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> EIPS/eip-is-contract.md | 78 | 3. Updating the standards to not rely on code introspection (`safeTransfer` safe guards) patterns, also unlikely to happen as ERC-721 and ERC-1155 are Final and adopted in practice. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

### "Endgame Account Abstraction" issues

TBD - can someone verbalize it?

### Relation to [EIP-7702](./eip-7702.md) "Set EOA account code"

After [EIP-7702](./eip-7702.md) is activated, the discrimination between EOAs and smart contracts using `EXTCODESIZE` (or `IS_CONTRACT`) has an edge case: Whenever an EOA sets its code to a smart contract 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.

### Including safe guarding against proxy bricking

In parallel to the ERC-721 / ERC-1155 problem, another potential risk has been brought to attention. Since EOFv1 prohibits `EXTDELEGATECALL` targetting 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.

One potential solution to this would be to have a generalized `CONTRACT_KIND` instruction instead of `IS_CONTRACT` which would further discriminate the account into legacy or EOFv1 with a `0` / `1` / `2` return value, thereby providing means for an additional safeguard against such a scenario.

This problem is potentially solvable on the application layer (proxy upgrade would include a direct check of the implementation contract being `EXTDELEGATECALL`-able) or even by re-enabling `EXTDELEGATECALL` to a legacy target in a future EVM upgrade.

## Backwards Compatibility

`IS_CONTRACT` 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

TBA

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).

0 comments on commit 8f785fb

Please sign in to comment.