Skip to content

Update EIP-8037: improve EIP-7702 authorization gas accounting in EIP-8037#11715

Merged
eth-bot merged 7 commits into
ethereum:masterfrom
rjl493456442:eip-8037-auth
Jun 10, 2026
Merged

Update EIP-8037: improve EIP-7702 authorization gas accounting in EIP-8037#11715
eth-bot merged 7 commits into
ethereum:masterfrom
rjl493456442:eip-8037-auth

Conversation

@rjl493456442

@rjl493456442 rjl493456442 commented May 21, 2026

Copy link
Copy Markdown
Member

This PR improves gas accounting for EIP-7702 authorizations, covering several corner cases such as authorization cancellation within the same transaction.

State gas charging is now aligned with the net state changes introduced by authorizations, except for the case clearing a pre-existing authorization which is intentionally not refunded.

@rjl493456442 rjl493456442 requested a review from eth-bot as a code owner May 21, 2026 06:58
@github-actions github-actions Bot added c-update Modifies an existing proposal s-draft This EIP is a Draft t-core labels May 21, 2026
@eth-bot

eth-bot commented May 21, 2026

Copy link
Copy Markdown
Collaborator

✅ All reviewers have approved.

@eth-bot eth-bot added the a-review Waiting on author to review label May 21, 2026
@eth-bot eth-bot changed the title EIPS: improve EIP-7702 authorization gas accounting in EIP-8037 Update EIP-8037: improve EIP-7702 authorization gas accounting in EIP-8037 May 21, 2026
@Helkomine

Copy link
Copy Markdown
Contributor

I disagree; we cannot overcharge for a very common use case in the protocol, as this contradicts the goal of only charging for the created state portion of this EIP.

@rjl493456442

Copy link
Copy Markdown
Member Author

@Helkomine What do you mean? It's exactly the purpose of this PR, to not overcharge the state gas in certain scenarios.

@Helkomine

Copy link
Copy Markdown
Contributor

I mean, the case where the invalid authorization but the state gas is not refilled even though there is no state growth.

@misilva73 misilva73 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a few suggestions, mostly wording and clarity. As proposal expands the refunds previously provided by 7702, I would like to hear more opinions before approving. Do we want to expand the refunds given to 7702 authorizations? Can we think of any attack vectors that may arrise?

Comment thread EIPS/eip-8037.md Outdated
Comment thread EIPS/eip-8037.md Outdated
Comment thread EIPS/eip-8037.md Outdated
Comment thread EIPS/eip-8037.md Outdated
Comment thread EIPS/eip-8037.md Outdated
Comment thread EIPS/eip-8037.md Outdated
@rjl493456442

Copy link
Copy Markdown
Member Author

@Helkomine Okay, it makes sense to reconsider this case.

@rjl493456442

Copy link
Copy Markdown
Member Author

Yes, I'll keep the PR as it is for now and wait the feedback from other teams whether the additional complexity is worthwhile. Personally, I think the complexity is justified, since it aligns the gas accounting for authorizations more closely with the actual net state changes.

Comment thread EIPS/eip-8037.md
##### Gas accounting for [EIP-7702](./eip-7702.md) authorizations

While computing the intrinsic gas cost, [EIP-7702](./eip-7702.md) authorizations are charged the worst-case cost for each delegation. Then, during authorization processing, the following gas adjustments are made for each processed authorization:
Intrinsic gas charges the worst-case state-gas cost (`(STATE_BYTES_PER_NEW_ACCOUNT + STATE_BYTES_PER_AUTH_BASE) × CPSB`) for each authorization. However, an authority's account leaf and its 23 byte delegation indicator can only be written once per transaction, even if the same authority appears in multiple authorizations. Therefore, per authorization adjustments are applied while processing the authorization list.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
Intrinsic gas charges the worst-case state-gas cost (`(STATE_BYTES_PER_NEW_ACCOUNT + STATE_BYTES_PER_AUTH_BASE) × CPSB`) for each authorization. However, an authority's account leaf and its 23 byte delegation indicator can only be written once per transaction, even if the same authority appears in multiple authorizations. Therefore, per authorization adjustments are applied while processing the authorization list.
Intrinsic gas charges the worst-case gas cost (`(STATE_BYTES_PER_NEW_ACCOUNT + STATE_BYTES_PER_AUTH_BASE) × CPSB`) for each authorization. However, an authority's account leaf and its 23 byte delegation indicator can only be written once per transaction, even if the same authority appears in multiple authorizations. Therefore, per authorization adjustments are applied while processing the authorization list.

