EIP-5920: PAY opcode

The zero address should not have a nonzero balance.

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.

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

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

  1. 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.
  2. 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
  3. 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.

Why would that possibly need to be done?

The call still fails.

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.

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

1 Like

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 :cry:

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.

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

1 Like

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.

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.

1 Like