Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EIP: HASCODE instruction #8838

Merged
merged 7 commits into from
Sep 9, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
---
pdobacz marked this conversation as resolved.
Show resolved Hide resolved
title: IS_CONTRACT instruction
description: Introduce IS_CONTRACT instruction
author: Andrei Maiboroda (@gumb0), Piotr Dobaczewski (@pdobacz), Danno Ferrin (@shemnon)
discussions-to: <URL>
pdobacz marked this conversation as resolved.
Show resolved Hide resolved
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.
Copy link
Contributor

@abcoathup abcoathup Aug 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

ERC-721/1155 transfer checks are required for contracts, irrespective of whether they are contract accounts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abcoathup Actually, we'd like a clarification of your suggestion. "smart contract accounts" was a mental shortcut intending to mean "EVM state accounts with code", and probably that wording is clearer and more specific. Next, maybe we change "smart contract accounts" to "smart contract wallets" to avoid confusion. Would this work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I have in mind: 2be26b3 - I think "contract account" is a well established and simple term, hope this works.


## 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).
Loading