it's not just the worst-case state but ACCOUNT_WRITE is also given back

Comment thread EIPS/eip-8037.md Outdated
- the authority is in `billed` (a prior authorization in this transaction has already been charged the auth-base creation for this authority)
- `authorization.address` is `ZERO` (the authorization is clearing the delegation)

**4. First charge.** If the authorization does not meet the conditions of rule 1, 2 and 3, add the authority to `billed`; this authorization is the one charged for the per-tx auth-base creation. This charge was already included in the transaction's intrinsic cost.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
**4. First charge.** If the authorization does not meet the conditions of rule 1, 2 and 3, add the authority to `billed`; this authorization is the one charged for the per-tx auth-base creation. This charge was already included in the transaction's intrinsic cost.
**4. First charge.** If the authorization does not meet the conditions of rule 1 and 3, add the authority to `billed`; this authorization is the one charged for the per-tx auth-base creation. This charge was already included in the transaction's intrinsic cost.

Depending on rule 2 to be on billed breaks the steps below, I don't think it should depend on it. Case for a normal EOA (no code deployed) at the start authorization processing:

  • rule 1 doesn't trigger - it's a valid delegation;
  • rule 2 triggers, account already exists - STATE_BYTES_PER_NEW_ACCOUNT + ACCOUNT_WRITE refunded;
  • rule 3 no trigger - this is a legit delegation as the auth bytes are being set for first time so you are billed for STATE_BYTES_PER_AUTH_BASE;
  • rule 4 no trigger - since it was triggered for 2;

Then the next time you loop, in rule 3 if there's a duplicate authorization, you won't see it in billed whereas you should. Then you will charge it again.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch!

@lu-pinto

lu-pinto commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

After the 2 issues are addressed I think the proposal is sound. Agree that it's not fair to charge for state the won't exist in the end so complexity is justified.

Comment thread EIPS/eip-8037.md Outdated
Comment on lines +161 to +182
Processing maintains two pieces of per-transaction bookkeeping:

`execution_state_gas_used` decreases by the corresponding amount of state-gas refills.
- `billed`: tracks which authorities have already been charged for auth-base creation in the current transaction.
- `account state`:
- **pre-transaction state**: the account state before transaction execution.
- **current state**: the account state while processing authorizations, updated after each authorization.

Because `execution_state_gas_used` is initialized to `0`, this refill may bring it below zero before any execution-time charges. Implementations may equivalently track the refill separately and subtract it from `tx_state_gas` when accumulating `block_state_gas_used`.
Authorization processing follows 5 gas accounting rules:

**1. Invalid authorizations.** Invalid authorizations are skipped without per-auth processing. Their entire intrinsic state-gas portion, `(STATE_BYTES_PER_NEW_ACCOUNT + STATE_BYTES_PER_AUTH_BASE) × CPSB`, is refilled to `state_gas_reservoir` and refund `ACCOUNT_WRITE` to the refund counter.

**2. Account-creation refill.** If the authority's account leaf already exists in the **current state** (non-zero nonce, non-zero balance, or non-empty code), refill `STATE_BYTES_PER_NEW_ACCOUNT × CPSB` to `state_gas_reservoir` and refund `ACCOUNT_WRITE` to the refund counter.

