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

Update EIP-7702: add delegation designation #8677

Merged
merged 7 commits into from
Jul 8, 2024
Merged
Changes from 4 commits
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
45 changes: 27 additions & 18 deletions EIPS/eip-7702.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
---
eip: 7702
title: Set EOA account code for one transaction
description: Add a new tx type that sets the code for an EOA during one transaction execution
title: Set EOA account code
description: Add a new tx type that sets the code for an EOA during execution
author: Vitalik Buterin (@vbuterin), Sam Wilson (@SamWilsn), Ansgar Dietrichs (@adietrichs), Matt Garnett (@lightclient)
discussions-to: https://ethereum-magicians.org/t/eip-set-eoa-account-code-for-one-transaction/19923
status: Review
type: Standards Track
category: Core
created: 2024-05-07
requires: 2718, 2929, 2930
requires: 2718, 2929, 2930, 3541, 3607
---

## Abstract

Add a new transaction type that adds a list of `[address, y_parity, r, s]` authorization tuples, and converts the signing accounts (not necessarily the same as the `tx.origin`) into smart contract wallets for the duration of that transaction.
Add a new transaction type that adds a list of `[chain_id, address, nonce, y_parity, r, s]` authorization tuples. For each tuple, write a delegation designator `(0xef0000 ++ address)` to the signing account's code. All code reading operations must load the pointed to by the designator.
lightclient marked this conversation as resolved.
Show resolved Hide resolved

## Motivation

Expand All @@ -40,38 +40,49 @@ We introduce a new [EIP-2718](./eip-2718.md) transaction, "set code transaction"
```
rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, value, data, access_list, authorization_list, signature_y_parity, signature_r, signature_s])

authorization_list = [[chain_id, address, [nonce], y_parity, r, s], ...]
authorization_list = [[chain_id, address, nonce, y_parity, r, s], ...]
```

The fields `chain_id`, `nonce`, `max_priority_fee_per_gas`, `max_fee_per_gas`, `gas_limit`, `destination`, `value`, `data`, and `access_list` of the outer transaction follow the same semantics as [EIP-1559](./eip-1559.md).

The `authorization_list` is a list of tuples that store the address to code which the signer desires to set in their EOA temporarily. The third element is a list item mimicking an optional value. When the list length is zero, consider the authorization nonce to be null. When the list length is one, consider the single integer value to be the provided nonce authorization. Other lengths and value types in this optional are invalid and the transaction as a whole should be considered malformed.
The `authorization_list` is a list of tuples that store the address to code which the signer desires to execute in the context of their EOA.

