Title: EIP_5920__PAY_opcode
Category: EIPs

Author: Pandapip1
Date: 2022-11-11T18:18:51.512Z

Ethereum Improvement Proposals


EIP-5920: PAY opcode
Introduces a new opcode, PAY, to send ether to an address without calling any of its functions





Although I am an EIP editor, I do not specialize in core EIPs, and this is my first core EIP. Any feedback is appreciated!

Author: axic
Date: 2022-11-11T18:44:22.311Z

There are a number of such proposals, the latest one being



EIP-5065 Instruction for transferring ether EIPs


Abstract
Add a new instruction that transfers ether to a destination address without handing over the flow of execution to it. It should work similarly to how SELFDESTRUCT transfers ether to the destination without making a call to it. 

Motivation
From an architectural point of view, execution flow should never be handed over to an untrusted contract. Ethereum currently does not have any ideal way to transfer ether without transferring the flow of execution. People have come up with reentrancy…

Author: Pandapip1
Date: 2022-12-20T14:23:44.476Z

EIP-5065 is Stagnant (and should have been stagnant when you sent that message - it wasn’t due to an EIP-bot bug). In addition, there are quite a few issues with the linked EIP, such as the hexadecimal being invalid. I’ll ping @maxsam4, and move this EIP to review if there isn’t a response in two weeks.

Author: Pandapip1
Date: 2023-01-04T21:08:52.673Z

There has not been a response in >14 days. @axic would you mind taking a closer look before I move this to review?

Author: Pandapip1
Date: 2023-01-08T00:07:59.692Z

Adding the cancun-candidate tag.

Author: mikebolt
Date: 2023-01-15T09:05:28.670Z

Could we throw in a burn opcode too? There is an actual benefit to having one because you could leave the address out and it would shorten the contract bytecode.
If you search on solidity forum I suggested send and burn keywords. These could be the opcodes generated by those keywords.

Author: Pandapip1
Date: 2023-01-15T13:39:54.028Z

According to the current spec, sending ether to address(0) is currently allowed. How about we make that a special case that burns ether?
EDIT: Done

Author: mikebolt
Date: 2023-01-15T18:49:56.133Z

I think the EVM implementations could subtract the amount from the balance of the burner without adding it to the balance of the 0 address (or other burn address), which would be very slightly more efficient.

Author: Pandapip1
Date: 2023-01-15T19:44:38.725Z

That’s what I just added:


Transfers val wei from the executing address to the address addr. If addr is the zero address, instead, val wei is burned from the executing address.

Author: horsefacts
Date: 2023-01-20T03:05:26.216Z

I’m a big fan of this idea and the earlier EIP-5065. Any insight as to why these earlier proposals stagnated?

Author: Pandapip1
Date: 2023-01-20T17:13:14.885Z

The author went AWOL, and the proposal also had some issues, such as assigning the opcode to the “hexadecimal” value 0xFG. Ceci n’est pas hexadecimal.

Author: Tointer
Date: 2023-02-24T05:45:32.758Z

I support this EIP because:

Reentrancy attacks are very frequent and dangerous, reducing attack surface for them is great
It is consistent with ERC20 tokens, most of which not providing any ability to react to balance changes. It’s common (and costly) practice to wrap ether into WETH and transfer it this way.
Gas optimizations are nice, especially for smart contract wallets. This would also stack nicely with eip-3651
I don’t see that much use cases in having contracts being able to fully control their ETH balance in post-SELFDESTRUCT EVM
Creating opcode that would guarantee succesfull transfer through all hardforks would resolve confusion about different ways to send ether that exist today

Author: shemnon
Date: 2023-03-02T15:47:25.933Z