**3. Auth-base refill.** Refill `STATE_BYTES_PER_AUTH_BASE × CPSB` to `state_gas_reservoir` if **any** of:

- the authority was already delegated in the **pre-transaction state** state
- the authority is in `billed` (a prior authorization in this transaction has already been charged the auth-base creation for this authority)
- `authorization.address` is `ZERO` (the authorization is clearing the delegation)

**4. First charge.** If the authorization does not meet the conditions of rule 1, 2 and 3, add the authority to `billed`; this authorization is the one charged for the per-tx auth-base creation. This charge was already included in the transaction's intrinsic cost.

**5. Cancel a prior auth-base creation.** If `authorization.address` is `ZERO` **and** the authority is in `billed`, additionally refill `STATE_BYTES_PER_AUTH_BASE × CPSB` to `state_gas_reservoir` and remove the authority from `billed`. The chain of authorizations for this authority now writes zero net delegation bytes, so the earlier creation charge is no longer justified. This covers patterns such as `0->a->0`, where the creation by an earlier authorization is undone by a later clearing one in the same list.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Building on the rule‑4 issue already raised above — rather than fixing how billed gets populated, I think the whole set can be dropped. billed is reconstructing something that's already in current state: once an earlier authorization delegates A, its codeHash is non-empty, so "was this authority delegated earlier in the tx?" is just a codeHash check. The only thing current state can't distinguish is pre-tx vs in-tx delegation, which matters solely for the cancel case and is a single read of the pre-tx snapshot.

That collapses the 5 rules to 3. It's equivalent to the intended behavior — the rule‑3 condition pre_delegated OR billed OR ZERO is exactly cur_delegated OR pre_delegated OR ZERO once billed = cur_delegated AND NOT pre_delegated — but reading codeHash directly can't have the rule‑4 failure mode, and it fixes 0→a→0 for funded EOAs (which rule 5 misses today because those authorities never enter billed).