The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, cumulative_transaction_gas_used, logs_bloom, logs])`.

#### Behavior

At the start of executing the transaction, for each `[chain_id, address, [nonce], y_parity, r, s]` tuple:
At the start of executing the transaction, for each `[chain_id, address, nonce, y_parity, r, s]` tuple:

1. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, [nonce]])), y_parity, r, s]`
1. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]`
2. Verify the chain id is either 0 or the chain's current ID.
3. Verify that the code of `authority` is empty.
4. If nonce list item is length one, verify the nonce of `authority` is equal to `nonce`.
5. Set the code of `authority` to code associated with `address`.
6. Add the `authority` account to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).)
3. Verify that the code of `authority` is either empty or already delegated.
Copy link
Contributor

Choose a reason for hiding this comment

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

or already delegated this is basically a check that the code of the authority is in the format of 0xef01 || address, right?

I assume this check is to prevent that this flow in case that any other code besides the delegation is set, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

very confusing.
AFAIK, the whole purpose was to define temporary delegations.
If it is "already delegated" it means:

  1. delegation are persisted - which is not defined by this doc (if/when we can change the delegation?)
  2. there is no need for an authorizer entry, since it is already on-chain
  3. if the repeated authorizer signature is the mechanism to change an on-chain delegation, then please specify it explicitly. (and it has a whole bunch of security implications)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes this is intended to maintain the 3607 requirement that EOAs may not have code. We made a special allowance for delegation designations.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok. so remove the "temporary" in text: it is no longer temporary, but permanent assignment of a "proxy" address.

4. Verify the nonce of `authority` is equal to `nonce`.
5. Set the code of `authority` to be `0xef0100 || address`. This is a delegation designation.
6. Increase the nonce of `authority` by one.
7. Add the `authority` account to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).)

If any of the above steps fail, immediately stop processing that tuple and continue to the next tuple in the list. It will In the case of multiple tuples for the same authority, set the code specified by the address in the first occurrence.
Copy link
Contributor

Choose a reason for hiding this comment

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

In the case of multiple tuples for the same authority, set the code specified by the address in the first occurrence.

In the previous spec this used to follow naturally from processing the list in order because the second and later valid tuples for the same authority would fail the check "verify that code is empty", but this is no longer the case since adding "... or already delegated".

I don't see why valid tuples should be ignored in this design. In particular, I think there could be two tuples with nonce and nonce + 1 that could need to be submitted together to "catch up" to the latest one.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think multiple tuples of the same address should be forbidden: the signer has all the information to detect and remove duplicate entries.

Choose a reason for hiding this comment

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

Ok

Copy link
Contributor

Choose a reason for hiding this comment

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

the signer has all the information to detect and remove duplicate entries.

I don't understand this. The signatures may already have been generated and the signer may not be available to create a new signature that combines them into one.

I see no reason to reject duplicates if each occurrence is paid for.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why would the sender of the 7702 transaction include duplicates and pay for them, even if allowed? It won't make both implementations available, only one of them. Any further tuples for the same account seem like a waste of gas.

Copy link
Contributor

Choose a reason for hiding this comment

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

If I have two signed delegations from the same account with nonces n and n + 1, and I need to submit an op from this account that requires the latest delegation, I need to submit both... The first one is needed to increment the nonce so that the second one becomes valid.

Copy link
Contributor

Choose a reason for hiding this comment

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

So basically you're using it as a way to increment the nonce within the same transaction, while only expecting to use the latest delegation. The previous delegation(s) won't be usable during the transaction (or later), so it's just a way to bump the nonce.

Seems fine to me, but I think there were past concerns about bumping nonces by more than 1 in a single transaction. I'm not sure what they were. From DoS perspective a single transaction can invalidate all pending transactions by emptying the eth from the account so multiple bumps don't really increase the risk. Need feedback from core devs who opposed to it before.

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought the concerns were about being able to bump nonces by an arbitrary amount. In this case it's always incrementing by 1 (per signature) so I don't imagine there would be an issue with that.


At the end of the transaction, set the code of each `authority` back to empty.

Note that the signer of an authorization tuple may be different than `tx.origin` of the transaction.

##### Delegation Designation

The delegation designation uses the banned opcode `0xef` from [EIP-3541](./eip-3541) to designate the code has a special purpose. This designator requires all code retrieving operations follow the address pointer to fill the accounts observable code. The following instructions are impacted: `EXTCODESIZE`, `EXTCODECOPY`, `EXTCODEHASH`, `CALL`, `CALLCODE`, `STATICCALL`, `DELEGATECALL`.

For example, `EXTCODESIZE` would return the size of the code pointed to by `address` instead of `24` which would represent the delegation designation. `CALL` would similarly load the code from `address` and execute it in the context of `authority`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to clarify the behavior of chains longer than 2. E.g. EOA1 points to EOA2, which points to a contract. Do we:

  1. Ban it? If so, what would be the behavior of these opcodes when hitting a cascaded delegation?
  2. Allow it? If so, we need to consider DoS (e.g. loading an uncapped number of accounts at the cost of a single EXTCODESIZE). And we need to add loop detection.

Loops can be detected when setting the delegation, since the last delegation has to point to an already-delegated account. But non-loop chains can't be, since the delegations can be ordered such that each account delegates to an empty account which only gets a delegation later. Therefore it has to be handled in the opcodes during actual delegation.

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought about that and i believe it makes no sense to support chainning. Imo, if EOA1 points to EOA2 where EOA2 has a designated address, it just 3xecut3d EF which is abort in evm and that is it. No need to handle this as a special case.

Copy link
Contributor

Choose a reason for hiding this comment

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

I also think it makes no sense to support chaining. Either treat it as an empty account, or let the EF execute and abort. Just thought it makes sense to define the behavior so that it's not parsed recursively.

Copy link
Contributor

Choose a reason for hiding this comment

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

The simplest answer would be to require the delegation to go to live contract accounts and not EOAs or delegated EOAs. Getting rid of pointing to EOAs gets rid of the chaining and possible looping. But we also need to ban delegating to empty addresses as they could counterfactually become either an EOA or a Contract with no way to predict which until it happens.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the best way is to define that delegation is not recursive: if you delegate to an already-delegated entity, the "magic" doesn't happen twice: you end up pointing to a bad code (starting with 0xef)

Copy link
Contributor

Choose a reason for hiding this comment

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

@drortirosh it is already done here: 358fc70

Copy link
Contributor

@shemnon shemnon Jun 24, 2024

Choose a reason for hiding this comment

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

What about delegating to code that is empty? Same as any other empty code account? And then the code shows up in a latter TX?

That happens in a few cases (a) an delegated EOA that has delegated and (b) an empty account, (c) a balance-only account (nonce == 0). This should be called out in the testing section:

Tests that do all combinations of the following:

  • Delegate to (an empty account | a balance only account | an undelegated EOA), which later (gains balance | becomes an EOA | becomes a contract | becomes a delegated EOA)
  • call the EOA delegating (in the same TX | in a latter TX after the permutation happens).


In case a delegation designator points to another designator, creating a potential chain or loop of designators, clients must retrieve only the first code and then stop following the designator chain.

#### Gas Costs

The intrinsic cost of the new transaction is inherited from [EIP-2930](./eip-2930.md), specifically `21000 + 16 * non-zero calldata bytes + 4 * zero calldata bytes + 1900 * access list storage key count + 2400 * access list address count`. Additionally, we add a cost of `PER_CONTRACT_CODE_BASE_COST * authorization list length`.
lightclient marked this conversation as resolved.
Show resolved Hide resolved

The transaction sender will pay for all authorization tuples, regardless of validity or duplication.

#### Transaction Origination

Modify the restriction put in place by [EIP-3607](./eip-3607.md) to allow EOAs whose code is a valid delegation designation, i.e. `0xef0100 || address`, to continue to originate transactions. Accounts with any other code values may not originate transactions.

## Rationale

### No initcode
Expand Down Expand Up @@ -108,9 +119,7 @@ An alternative to adding chain ID could be to sign over the code the address poi

#### In-protocol revocation

A hotly debated element of this EIP is the need for in-protocol revocation. Although it is possible to implement revocation logic within delegated code, for some this isn't sufficient and it is the duty of the protocol to provide a revocation option of last resort.

For this reason, the proposal provides two separate schemes. The first is an eternal delegation to a smart contract. At the protocol level, it is not possible to revoke. However, the contract you delegate to is one which can expect to use in your account for perpetuity; similar to how smart contract wallet users deploy proxy contracts to their account and point the target at a wallet implementation. The second is a scoped delegation with a revocation mechanism based on the EOA's nonce. This is a safer to begin using smart contract code in the context of your EOA without potentially committing to a specific piece of code forever.
Unlike previous versions of this EIP and EIPs similar, the delegation designation can be revoked at anytime signing and sending a EIP-7702 authorization to a new target with the account's current nonce. Without such action, a delegation will remain valid in perpetuity.
lightclient marked this conversation as resolved.
Show resolved Hide resolved

### Self-sponsoring: allowing `tx.origin` to set code

Expand Down Expand Up @@ -146,7 +155,7 @@ Specifically:
* The "code pathways" that are used are code pathways that would, in many cases (though perhaps not all), continue to "make sense" in a pure-smart-contract-wallet world.
* Hence, it avoids the problem of "creating two separate code ecosystems", because to a large extent they would be the same ecosystem. There would be some workflows that require kludges under this solution that would be better done in some different "more native" under "endgame AA", but this is relatively a small subset.
* It does not require adding any opcodes, that would become dangling and useless in a post-EOA world.
* It allows EOAs to temporarily convert themselves into contracts to be included in ERC-4337 bundles, in a way that's compatible with the existing `EntryPoint`.
* It allows EOAs to masquerade as contracts to be included in ERC-4337 bundles, in a way that's compatible with the existing `EntryPoint`.
* Once this is implemented, allowing EOAs to migrate permanently is "only one line of code": just add a flag to not set the code back to empty at the end.

## Backwards Compatibility
Expand Down
Loading