I’d like to raise a concern for this OPCODE, and ask the authors solicit the advice of Solidity devs and some sampling of security auditors before this proceeds to be eligible for inclusion in Cancun.
Using the PAY opcode would enable bypassing the fallback functions of smart contracts, and the default coding of non-payable functions is to revert on function calls unexpectedly sending Ether to a smart contract.  This subverts that protection.  It is possible for a smart contract to configure itself so that no valid invocation results in Ether being deposited in the account.  The motivation section of the EIP speaks to stoping reentrancy attacks, but a smart contract could configure their fallback function to accept payment and exit if they desired that ability.  Enabling the PAY opcode prevents contracts from rejecting all payments.
One alternative to preserve this behavior would be to allow PAY to only work when sending value to accounts with no code in their account, but that isn’t much different from the existing CALL mechanisms.
Another concern is the gas schedule, it is not in harmony with the warm account/cold account pattern established in EIP-2929 (which it cites) and instead proposes a different handling.  If it proceeds I would ask the gas be set up as a premium (100 gas or so) on top of the warm/cold cost, and that it interacts with the warm/cold lists like the CALL and SSTORE series opcodes do.  Specifically, i think adopting the gas pattern used in CALL would be the best pricing strategy and present the most maintainable code for clients.

Author: Pandapip1
Date: 2023-03-07T15:45:09.074Z

shemnon:

Using the PAY opcode would enable bypassing the fallback functions of smart contracts, and the default coding of non-payable functions is to revert on function calls unexpectedly sending Ether to a smart contract. This subverts that protection.


It’s already possible to do this with SELFDESTRUCT and coinbase payments, as mentioned in the motivation. This introduces no new security concerns.
See SWC-132 - Smart Contract Weakness Classification (SWC)



 shemnon:

Another concern is the gas schedule, it is not in harmony with the warm account/cold account pattern established in EIP-2929 (which it cites) and instead proposes a different handling. If it proceeds I would ask the gas be set up as a premium (100 gas or so) on top of the warm/cold cost, and that it interacts with the warm/cold lists like the CALL and SSTORE series opcodes do. Specifically, i think adopting the gas pattern used in CALL would be the best pricing strategy and present the most maintainable code for clients.


As mentioned in the specification, the EIP-2929 costs are applied.

Author: shemnon
Date: 2023-03-07T16:41:37.987Z

As mentioned in the specification, the EIP-2929 costs are applied.

New account costs are not being applied (25K when paying to an address with an empty account)

Author: Pandapip1
Date: 2023-03-07T16:42:16.901Z

shemnon:

New account costs are not being applied (25K when paying to an empty address)


Are they not? Oops then, I’ll fix that.

Author: mikebolt
Date: 2023-03-13T21:56:11.160Z

This is a good point but I would argue that fallbacks are potentially dangerous and should be deprecated. However, this should definitely be noted as a big caveat in the documentation for the new PAY opcode.

Author: asdnlasdlkj
Date: 2023-03-14T03:30:17.595Z

What reason would someone have to burn ETH?

Author: Pandapip1
Date: 2023-03-14T21:45:08.089Z

Beats me. Now that I think about it, it would probably make more sense for the call to fail, to avoid potential bug-related losses.

Author: asdnlasdlkj
Date: 2023-03-14T23:35:35.335Z

It’s probably best not to make any kind of special exemption for the zero address, that’s how all the call opcodes already operate

Author: Pandapip1
Date: 2023-03-15T14:22:33.151Z

The zero address should not have a nonzero balance.

Author: asdnlasdlkj
Date: 2023-03-15T22:14:10.168Z

It already does. And adding a single special case for reversion just replaces one class of potential bugs with another. If an address gets set to zero and is unchangeable, then any function that calls PAY on that address will always revert and effectively be uncallable. If that function needs to be called as some necessary phase of the contract, then your contract is now permanently frozen and the ETH is inaccessible.
E.g. imagine a lottery contract where people can set their payout address when they buy a ticket. A function called startNewRound() checks if the last round is over, PAYs out ETH to the winner, and starts the next round anew. If the winner sets their payout address to 0, then startNewRound() will always revert when attempting to make the payout, and the lottery is now frozen with no future rounds possible.
It makes PAY a lot easier to reason about if it succeeds no matter what, as long as you pass in a valid address and ETH amount. If you want your contract to prevent payouts to the zero address, just add in some custom check like require(addr != address(0)). This is semantically equivalent to how people already make payouts with call(), transfer(), and send(), and there is no reason to change it for just this single opcode. Even less so, in fact, since the point of PAY is pretty much to bypass any checks and force an address to receive ETH.