Concrete suggestion (also drops the billed set, fixes the "state state" typo, and tidies rule 1's grammar):

Suggested change
Processing maintains two pieces of per-transaction bookkeeping:
`execution_state_gas_used` decreases by the corresponding amount of state-gas refills.
- `billed`: tracks which authorities have already been charged for auth-base creation in the current transaction.
- `account state`:
- **pre-transaction state**: the account state before transaction execution.
- **current state**: the account state while processing authorizations, updated after each authorization.
Because `execution_state_gas_used` is initialized to `0`, this refill may bring it below zero before any execution-time charges. Implementations may equivalently track the refill separately and subtract it from `tx_state_gas` when accumulating `block_state_gas_used`.
Authorization processing follows 5 gas accounting rules:
**1. Invalid authorizations.** Invalid authorizations are skipped without per-auth processing. Their entire intrinsic state-gas portion, `(STATE_BYTES_PER_NEW_ACCOUNT + STATE_BYTES_PER_AUTH_BASE) × CPSB`, is refilled to `state_gas_reservoir` and refund `ACCOUNT_WRITE` to the refund counter.
**2. Account-creation refill.** If the authority's account leaf already exists in the **current state** (non-zero nonce, non-zero balance, or non-empty code), refill `STATE_BYTES_PER_NEW_ACCOUNT × CPSB` to `state_gas_reservoir` and refund `ACCOUNT_WRITE` to the refund counter.
**3. Auth-base refill.** Refill `STATE_BYTES_PER_AUTH_BASE × CPSB` to `state_gas_reservoir` if **any** of:
- the authority was already delegated in the **pre-transaction state** state
- the authority is in `billed` (a prior authorization in this transaction has already been charged the auth-base creation for this authority)
- `authorization.address` is `ZERO` (the authorization is clearing the delegation)
**4. First charge.** If the authorization does not meet the conditions of rule 1, 2 and 3, add the authority to `billed`; this authorization is the one charged for the per-tx auth-base creation. This charge was already included in the transaction's intrinsic cost.
**5. Cancel a prior auth-base creation.** If `authorization.address` is `ZERO` **and** the authority is in `billed`, additionally refill `STATE_BYTES_PER_AUTH_BASE × CPSB` to `state_gas_reservoir` and remove the authority from `billed`. The chain of authorizations for this authority now writes zero net delegation bytes, so the earlier creation charge is no longer justified. This covers patterns such as `0->a->0`, where the creation by an earlier authorization is undone by a later clearing one in the same list.
The adjustments enforce a single invariant: the `STATE_BYTES_PER_NEW_ACCOUNT × CPSB` account-leaf portion is charged at most once per authority and only when the account did not exist before the transaction, and the `STATE_BYTES_PER_AUTH_BASE × CPSB` delegation-indicator portion is charged at most once per authority and only when the authority ends the transaction delegated having started it undelegated.
Processing reads the account state at two points:
- **pre-transaction state**: the account state before transaction execution.
- **current state**: the account state while processing authorizations, updated after each authorization. Because an in-transaction delegation leaves a delegation indicator in the authority's `code` field, the current state already records whether an earlier authorization in this transaction delegated the same authority.
For each authorization, let `cur_delegated` be `true` when the authority has a non-empty delegation indicator in the **current state** (`codeHash != emptyHash`), and `pre_delegated` be `true` when it had one in the **pre-transaction state**. Authorization processing follows 3 gas accounting rules:
**1. Invalid authorizations.** Invalid authorizations are skipped without per-auth processing. Their entire intrinsic state-gas portion, `(STATE_BYTES_PER_NEW_ACCOUNT + STATE_BYTES_PER_AUTH_BASE) × CPSB`, is refilled to `state_gas_reservoir` and `ACCOUNT_WRITE` is refunded to the refund counter.
**2. Account-leaf refill.** If the authority's account leaf already exists in the **current state** (non-zero nonce, non-zero balance, or non-empty code), refill `STATE_BYTES_PER_NEW_ACCOUNT × CPSB` to `state_gas_reservoir` and refund `ACCOUNT_WRITE` to the refund counter.
**3. Delegation-indicator refill.**
- If `authorization.address` is not `ZERO` (setting a delegation), refill `STATE_BYTES_PER_AUTH_BASE × CPSB` to `state_gas_reservoir` when `cur_delegated` or `pre_delegated` is `true`. In that case the 23-byte indicator slot is already occupied, so this authorization overwrites it without writing new bytes. Otherwise this authorization performs the net-new creation and its intrinsic charge stands.
- If `authorization.address` is `ZERO` (clearing the delegation), refill `STATE_BYTES_PER_AUTH_BASE × CPSB` to `state_gas_reservoir`, since the clear writes no indicator. Additionally, when `cur_delegated` is `true` and `pre_delegated` is `false`, refill another `STATE_BYTES_PER_AUTH_BASE × CPSB`: the delegation being cleared was created by an earlier authorization in this same transaction, so the chain writes zero net delegation bytes and the earlier creation charge is no longer justified. This covers patterns such as `0->a->0`, where the creation by an earlier authorization is undone by a later clearing one in the same list.

@github-actions

Copy link
Copy Markdown

The commit a2a92bc (as a parent of 2a6c84b) contains errors.
Please inspect the Run Summary for details.

@github-actions github-actions Bot added the w-ci Waiting on CI to pass label Jun 10, 2026

@misilva73 misilva73 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good. Approving now

@eth-bot eth-bot enabled auto-merge (squash) June 10, 2026 06:55

@eth-bot eth-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

All Reviewers Have Approved; Performing Automatic Merge...

@eth-bot eth-bot merged commit a374918 into ethereum:master Jun 10, 2026
11 checks passed
@github-actions github-actions Bot removed the w-ci Waiting on CI to pass label Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a-review Waiting on author to review c-update Modifies an existing proposal s-draft This EIP is a Draft t-core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants