Skip to content
Open
Changes from all 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
17 changes: 8 additions & 9 deletions EIPS/eip-8037.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,28 +159,27 @@ The same rules apply when the top-level call frame reverts or halts. Here, `stat

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

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 for each authorization, and this worst-case amount is included in `intrinsic_state_gas` and `intrinsic_regular_gas`. 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, thus, per authorization adjustments must be applied while processing the authorization list.

The adjustments enforce a single invariant: the `STATE_BYTES_PER_NEW_ACCOUNT × CPSB + ACCOUNT_WRITE` 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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This makes the calculation of the intrinsic gas cost more expensive, because we now are forced to ecrecover all authorizations on the list to account for multiple authorizations for the same address. This is much more expensive than simply doing the authorization count times the possible cost of state creation.

Although I get that this severely overprices authorizations to the same account, I do not see what the use case of doing multiple delegations in the same transaction to the same account: the end result will be a change of the delegation, or no change, and the extra delegations are paid for anyways (in gas cost of at least 12.5k currently).

In order to protect the mempool for authorization-heavy transactions I would stick with the original rule of each transaction having base intrinsic cost which can be refunded. The refund mechanism is something we can change though.

CC @lightclient who might also have some extra thoughts on this one.


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.

For each authorization, let `cur_delegated` be `true` when the authority has a non-empty delegation indicator in the **current state**, and `pre_delegated` be `true` when it had one in the **pre-transaction state**.
For each authorization, let `cur_delegated` be `true` when the authority has a non-empty delegation indicator in the **current state**, 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.
**1. Invalid authorizations.** Invalid authorizations are skipped without per-auth processing. The `(STATE_BYTES_PER_NEW_ACCOUNT + STATE_BYTES_PER_AUTH_BASE) × CPSB` portion is credited back to `intrinsic_state_gas` and the `ACCOUNT_WRITE` portion is credited back to `intrinsic_regular_gas`.

**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.
**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), the `STATE_BYTES_PER_NEW_ACCOUNT × CPSB` portion is credited back to `intrinsic_state_gas` and the `ACCOUNT_WRITE` portion is credited back to `intrinsic_regular_gas`.

**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.

`execution_state_gas_used` decreases by the corresponding amount of state-gas refills. 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`.
- If `authorization.address` is not `ZERO` (setting a delegation), the `STATE_BYTES_PER_AUTH_BASE × CPSB` portion is credited back to `intrinsic_state_gas` 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), the `STATE_BYTES_PER_AUTH_BASE × CPSB` portion is credited back to `intrinsic_state_gas`, since the clear writes no indicator. Additionally, when `cur_delegated` is `true` and `pre_delegated` is `false`, credit back another `STATE_BYTES_PER_AUTH_BASE × CPSB` to the `intrinsic_state_gas`: 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.

#### Pre-state and post-state gas validation

Expand Down
Loading