Author: Pandapip1
Date: 2023-03-16T18:09:52.592Z

That makes sense. What’s currently written probably makes more sense then.

Author: asdnlasdlkj
Date: 2023-03-17T02:59:09.695Z

burning for payments to address(0) is still a bad idea for a few reasons:

when a contract PAYs to some address, it might operate under the assumption that the balance of that address will increase. E.g. the contract PAYs some amount ether to some receiving address, then at a later point checks that the receiving address’s balance has increased by that amount. If the receiver is address(0), then that check will fail, even though the payment was made.
the existing CALL opcode does not burn ETH when the recipient is address(0); changing the semantics of ETH transfers for this new opcode will be confusing
it’s pointless; the only reason I can think of for someone to burn ETH is executing a malicious attack using the situation described in (1)

Again, it’s best for no exceptions to be made for the zero address.

Author: Pandapip1
Date: 2023-03-17T03:00:27.662Z

asdnlasdlkj:

when a contract PAYs to some address, it might operate under the assumption that the balance of that address will increase. E.g. the contract PAYs some amount ether to some receiving address, then at a later point checks that the receiving address’s balance has increased by that amount. If the receiver is address(0), then that check will fail, even though the payment was made.


Why would that possibly need to be done?



 asdnlasdlkj:

the existing CALL opcode does not burn ETH when the recipient is address(0); changing the semantics of ETH transfers for this new opcode will be confusing


The call still fails.

Author: asdnlasdlkj
Date: 2023-03-17T03:28:46.657Z

The call still fails.

Just checked on Goerli, and CALL does not fail when the receiver is address(0). Here’s my tx sending 0.01 ETH there from a contract:
https://goerli.etherscan.io/tx/0x785a1484424779a040b3c6213e01f162b43c5fc4c5d9780d8ed117d332b595b4
And it increased the balance of address(0) from “11,090.383…” to “11,090.393…”, so no burn. So to bring the opcodes semantically in-line would require not giving address(0) any special treatment.

Why would that possibly need to be done?

Dunno, but someone might still end up doing it. Like, if there’s a bunch of complicated conditional code that  decides whether or not to make a payment, so the dev decides that the simplest way to check whether or not the payment was made is just to query the receiver’s balance. My point stands that a burn is pointless, and (hypothetically at least) could lead to bugs.

Author: Pandapip1
Date: 2023-03-17T03:29:15.012Z

asdnlasdlkj:

Just checked on Goerli, and CALL does not fail when the receiver is address(0). Here’s my tx sending 0.01 ETH there from a contract:


Never mind then. It seems that the special case is not needed.

Author: jochem-brouwer
Date: 2023-05-27T14:31:44.526Z

Since this EIP is now CFId, I would like a clarfication if this new “burn rule” for the zero address still holds. It seems that there is confusion if it is ok to transfer eth to the zero address, and this is ok, and it behaves just as normal. But this EIP introduces a rule to burn the eth…? Is this still desired, or is this rule going to be removed (I have no strong opinion here).
I also think that the gas section is not clear. From current EIP:

Gas pricing


The gas pricing is that of a CALL with a positive msg.value, but without any memory expansion costs or “gas sent with call” costs, with a gas reduction of 500 to compensate for the reduced amount of computation.

If we review CALL regarding the relevant gas costs for PAY: the CALL gas price currently depends on: do we transfer value (9000), is it warm or cold (100 for warm, 2600 for cold), and do we create a new account (25000).
I do not see how this has a gas reduction of 500 in any situation here, to reach the base gas costs of the PAY opcode of 3000.
At this point I do not understand how to implement the gas costs

Author: Pandapip1
Date: 2023-05-27T15:47:46.785Z

jochem-brouwer:

If we review CALL regarding the relevant gas costs for PAY: the CALL gas price currently depends on: do we transfer value (9000), is it warm or cold (100 for warm, 2600 for cold), and do we create a new account (25000).


Clearly I forgot to fix the pricing. The cost of the PAY opcode should 100 for warm addresses, 2600 for cold addresses, and 25000 for new accounts, and should have a base gas cost of 9000.



 jochem-brouwer:

Since this EIP is now CFId, I would like a clarfication if this new “burn rule” for the zero address still holds.


I clearly forgot to push quite a few changes. Fixed.

Author: jwasinger
Date: 2023-06-08T15:13:34.101Z

Given that EIP 6780 is included in Cancun, and it preserves the unconditional send aspect of SELFDESTRUCT.  It seems that the functionality of this EIP can be implemented as a contract with low gas overhead.

Author: Pandapip1
Date: 2023-06-11T15:02:16.504Z

While this is true, it creates unnecessary work for the EVM. Additionally, ‘sending ether’ is such a common task for contracts, and the behavior of selfdestruct is still very much TBD.

Author: wjmelements
Date: 2025-02-19T03:35:17.764Z

I think EOF needs PAY for security if it won’t have zero-gas CALL.
It’s useful security-wise to be able to block re-entry on an ether payment. This is one of the reasons that the callvalue stipend is not enough gas to SSTORE or call with value. While re-entry can be protected with TLOAD /TSTORE , simple ether payments are the main reason I still use less than GAS for the gas parameter of CALL . Especially with eip-7702 you do not want to grant control to the recipient just to pay them. They might even be an account unknown to you, as is the case for COINBASE payments.
PAY is in the spirit of EOF because it doesn’t have a gas parameter. It doesn’t enable anything not already possible, because it is still possible to pay an account without its permission via COINBASE priority fees.



 jwasinger:

It seems that the functionality of this EIP can be implemented as a contract with low gas overhead.


The workaround is to EXTCALL the value into a legacy contract and CALL with 0 gas. The overhead is 9000 gas. But PAY is strictly better because there is only one value transfer.



 jwasinger:

it preserves the unconditional send aspect of SELFDESTRUCT


This aspect is still preserved by COINBASE priority fees. It is impossible to reject these fees and you aren’t notified.
@Pandapip1 Please update the opcode so it does not conflict with one of the ones currently used by EOF.

Author: charles-cooper
Date: 2025-02-19T14:38:46.434Z

I proposed this EIP for inclusion into Prague. But client teams blocked it without any discussion. I think @shemnon put together a piece summarizing each client team’s position on all of the Prague EIPs, but I can’t find it offhand.

Author: shemnon
Date: 2025-02-19T16:28:10.174Z

What we were lacking was the rationale for why it was needed, what hole it filled.  It wasn’t until last night that the gas=0 for CALL pattern was shared, and how gas introspection broken that.  So, that was new information material to the opcodes utility.
Last year we were by default resistant to new additions that didn’t fill holes due to pectra scheduling pressure.
We added it in the call this morning to the universally nebulous “post eof-devnet-1” bucket because we don’t have much clarity on when Fusaka will ship.  Once we get devnet-1 shipped we can determine where it lands. This bucket includes Osaka proposed EIPs as well as EIPs not proposed for Osaka and likely targeting Amsterdam.
If inclusion in Osaka is a goal, then this request should also be following the EIP-7723 process, and you or @wjmelements should post a PR to the osaka meta (EIP-7607) and attend the next two ACDE calls where my understanding is we will set and freeze Osaka scope. Be aware that the vibe is that all new EIPs proposed for Fusaka not already known is “defer to Amsterdam” at this point, and that advocacy will need to overcome this position.  The door is not closed yet, at least not officially.

Author: wjmelements
Date: 2025-02-20T04:04:51.872Z

shemnon:

should post a PR to the osaka meta (EIP-7607)




github.com/ethereum/EIPs








Propose EIP-5920 for inclusion in Osaka


ethereum:master ← wjmelements:osaka-pay



          opened 04:03AM - 20 Feb 25 UTC




            wjmelements
          



+1
-0






Reviewer @timbeiko
I think EOF needs the PAY opcode to maintain payment security…, so I propose it will be considered for Osaka.
#### Changes
* add eip-5920 to proposed for inclusion

