EIP-2780: Reduce intrinsic cost of transactions

Discussion thread for EIP-2780: Reduce intrinsic transaction gas

(Note: 16,340 is a back-of-the-napkin estimate until we can perform better load testing on a testnet and analyze its performance)

Inspired by this thread: https://twitter.com/uriklarman/status/1281972141055934464?s=20

If the account is not initialized, then would it make a difference? For example if we consider 20000 for writing to an uninitialized storage, (16 * 110) + 2 * 800 + 2 * 20,000 + 3000 = 46,360.

You’re correct. I need to update the EIP to better outline both scenarios, highlighting the discrepancy.

I think there are two potential (and compatible) EIPs here:

  1. Significantly adjusting down the 21K gas cost for sending Tx - keeping everything else as-is.
  2. Differentiating between sending Tx to a new account vs an existing account.

I want to outline my arguments for the 1st approach, which I believe delivers 90% of the value to the Ethereum community, can be achieved much quicker, carry very little risk, complexity or debt, and is incremental in nature.
However, I don’t want to ā€œhogā€ your EIP on this, which feels to me leaning towards the 2nd approach.
Should I fork this with my own take or should I add another EIP?

@uri don’t worry about hogging the EIP, please feel free to fork it and add in your outline. I welcome it and look forward to reading your arguments. Although I don’t think there is any need worrying about this until after you complete your outline, I feel strongly that 1 should not be done unless 2 is also done. Please add yourself as an author to the EIP so you can automerge changes in the future. Thanks.

1 Like

I hope I didn’t mess up the process, but I submitted my draft for this to your branch.

This EIP was discussed by @uri in ACD #93.

The main points of feedback were:

  • generally speaking, there are other more concerning problems with the network (state size growth) that should be addressed before more EIPs which negatively impact this are accepted
  • some calculations should be double checked, especially the state size growth when new accounts are created
  • approximately 3x as many transactions will be possible within a block, so how will the network handle the increased block size?

This has been revived, but change is to

Reduce intrinsic transaction gas from 21k to 6k and charge 25k when a value transfer creates a new account

1 Like

Currently, the specs do perform checks on the sender code to ensure that it is either empty or is a valid 7702 delegation. Was just wondering how that aligns with this line from the EIP

ā€œtx.sender is charged as a cold non-code account (COLD_ACCOUNT_COST_NOCODE = 500), representing the first access and coalesced account-leaf update.ā€œ

Here some comments when reading this EIP. I find it rather hard to figure out the specification here, especially in edge cases.

I’d also suggest requiring EIP-7523: Empty accounts deprecation , this removes the need to specify rules for empty accounts as they are not on the network anymore.

Specify that a PAY-style value-move primitive, if present, does not warm its recipient and never loads code (charged at COLD_ACCOUNT_COST_NOCODE if not already warmed).

Does this also apply to SELFDESTRUCT? I think that an EIP which introduces new concepts (like PAY) should handle accounting specifics at that EIP, and it should not be handled here. It is also ambiguous since it is not clear which EIP is meant, and the EIP could also change in the future. Later in the EIP there are also references to PAY, I’d suggest to remove it because it is confusing and requires readers to have an understanding of what PAY is.

Equivalent gas-limit uplift | ā‰ˆ +20.8% | - | Effective throughput gain for ave tx usage (e.g., 60 M => ~72.5 M)

Where do these numbers come from? (What is this 20.8% and how is it calculated?)
Ah, ok later on this is explained, the table has +20.08% though (in Equivalent gas-limit increase).

Using recent totals of 163 Ggas/day and 1.7 M tx/day:

These numbers look outdated. Avg gas per tx also depends on the on-chain activities, if there is a new concept launched which demands a lot of usage per transaction then the avg gas per tx will rise a lot.

tx.sender is charged as a cold non-code account (COLD_ACCOUNT_COST_NOCODE = 500), representing the first access and coalesced account-leaf update.

