Try   HackMD

[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”.

📝 TLDR

We believe that EXTCODE* 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.

Anticipated Upgrades

EIP-7702 — a new transaction type that allows EOAs to set persistent runtime bytecode in their account (ref)

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)

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

  • 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 (cc julrach)