Author: wjmelements
Date: 2025-02-20T04:19:59.351Z

Opcode update, also mentioning EXTCALL and priority fees: Update EIP-5920: Update PAY Opcode, Motivation, and Rationale by wjmelements · Pull Request #9389 · ethereum/EIPs · GitHub

Author: wjmelements
Date: 2025-02-26T07:30:38.317Z

Sharing my presentation for this week’s ACDE here:

docs.google.com



EOF: PAY Security
EOF: PAY Security

Author: wjmelements
Date: 2025-02-27T02:37:49.224Z

Copying the address halting behavior from EXTCALL (EIP-7069):


github.com/ethereum/EIPs








Update EIP-5920: PAY Opcode Recipient Address


ethereum:master ← wjmelements:pay-opcode-recipient-address



          opened 02:36AM - 27 Feb 25 UTC




            wjmelements
          



+10
-0






Reviewers @Pandapip1 @SamWilsn @xinbenlv
This future-proofs PAY for Address Spac…e Extension, using the same method as `EXTCALL`.
#### Changes
* copy `extcall` address halting behavior
* define gas cost timing relative to exceptional halt

Author: charles-cooper
Date: 2025-03-01T10:20:19.784Z

shemnon:

What we were lacking was the rationale for why it was needed, what hole it filled.


And that was brought up where? What is the point of requiring EIP authors to post to ethereum magicians if these objections won’t actually be discussed in ethereum magicians. By the way, the rationale has already been covered in this thread; and is also right there in the EIP:

Currently, to send ether to an address requires you to call into that address, which transfers execution context to that address, which creates several issues:

First of all, it opens a reentrancy attack vector, as the recipient can call back into the sender. More generally, the recipient can unilaterally execute arbitrary state changes, limited only by the gas stipend, which is not desirable from the point of view of the sender.
Secondly, it opens a DoS vector. Contracts which want to send ether must be cognizant of the possibility that the recipient will run out of gas or revert.
Finally, the CALL opcode is needlessly expensive for simple ether transfers, as it requires the memory and stack to be expanded, the recipient’s full data including code and memory to be loaded, and finally needs to execute a call, which might do other unintentional operations. Having a dedicated opcode for ether transfers solves all of these issues, and would be a useful addition to the EVM.


It seems like every other ACD call, client teams wonder why engagement from application and compiler teams is low. This is why – in order to advocate for changes you need to attend hours of calls (and most of the content is irrelevant to outsiders), and in the end, the changes you advocate for can and will be rejected by an opaque, backroom process with little-to-no feedback.

Author: shemnon
Date: 2025-03-02T13:58:10.744Z

I was trying to point out how it fits into the current EOF plan—what specific gap it addresses. The reasons you mentioned are more general benefits, not tied directly to EOF. What stood out as new was the gas=0 use case, which wasn’t included in the rationale you pulled from the EIP.
When it comes to proposing new opcodes, I often bring up TSTORE/TLOAD as a great example. The person championing it wasn’t a developer from a client team, yet they still managed to provide implementations across all four clients, along with solid reference tests (including pre-EEST tests). Client developers are usually stretched thin, so showing up with working implementations and strong tests is often the best way to get them to seriously evaluate an EIP on its own merits. From what I’ve seen, the one time this approach was used, it got included in the fork after the current one under consideration.

Author: charles-cooper
Date: 2025-03-14T16:42:12.087Z

What stood out as new was the gas=0 use case, which wasn’t included in the rationale you pulled from the EIP.

It’s the first item in the rationale:



 charles-cooper:


First of all, it opens a reentrancy attack vector, as the recipient can call back into the sender. More generally, the recipient can unilaterally execute arbitrary state changes, limited only by the gas stipend, which is not desirable from the point of view of the sender.






 shemnon:

When it comes to proposing new opcodes, I often bring up TSTORE/TLOAD as a great example. The person championing it wasn’t a developer from a client team, yet they still managed to provide implementations across all four clients, along with solid reference tests (including pre-EEST tests). Client developers are usually stretched thin, so showing up with working implementations and strong tests is often the best way to get them to seriously evaluate an EIP on its own merits. From what I’ve seen, the one time this approach was used, it got included in the fork after the current one under consideration.


Not sure what to say here. My objection was not the fact of the EIP being declined, more that the EIP was declined for opaque reasons (apparently, due to backroom discussions) – rather than given in the EIP thread, where concerns or misunderstandings could have been discussed+addressed.

Author: benaadams
Date: 2025-03-18T08:24:03.354Z

Can we add that its invalid to use the opcode in a static call?
Otherwise ambiguity as to whether is fine if the amount transferred is 0

Author: benaadams
Date: 2025-03-18T08:32:44.493Z

Also what is the situation/result if there isn’t enough balance in sender?
Is it a success/fail value to stack; or is it a contract fail exception (=> outofgas)?
Assuming is 0 to stack on fail and 1 to stack on success (as per CALL et al)

Author: benaadams
Date: 2025-03-18T09:01:05.455Z

Implementation in Nethermind Implement EIP-5920: PAY by benaadams · Pull Request #8382 · NethermindEth/nethermind · GitHub

Author: charles-cooper
Date: 2025-03-24T19:44:33.164Z

I spent a bit of time thinking about this, and from my perspective, I think both are reasonable. Unlike CALL, you can’t get a failure for a reason outside of your control, since you can check how much balance you have before issuing the opcode. The only thing I can think of is users may want to do this to avoid calling SELFBALANCE.
Overall, I would consider exceptional halting as slightly safer for users, but as you said, pushing 1 or 0 to stack is more consistent with the implementation for CALL.
Another, third option, would be to send min(selfbalance, <specified value>). Not sure how useful it is, but it allows you to send full balance with pay(uint256_max).

Author: benaadams
Date: 2025-03-24T20:23:12.029Z

charles-cooper:

I would consider exceptional halting as slightly safer for users,


Is generally the more expensive option though (consume all gas); rather than cheaper check return in code and revert?

Author: charles-cooper
Date: 2025-03-24T21:52:51.641Z

Safer in the sense that it’s harder to screw up - there is no way to “forget” to handle the invalid input case, the EVM handles it for you.
Also I don’t really think the amount of gas consumed is an issue. Generally if the argument to PAY is higher than SELFBALANCE, it indicates a user bug (same as stack underflow or other invalid opcode). I think that’s the point I was trying to make about “can’t get a failure for a reason outside of your control”. Like even with the exceptional-halting scheme, you can avoid the exceptional halt with DUP1<value> SELFBALANCE LT PUSH2 [join] JUMPI PAY [join] JUMPDEST.
I still think that consistency with CALL of the 1/0 scheme (and let’s face it, consistency with most of the rest of the EVM) is a pretty strong argument. I’ll let others chime in here.

Author: jochem-brouwer
Date: 2025-03-24T22:45:32.615Z

I would go for the CALL scheme also, as you mentioned these exceptional halts are easy to prevent. In CALL if you try to send more than current balance, it exceptionally halts (consumes all gas in current frame) and puts 0 on the stack of the caller. This is consistent, and I like it.
The EIP reads:
Does addr exist or is val zero?
* If yes to either, zero;
* Otherwise, GAS_NEW_ACCOUNT.

This reads to me as if the case that val is nonzero always GAS_NEW_ACCOUNT must be charged which seems wrong. It should only charge GAS_NEW_ACCOUNT if value is nonzero and the addr is non-existent. NVM, I now understand the word “either” 
Also brings the question for the val=0 case:

Is this allowed in STATICCALL? (I’d argue: just ban the entire opcode in static context)
Does this perform the warm/cold account check? (should not do this and not charge for it)
Does this put the target in warm (should not do this)

I like this EIP a lot because this is extremely good UX in the case a smart contract developer wants to send ETH to an account without having to worry about any code which might be executed on that account. Also for starting smart contract developers it does not make much sense that you have to worry about executing code if you want to send someone ETH. This is like the “safe way” to transfer ETH. I like this a lot, this should have been introduced to EVM much earlier.
Without PAY it is not possible to reach this behavior (well, techncially there is: CREATE a contract with X value and then selfdestruct X to pay A, but this is extremely expensive and will likely also not work at some fork in the future) because all CALL types will execute code, and in order to succesfully transfer ETH you must succeed the call frame.

Author: wjmelements
Date: 2025-03-28T13:32:35.841Z

jochem-brouwer:

Is this allowed in STATICCALL? (I’d argue: just ban the entire opcode in static context)


There is a proposal to add logs for ether transfers, even if the amount is zero. Logs should not be created in a readonly context like STATICCALL.

Author: jochem-brouwer
Date: 2025-04-03T05:31:55.977Z

Hey all, I just realized a super nice feature of this opcode, and this is to mark addresses as warm without running any code on them (PAY the address-to-warm 0!).
I opened 2 PRs:
Clarify behavior in STATIC mode
Push transfer status number (0 or 1) to stack like CALL (CC @charles-cooper)
Would love to get some feedback on those, thanks

Author: shemnon
Date: 2025-04-03T18:08:18.346Z

BALANCE also warms the target address and is not considered a “writing” operation, and presents no STATICCALL interaction issues.

Author: jochem-brouwer
Date: 2025-04-03T18:23:29.311Z

Ah right, good point, forgot about BALANCE

Author: gurukamath
Date: 2025-04-07T17:26:02.733Z

The EIP execution spec says


Pops two values from the stack: addr then val.
Charges the gas cost detailed below.
Exceptionally halts if addr has any of the high 12 bytes set to a non-zero value (i.e. it does not contain a 20-byte address).


I am curious about the order of operations between charging the gas and exceptional halting when address has non-zero high 12 bytes.
One of the components of the gas cost is if the to_address is warm or not and if the address has high bytes set to non-zero values, performing this check does not make sense. Would it more sense to provide more clarification on this?

Author: shemnon
Date: 2025-04-07T18:38:33.441Z

The halting because of ASE should come first for performance reasons.
The cost wouldn’t matter as an exceptional halt consumes all available gas.
If the cache kept 32-byte addresses it wouldn’t matter because the only way to access them currently is via opcodes that only revert when they see them, so warm/cold state is not observable.

Author: jochem-brouwer
Date: 2025-04-20T16:20:27.694Z

Could someone review my two open PRs?


github.com/ethereum/EIPs








Update EIP-5920: add clarification to ban opcode in STATIC mode


master ← jochem-brouwer:pay-static



          opened 05:22AM - 03 Apr 25 UTC




            jochem-brouwer
          



+1
-0






This PR updates the PAY opcode PR such that it is self-consistent: currently, th…ere is no rule about what happens in a `STATIC` context (`STATIC` context/flag: https://eips.ethereum.org/EIPS/eip-214#specification). It is clear that using PAY in a `STATIC` context is usually not allowed (it changes state), but what about the case if the value sent is 0? I have followed the way that CALL handles this: if the value is zero, it is fine: if the value is nonzero and we are in a `STATIC` context then exceptionally abort.








github.com/ethereum/EIPs








Update EIP-5920: change to call-like functionality


master ← jochem-brouwer:pay-returnvalue



          opened 05:28AM - 03 Apr 25 UTC




            jochem-brouwer
          



+10
-3






This PR changes EIP-5920:

- Mimics the behavior of CALL in terms of pushing i…tems to the stack
- Changes the order of the semantics of the opcode a bit, and this matters, because if a transfer would not succeed before these changes, then the target address stays cold. This is not the case for CALL (it will warm).

Please verify that:

- [ ] When using the CALL opcode and it is tried to send more value than the current account, then the just-created call frame exceptionally aborts and 0 is pushed to the stack
- [ ] When using CALL (the target address is cold) and the inner frame throws (for instance sending more value than in current account), the address-to-be-called is still in warm afterwards.

Author: Arvolear
Date: 2025-04-26T12:07:03.516Z

What if the addr is the address(this)? Should pay be a no-op charging only WARM_STORAGE_READ_COST in this case (like sstoring the same storage value)?