Why does this not also charge STATE_UPDATE costs? We need to update the nonce of the account and we also have to pay fees (so coinbase will also be updated here if the priority fee > 0). So might even be STATE_UPDATE * 2 to account for this. Ah, nvm, this is described later, however the fact that coinbase is being paid is not accounted for here.

EIP states: The intrinsic gas accounting defined here overrides [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929)’s blanket ā€œall tx addresses are warmā€ rule., but also states * if a precompile, it is warm at tx start and charged zero..

So which addresses are warm and which are cold? Also has to be clear what the precompiles are and what not (system addresses likely not). Why an extra rule for precompiles? If we directly call into a precompile it currently does not make sense to do so on-chain as it will only execute some logic and upon returning from the precompile, the tx is done executing.

  • STATE_UPDATE counts writes at the account-leaf granularity. If a single account’s nonce and balance both change in one transition, charge one STATE_UPDATE.

What is a transition, is this during the same transaction? If it is at the same ā€œmomentā€, then what happens if I delegate via 7702 an account and I also send to that account? Are these two distinct moments or does this transfer happen at the same time (do I charge 2 STATE_UPDATE or only one?)

  1. to is not a precompile.

If you send value to an empty precompile you don’t pay GAS_NEW_ACCOUNT. Why? And why this extra rule?

How does the account work if you send to self? The fee transfers to coinbase, how are these accounted for?

Pseudocode (normative)

In order to determine the validity of a transaction, we now have to check the existance of the tx.to. I think this is incorrect, we should assume that the target is non-existant. If this turns out not to be the case then we can refund this gas (either directly at the start of the tx so it can be used for execution, or at the end of the transaction like all refunds are handled currently)

The CALL_VALUE_COST = 2,000 applies for all value-carrying calls, regardless of whether the callee’s account has already been updated earlier in the same transaction. This ensures the 2300-gas stipend mechanism remains safe and consistent for simple value forwarding and does not retroactively depend on account-update history.

This looks dangerous, it now seems possible to get the 2300 stipend paying less than 2300 gas (call to self with value, looks like this can be done infinitely). This should be addressed.

  • Cold-account touches.
  • Use COLD_ACCOUNT_COST_CODE = 2,600 when touching an account with code.
  • Use COLD_ACCOUNT_COST_NOCODE = 500 when touching an account known to have no code.

It is not clear if COLD_ACCOUNT_COST_CODE must be paid if BALANCE is called on an account with code or that COLD_ACCOUNT_COST_NOCODE should be charged.

Test cases
Interactions with EIP-7702 should be clearly noted. What if a delegation is followed, does this charge twice the COLD_ACCOUNT_COST_CODE? What if I send value to a just created 7702 account, does it charge the STATE_UPDATE? It should also be made clear if these accounts are warm or not since this EIP changes which accounts are warm. The listed precompiles are incorrect (now: 0x01 - 0x09). P256Verify at 0x100 should also be included there.

Suggestions
It is not clear to me how the new warm/cold accounting works. What if a call frame reverts, does it mark the just warmed accounts as cold again?

I think the EIP should create 2 journals to track warm/cold which works the same as EIP-2929. The current rules for pre-warmed addresses hold (coinbase, precompiles, self, to). The journal consists of account and code. If the account is loaded (BALANCE, EXTCODEHASH) add it to the warm accounts. If code is loaded (EXTCODECOPY, EXTCODESIZE, CALL, CALLCODE, DELEGATECALL, STATICCALL) then it is added to the warm code. Can add three costs, one to warm the account, one to warm the code, and another to warm the code if the account is already warm.

The transactions should be able to pay the maximum cost possible. If too much was paid upfront, it is refunded.

I think PAY should be removed and also all references to empty accounts should be removed, there are no empty accounts on Mainnet, with a reference to EIP-7523: Empty accounts deprecation.

Also regarding performance, currently to read accounts 2600 gas is charged for cold ones. If this is reduced to 500 gas for cold ones without code, then this means we can now read more than 5 times account from disk than previously. Together with block level access lists (which have to add these accounts) this is likely very undesired.