# [EIP-7702] Making the Case for “Delegation Introspection” ## Introduction The purpose of this document is to: 1. bring attention to concerns brought up over “code introspection” behavior for EIP-7702 delegated Externally Owned Accounts (EOAs) 2. relate the rationale for this current spec to the EVM Object Format (EOF) upgrade 3. propose a slight behavior change to EIP-7702 as a result of this analysis Both upgrades, especially EOF, are complex. We encourage readers to research them independently, but this document will attempt to explain “code introspection” as it relates to each upgrade, and separate out a notion of “delegation introspection” from that of “code introspection”. <div class="alert alert-block" style=" border-left: 4px solid #C6FF4D; background-color: rgba(198, 255, 77, 0.2); /* C6FF4D with 20% opacity */ border-radius: 6px; padding: 1.5rem 1rem; /* Extra padding */ margin: 1rem 0; color: #C6FF4D; font-family: inherit; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); "> <!-- Heading row: icon + title --> <div style="display: flex; align-items: center; margin-bottom: 0.5rem;"> <span style="margin-right: 0.5rem;">📝</span> <strong style="color: #C6FF4D;">TLDR</strong> </div> <!-- Body text --> <p style="margin: 0; color: #C6FF4D; line-height: 1.75;"> We believe that <code style="color: #C6FF4D; background: rgba(198, 255, 77, 0.4); padding: 0.2em 0.1em; border-radius: 4px;">EXTCODE*</code> opcodes should act on the full EIP-7702 "delegation designator" for delegated EOAs instead of just the prefix because it would make the EIP far more useful and wouldn't change the behavior of EOF contracts. </p> </div> ### Anticipated Upgrades **EIP-7702** — a new transaction type that allows EOAs to set persistent runtime bytecode in their account ([ref](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md)) **EVM Object Format (EOF)** — a new smart contract bytecode format defined by a collection of EIPs which aim to improve smart contract structure, efficiency, security, and EVM forward compatibility. ([ref](https://eips.ethereum.org/EIPS/eip-7692)) ### Definitions - “legacy contract” — pre-EOF smart contract - “EOF contract” — a smart contract with EOF-compliant bytecode - “delegated EOA” — an EOA that has set persistent bytecode in its account via EIP-7702 ## What is “code introspection”? “Code introspection” refers to a legacy contract’s ability to inspect its own bytecode or the bytecode of an external contract and change behavior based on that information. This is enabled by `CODE*` opcodes for self-inspection and `EXTCODE*` opcodes for external inspection. However, “code introspection” is not supported in EOF contracts. There are a variety of reasons for this decision, including: - **simplicity** — facilitates pre-deployment validation and static analysis - **efficiency** — reduces gas costs associated with unnecessary runtime analysis - **security** — minimizes vulnerabilities related to dynamic code analysis - **encapsulation** — ensures that distinct code and data sections in EOF contracts are genuinely separate and modular - **forward compatibility** — allows EOF contracts to remain agnostic to underlying changes in the EVM or the structure of deployed contracts ## How does “code introspection” relate to EIP-7702? Under the hood, EIP-7702 works by setting a “delegation designator” in the EOA’s `code` field with the structure: `0xef0100 || delegate_address`; where `delegate_address` is the address of a singleton contract. All code-executing operations called on the EOA will load the runtime bytecode of this contract and execute it in the context of the EOA. The structure of the `code` field for each account type is as follows: | | `code` field structure | | --- | --- | | legacy contract | pre-EOF legacy bytecode | | EOF contract | EOF structured bytecode | | un-delegated EOA | `0x` | | EOA delegated to a legacy contract | `0xef0100 \|\| delegate_address` | | EOA delegated to an EOF contract | `0xef0100 \|\| delegate_address` | Note: the `code` field structure of this kind of account is clearly distinct from that of any contract. ## How does “code introspection” currently work for delegated EOAs? Here’s a table describing `CODECOPY` behavior for each account type: | | `CODECOPY` behavior | | --- | --- | | legacy contract | pre-EOF legacy bytecode | | EOF contract | not possible | | EOA delegated to a legacy contract | `0xef0100 \|\| delegate_address` | | EOA delegated to an EOF contract | not possible | *`CODESIZE` returns the size of `CODECOPY` (if possible) And, here's a table describing current `EXTCODECOPY` behavior for each account type. The rows represent the type of account calling `EXTCODECOPY`, the columns represent the account type of the target, and each entry is the return value of the opcode: | | legacy contract (target) | EOF contract (target) | EOA delegated to a legacy contract (target) | EOA delegated to an EOF contract (target) | | --- | --- | --- | --- | --- | | legacy contract (caller) | pre-EOF legacy bytecode | `0xef00` | `0xef01` | `0xef01` | | EOF contract (caller) | not possible | not possible | not possible | not possible | | EOA delegated to a legacy contract (caller) | pre-EOF legacy bytecode | `0xef00` | `0xef01` | `0xef01` | | EOA delegated to an EOF contract (caller) | not possible | not possible | not possible | not possible | *`EXTCODEHASH` and `EXTCODESIZE` return the `keccak256` hash and the size of `EXTCODECOPY` respectively (if possible) Note: - the `delegate_address` is hidden on-chain unnecessarily for legacy-contract-based accounts even though the information is readily available in the account’s `code` - `CODE*` and `EXTCODE*` behavior is different when referencing the same account’s `code` ## Why is this the current `EXTCODE*` behavior? Developers are being nudged to use EOF contracts in conjunction with EIP-7702. Since EOF contracts can’t contain `CODE*` and `EXTCODE*` opcodes, the spec disallows legacy-contract-based accounts from leveraging these opcodes in a useful manner. However, this rationale takes an unnecessarily forceful stance on EOF adoption and tangles EOF and EIP-7702 when they are not explicitly connected. While EOF will be a massive improvement for the EVM and should be used in conjunction with EIP-7702 when beneficial, the simple fact is that EOF in its initial form will be an optional feature. There might come a day when legacy contract deployment is deprecated entirely and replaced by EOF. However, such a fundamental change would require extensive discussion across the entire ecosystem and would need to involve a substantial time buffer to allow developers to migrate to this format. Not to mention, EOF itself would likely see new iterations before that time. In the meantime, AA teams building on EIP-7702 should have the choice of developing legacy contracts, EOF contracts, or a combination as they see fit. We believe the use of `EXTCODE*` opcodes to check an EOA’s “delegation designator” at runtime (aka “delegation introspection”) is an incredibly useful tool for building on EIP-7702 and we should not disallow legacy contracts from leveraging this feature regardless of EOF’s stance on “code introspection”. ## Why is “delegation introspection” useful? Listed in EIP-7702’s “Motivation” section are the following ([ref](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md#motivation)): > - **Sponsorship**: account X pays for a transaction on behalf of account Y. Account X could be paid in some other ERC-20 for this service, or it could be an application operator including the transactions of its users for free. > - **Privilege de-escalation**: users can sign sub-keys and give them specific permissions that are much weaker than global access to the account. For example, you could imagine a permission to spend ERC-20 tokens but not ETH, or to spend up to 1% of the total balance per day, or to interact only with a specific application. Neither of these goals can be accomplished without relayers or other external accounts being able to safely submit transactions on behalf of users. ### Sponsorship The use case outlined here relies on account X (either an application relayer or other external account) being able to submit a transaction on behalf of account Y. However, if account X cannot be sure at runtime that account Y is delegated to the agreed upon implementation, account Y is free to take advantage of account X by front-running the transaction: 1. watch for account X’s transaction 2. front-run with an EIP-7702 transaction changing implementations 3. account X’s transaction executes against a different implementation This would be improved by allowing account X to check account Y’s delegation designator at runtime via `EXTCODECOPY` because it would allow account X to revert execution at the protocol level instead of falling into an unknown execution path as a result of the attack. ### Privilege de-escalation Similarly, sub-keys can only be used reliably by external accounts that trust the delegated EOA. There is currently no way to ensure that the sub-key holder’s transaction will behave as intended. This would be improved by allowing the sub-key holder to check the EOA’s delegation designator at runtime via `EXTCODECOPY` for the same reason. ## Proposed `EXTCODE*` behavior We propose `EXTCODE*` behavior to be changed to the following: | | Legacy Contract (target) | EOF Contract (target) | EOA delegated to Legacy Contract (target) | EOA delegated to EOF Contract (target) | | --- | --- | --- | --- | --- | | Legacy Contract (caller) | pre-EOF legacy bytecode | `0xef00` | `0xef0100 \|\| delegate_address` | `0xef0100 \|\| delegate_address` | | EOF Contract (caller) | not possible | not possible | not possible | not possible | | EOA delegated to Legacy Contract (caller) | pre-EOF legacy bytecode | `0xef00` | `0xef0100 \|\| delegate_address` | `0xef0100 \|\| delegate_address` | | EOA delegated to EOF Contract (caller) | not possible | not possible | not possible | not possible | *`EXTCODEHASH` and `EXTCODESIZE` return the `keccak256` hash and the size of `EXTCODECOPY` ### Benefits of proposed `EXTCODE*` behavior - “delegation introspection” is enabled for legacy-contract-based accounts while the utility/behavior of EOF-contract-based accounts is unchanged. - `CODE*` and `EXTCODE*` opcodes behave exactly the same for all account types ### Would the proposed change truly enable “code introspection” for delegated EOAs? No. A “delegation designator” is NOT bytecode. This value does not reveal any internal implementation details of the EOA’s delegate contract. While it is stored in the same place where bytecode is typically stored for contract accounts, it’s actually just a pointer to an implementation contract, not actual bytecode. The ability to query an EOA’s delegation designator on-chain would allow legacy contracts to know the general behavior of the delegated EOA, but this would still be fundamentally different than true “code introspection”. Considering the utility of “delegation introspection”, it might be beneficial to enable this functionality somehow in the next version of EOF: continuing to block “code introspection” but enabling “delegation introspection”. ## Conclusion “Delegation introspection” would be a very useful EIP-7702 feature. While EOF contracts wouldn’t be able to use this feature directly, EOF compliance won't be required when the upgrade lands. It doesn’t make sense to limit legacy contracts when the utility of EOF is unchanged. AA teams building on EIP-7702 should have the choice to develop legacy contracts, EOF contracts, or a combination to maximize EIP-7702’s utility while both EIP-7702 and EOF are still relatively new. Let’s untangle these upgrades. Let’s implement both in their maximally useful forms and figure out how to better integrate them later via the EIP process. -- [dv3_000](https://x.com/dv3_000) (cc [julrach](https://x.com/julrach))