Title: EVM_Object_Format__EOF_
Category: EIPs

Author: axic
Date: 2021-03-16T16:41:15.498Z

Last week I have shared a document on the Eth R&D discord explaining some background on why some EVM changes are hard and motivation for improving the situation:


HackMD



EVM encapsulation format - HackMD
# EVM encapsulation format  > Name suggestion: *EOF – EVM Object Format* > Or could go the GNU way:





It also suggests a container format for EVM, which would enable further improvements, such as removing jumpdests, moving to static jumps, etc. While the document does not aim to provide a final, implementable solution, it is a good one for discussions.

Author: axic
Date: 2021-03-16T16:42:30.296Z

@chriseth published his initial opinion here:


notes.ethereum.org



Thoughts on EVM encapsulation format - HackMD

Author: axic
Date: 2021-03-16T16:43:20.140Z

And with @chfast and @gumb0 we have looked into some other potential changes this could bring in the long term:


notes.ethereum.org



evm object format (eof) discussion - HackMD

Author: MicahZoltu
Date: 2021-04-30T15:17:21.696Z

Can we change this EIP to just say any contract deployment that has an unused opcode as the first byte is invalid and should revert?  Rather than just have a single opcode that is considered invalid?  There is no good reason to start a contract with an unused opcode, and maybe we’ll want the others later.

Author: chfast
Date: 2021-04-30T17:30:43.702Z

This is technically possible, but this would block much bigger set of “data contracts” from being deployed, therefore there would be bigger chance to break some existing factory contracts.
Data contracts are deployed bytecodes with no intention for execution from which you can read data with EXTCODECOPY.
On the other hand, this increases our chance to have shorter EOF prefix.
Some middle ground would be to reject some fixed range based on the analysis of currently deployed contracts. E.g. D0-EF.

Author: MicahZoltu
Date: 2021-05-01T04:02:06.058Z

I wonder if we should enshrine data contracts somehow?  Maybe have a contract prefix specifically for them?

Author: jochem-brouwer
Date: 2021-05-02T14:00:50.418Z

If we disallow contracts starting with 0xEF then I’d say we should also explicitly mark 0xEF as an INVALID opcode (or alternatively if we want to use 0xEF at some point it should use  >=1 stack items), otherwise we get these weird situations that contracts want to start using this opcode but have to push a dummy item to stack first.
I am assuming that this only disallows the code deposit starting with 0xEF, we could technically still try to CREATE a contract where the deploycode starts with 0xEF? (Just for clarification here)

Author: axic
Date: 2021-05-03T10:29:26.733Z

I see a much bigger risk of this change not making into London if the range is extended. I think it has merits to reject any contract with invalid starting byte, but perhaps that can be done separately and even after London.

Author: axic
Date: 2021-05-13T17:47:21.641Z

The first step towards this is defined under EIP-3541:

Ethereum Improvement Proposals


EIP-3541: Reject new contracts starting with the 0xEF byte
Details on Ethereum Improvement Proposal 3541 (EIP-3541): Reject new contracts starting with the 0xEF byte





It is also considered for the upcoming London fork.

Author: Vie
Date: 2021-05-21T03:14:02.385Z

So, when we try to add TokenA and TokenB to uniswap v2, we maybe got error as create2 create a pair start with 0xEF?

Author: axic
Date: 2021-05-21T14:04:08.421Z

No working contract starts with 0xEF.

Author: Vie
Date: 2021-05-27T16:35:43.598Z

Ok, I’ll try to fix out that.

Author: axic
Date: 2021-06-09T21:30:48.138Z

This first step has been accepted for London.
The next step was proposed for Shanghai:

Ethereum Improvement Proposals


EIP-3540: EVM Object Format (EOF) v1
Details on Ethereum Improvement Proposal 3540 (EIP-3540): EVM Object Format (EOF) v1





This introduces code - data separation as the main tangible benefit to users.

Author: poojaranjan
Date: 2021-06-15T01:45:53.379Z

Watch an overview of EIP-3540 & EIP-3541 by @axic @chfast @gumb0.

Author: axic
Date: 2021-06-16T17:24:24.579Z

There is also a new overview document here:


HackMD



Everything about the EVM Object Format (EOF) - HackMD
# Everything about the EVM Object Format (EOF)  The aim of this document is to serve as an explainer





This gives an explanation of why two hard forks, gives a roadmap of different features we investigate (Shanghai, Cancun, and beyond), and links to all relevant resources.
(We plan to drop the two-hard fork explainer from EIP-3540 to simplify it.)

Author: axic
Date: 2021-07-20T09:10:40.312Z

As a further step, we suggest to introduce code validation with EOF: EIP-3670: EOF - Code Validation
It is proposed as a separate EIP to keep concerns separated and the EIPs shorter.

Author: axic
Date: 2021-08-07T13:30:30.375Z

A potential way to remove the need for jumpdest analysis at execution time was published here: EIP-3690: EOF - JUMPDEST Table

Author: axic
Date: 2021-09-23T13:57:44.161Z

A proposal made possible by EOF is to have static jumps:



EIP-4200: Static relative jumps Core EIPs


    This is the discussion topic for 

This proposal started as a comment back in February and was one of the reasons which kicked off our journey with EVM Object Format (EOF). In the past few months @gumb0 has been working on experimenting and validating this in evmone (PR here), but now is the time to release an actual EIP.

Author: chfast
Date: 2021-10-23T15:47:18.688Z

After the London upgrade which included EIP-3541 we were able to collect all previously deployed bytecodes starting with the 0xEF byte. The following document has information about collected data and 2 possible EOF prefix values. We recommend the use 0xEF00 2-byte prefix.
EOF Prefix Selection

Author: MicahZoltu
Date: 2021-10-24T09:17:15.586Z

Has anyone looked into figuring out what https://etherscan.io/address/0xca7bf67ab492b49806e24b6e2e4ec105183caa01 does?
Given that there are only 3 contracts that start with 0xEF and two of them have (in theory) reachable owners/authors and the third doesn’t actually do anything, I think we should explore the option of an irregular state change to get rid of them so we have a completely clear 0xEF space.
This is especially true since all 3 of them were created after EIP-3541 was proposed, and I suspect at least two of them (the contracts that merely deployed 0xEF) were created explicitly to cause problems for us (the third possibly as well, but that one is slightly less clear).

Author: axic
Date: 2021-10-24T10:06:26.076Z

I asked about the long contract here: https://twitter.com/alexberegszaszi/status/1452210984987369474
Writing an EIP for the irregular state change is easy enough, ACD may or may accept it, but it definitely is yet another hurdle to overcome.
I’d be happy if we could get rid of those three contracts, if someone champions that change.

Author: 0age
Date: 2021-10-25T15:13:44.566Z

Hey there — so that third contract was deployed by me, and it’s just a “farewell” message to 0xef before EIP-3541 took effect. Definitely did not intend to cause any problems with it and would have no qualms whatsoever with the contract being removed if it makes things easier / cleaner.
f09f918b => 
f09f9fa9 =>

Author: axic
Date: 2021-10-25T20:53:29.143Z

0age:

Definitely did not intend to cause any problems with it and would have no qualms whatsoever with the contract being removed if it makes things easier / cleaner.


Thanks for the response!
It actually complicates matters, so removing it would be helpful. The other two contracts are annoying too, but cause less issue.

Author: holiman
Date: 2022-01-13T12:08:23.502Z

Reviving this now, after having reviewed the EIP a bit. My notes:
The terminator is not particularly well specificed. The only thing it says, is that “If the terminator is encountered, section size MUST NOT follow.”. The>
I suggest that the “Validation rules” are clarified further re terminator.
Example cases. Which of these are ‘valid’ and which are not?
In each case, where it’s not valid, there should be a corresponding rule in the ‘Validation rules’.
// 1.
// 0-size code section, with terminator, no code)

0xEF0001 0x01 0x0000 0x00

// 2.
// 0-size code section, no terminator, no code)

0xEF0001 0x01 0x0000

// 3.
// 0-size code,0-size data, with terminator

0xEF0001 0x01 0x0000 0x02 0x0000 0x00

// 4.
// 0-size code,0-size data, no terminator

0xEF0001 0x01 0x0000 0x02 0x0000

// 5.
// 1-size code,0-size data
0xEF0001 0x01 0x0001 0x02 0x0000 0x00 0xEF


// 6.
// 1-size code,16-size data, but actual code is smaller than that. Does the 'infinite field of zeroes' apply?
0xEF0001 0x01 0x0000 0x02 0x0010 0x00 0xdada

Author: chfast
Date: 2022-01-13T17:31:56.203Z

Thanks for the comment.
The intended meaning is that terminator byte is mandatory. But I can agree the specification is not clear about it nor what terminator is.
By following this reasoning, all examples without terminator are invalid (2,4).
It is also specified that "section_size MUST NOT be 0". We decided to include this rule to eliminate two encodings for the same effect - empty section. If a section is empty this forces omitting its header as well. This makes (1,3,5) invalid.
Finally, we also wanted all bytes of a section to be present (no ‘infinite field of zeroes’) but looks this rule is not articulated. That would make 6 invalid. We also don’t allow any bytes outside of sections specified by headers: “Stray bytes outside of sections MUST NOT be present. This includes trailing bytes after the last section.”
In summary, EOF container requires: no implicit bytes, no additional bytes, shorter encoding if two options are possible. The specification does not express this perfectly yet and we will apply fixes.

Author: holiman
Date: 2022-01-17T08:47:11.881Z

Thanks. A follow-up question/observation then.

If a code section must be present,
And a section must not be empty,

Then it’s not possible to deploy a data-only contract. This is IMO a side-effect which deserves mention in the EIP.

Author: axic
Date: 2022-02-03T12:01:09.849Z

holiman:

Then it’s not possible to deploy a data-only contract. This is IMO a side-effect which deserves mention in the EIP.


This was actually desired, though it is still possible to have a contract with a single instruction in the code section (such as INVALID) to have a determined execution path. Agree we should document it.

Author: axic
Date: 2022-02-03T12:02:42.753Z

As a further step to replace dynamic jumps, we propose EIP-4750 (EIP-4750: EOF Functions). This could be adopted together with EIP-4200 (EIP-4200: Static relative jumps) to remove the need for dynamic jumps.

Author: jochem-brouwer
Date: 2022-02-16T16:31:22.307Z

I cannot deploy an EOF where codesize is zero. What if I want to deploy an EOF where I only want the data section and no code?

Author: jochem-brouwer
Date: 2022-02-16T16:35:28.137Z

If PC goes outside of the code section bounds, execution aborts with failure.


This is not entirely in-line with current semantics when PC runs out of code. On a contract where PC goes out-of-bounds the STOP instruction is executed. I am also fairly sure that with PUSH if not all data can be read (e.g. the contract 60, PUSH1 where it is not clear what data should be pushed) then also STOP is executed.
Current EIP changes this behavior; if you run out-of-bounds or PUSH when it is not clear what bytes you should push now goes OOG. Is this intended?

Author: gumb0
Date: 2022-02-16T17:55:40.590Z

jochem-brouwer:

Current EIP changes this behavior; if you run out-of-bounds or PUSH when it is not clear what bytes you should push now goes OOG. Is this intended?


EIP-3670 requires that the code section ends with a terminating instruction, this makes running out-of-bounds impossible and code ending with truncated PUSH data invalid code.

Author: axic
Date: 2022-02-17T09:35:50.881Z

jochem-brouwer:

I cannot deploy an EOF where codesize is zero. What if I want to deploy an EOF where I only want the data section and no code?


Data contracts should have INVALID as the only instruction in the code section to signal they are not executable.

Author: chfast
Date: 2022-02-17T15:06:54.741Z

Thanks @holiman @jochem-brouwer for your comments and questions.
Can you check if this spec re-wording change make it any better?

github.com/ethereum/EIPs








EIP-3540: container and validation spec clarification, new code


master ← ipsilon:eip-3540-update



          opened 03:04PM - 17 Feb 22 UTC




            chfast
          



+82
-70

Author: acolytec3
Date: 2022-03-02T20:10:26.450Z

Can I just confirm that below from the “Contract Creation restrictions” section of the EIP

This adds two validation steps in the contract creation, any of it failing will result in contract creation failure.

implies that when an execution client is performing the deploy validation of the EOF1 container/header as part of a contract creation transaction, if the code is invalid, the contract should just not be created (but no gas charged for this contract creation failure), not that an exceptional abort should occur and all remaining gas should be consumed.  Is that correct (per this comment on the related tests PR from @gumb0)?

Author: chfast
Date: 2022-04-15T08:06:47.581Z

The EIP wording has been updated based on the feedback. No functional changes. EIP-3540: container and validation spec clarification, new code by chfast · Pull Request #4822 · ethereum/EIPs · GitHub

Author: charles-cooper
Date: 2022-06-22T14:30:59.795Z

Data contracts should have INVALID as the only instruction in the code section to signal they are not executable.

I think the requirement that an EOF1 container requires a code section (or any sections at all) is a bit restrictive. In light of SSTORE2-like patterns, “data-only” contracts might be quite popular, and requiring a code section even with a single instruction incurs (if I am reading correctly) 4 bytes of overhead.
I suggest that the spec be modified so that the code section is not required. CALL-ing an EOF1 container with no code section has the same semantics as calling a legacy contract with no code. In other words, if the code section is not explicitly included, the code of the container is implied to be empty.
(I originally considered that an EOF1 container with no explicit code section should imply code with a single 0xFE (INVALID) instruction. But that has some weird consistency issues with EXTCODEHASH and EXTCODESIZE).
I also suggest enabling completely empty EOF1 containers, e.g. EOF1 containers whose contents are EF0001 00. It may be desirable to deploy empty contracts for some reason (e.g. as part of some address marking scheme, especially if a GETNONCE instruction ever becomes available).

Author: axic
Date: 2022-08-19T21:41:11.937Z

As a somewhat concluding step we propose EIP-5450: EOF - Stack Validation (building on top of all previous EOF EIPs) to reduce the number of checks to be done in the interpreter loop.

Author: chfast
Date: 2022-09-30T17:09:33.423Z

New EIP idea to remove already deployed invalid EOF code.
If account is called and its code starts with EF but is not valid EOF the execution is done normally (it will result in exception) but additionally the account’s code is deleted (or alternatively account is added to selfdestruct list).
This is similar to touching “empty” accounts in order to remove them from the state.
The disadvantage is that we have to validate EOF code before every execution. However, after all invalid EOF code is deleted from the state the EIP can be silently disabled.
The Mainnet has only 3 such contracts but there are rumors that some L2s have more.

Author: MicahZoltu
Date: 2022-10-01T11:44:46.103Z

I’m not a client dev so my intuition on this may be off, but it feels like an irregular state change to just remove those 3 accounts would be the simpler option.

Author: wjmelements
Date: 2022-10-06T00:48:22.006Z

EIP-3540 [Rationale]:
Finally, create transaction must be allowed to contain legacy initcode and deploy legacy code because otherwise there is no transition period allowing upgrading transaction signing tools. Deprecating such transactions may be considered in future.

I’m strongly opposed to breaking legacy initcode (eg UniswapV2Factory), but deprecation, perhaps via gas discrimination, could be beneficial to encourage adoption of EOF, once there is a demonstrated performance benefit.

Author: wjmelements
Date: 2022-10-06T00:55:41.392Z

chfast:

If account is called and its code starts with EF but is not valid EOF the execution is done normally (it will result in exception) but additionally the account’s code is deleted (or alternatively account is added to selfdestruct list).


There are other reasons to deploy bytecode besides executing it. I once used it to store a large amount of calldata for a subsequent operation. It is also useful for multisigs, but my usage was for storing verified bids in a crowd-liquidation system. This allows the finalization step to operate without knowing the details of the operation.
It’s unfortunate that EOF uses bytecode for the formatting and not the high bits of the eth balance.

Author: chfast
Date: 2022-11-05T16:01:30.325Z

I put a clarification for EIP-3540 (EOFv1): EIP-3540: Clarify contract creation failure by chfast · Pull Request #5878 · ethereum/EIPs · GitHub
This is also a functional change because of geth implementation differs and therefore geth stance is reflected in the  state tests. So I assume many implementations followed this.

Author: qbzzt
Date: 2022-12-28T21:38:45.101Z

Why do we specify the number of code sections in two places:

In the type 1 EIP-4750 section header, where we specify <code sections>*4 for the length of the that section in bytes.
In the type 2 code header, where we specify the number of code sections whose length we’ll specify

Author: matt
Date: 2022-12-29T19:58:11.631Z

It gives us the possibility in the future to relax the strict ordering the header sections. If in the types 2 code header we used the value from the type 1 section, there would always be a requirement that the type header must precede the code header.

Author: axic
Date: 2023-01-05T14:09:49.863Z

Published a lengthier discussion starter about a large changeset called “EOFv2”:



EOFv2 aka what "EVM 2.0" could look like Core EIPs


    We have been thinking about the a number of proposals which have been made for EOF (most notable around account introspection) and summarised our thoughts in this document: 

This gives our view on the roadmap of EOF (v1, and v2 – the final boss ).

Author: purplehat
Date: 2023-01-06T01:23:57.123Z

Adding a comment here as an acknowledgement that we’re following along with this EIP at Art Blocks as it relates to how we now do on-chain storage of artists’ generative art scripts using our BytecodeStorage library.
We have this issue on our end tracking this and have filed issues on the two common “SSTORE2” library implementations that we are aware of flagging this as well:

Update BytecodeStorage Library to be EOF-compliant, while also considering Shard Blob Storage · Issue #422 · ArtBlocks/artblocks-contracts · GitHub
Update SSTORE2 Library to be EOF-compliant (see EIP 3540) · Issue #9 · 0xsequence/sstore2 · GitHub
Update SSTORE2 Library to be EOF-compliant (see EIP 3540) · Issue #354 · transmissions11/solmate · GitHub

No response to this comment is expected, just flagging here for visibility.

Author: hritzdorf
Date: 2023-01-10T17:16:57.800Z

Hi everyone,
I would like to submit an EOF-related proposal. (Previously posted on the R&D Discord, but now here upon recommendation.) I have read the EIPs and have hopefully not missed anything.
EOF1 contracts can only DELEGATECALL EOF1 contracts
Motivation:
Currently contracts can selfdestruct in three different ways (directly through SELFDESTRUCT, indirectly through CALLCODE and indirectly through DELEGATECALL). EIP 3670 disables the first two possibilities, however the third possibility remains. Allowing EOF1 contracts to only DELEGATECALL other EOF1 contracts allows the following strong statement: EOF1 contract can never be destructed.
Specification:
When an EOF1 contract performs a DELEGATECALL the target contract has to be EOF1. If it is not EOF1 (e.g. it is EOF0 or EOF2), the DELEGATECALL exceptionally halts. Hence, (among other things) all the gas passed along is consumed and 0 is pushed onto the stack. DELEGATECALL to an empty code also fails.
Security Implications:
Attacks based on SELFDESTRUCT simply disappear for EOF1 contracts. These include:

Lost library contract, e.g. Parity Multisig (https://www.parity.io/blog/a-postmortem-on-the-parity-multi-sig-library-self-destruct/) and more recently Aave v2 with multiple audits (Breaking Aave Upgradeability | Trail of Bits Blog)
Other unprotected selfdestructs (SWC-106)
Replacing code with new code using CREATE2 , SELFDESTRUCT and CREATE2  (legitimate use cases exist, but are rare)

Backwards Compatibility:
No backwards compatibility is broken as EOF is newly introduced. In theory EOF1 contracts could use EOF0 libraries using DELEGATECALL but that seems relatively far fetched.
Complexity:
The check is relatively simple. Hence, no changes to the gas cost of DELEGATECALL would be needed and the implementation overhead should not be prohibitive.
Please let me know if I should provide more clarifications, expand, write a PR or move this elsewhere.

Author: shemnon
Date: 2023-01-10T17:32:28.935Z

I think the biggest takeaway from this is if we don’t restrict DELEGATECALL then it becomes an escape hatch to do any features we banned in EOFv1.  SELFDESTRUCT and CALLCODE are the only ones it really impacts right now, but it establishes a pattern.
PC, JUMP, and JUMPI are not escapable because their scope is only on the EVM code.
Note that if the calling contract doesn’t matter then a regular CALL could be used to access the features at extra cost.  If we banned ECRECOVER from EOFv1 all we would be doing is just increasing the cost of that precompile to include a cold account load for the host contract.  Currently none of the precompiles depend on the caller so this is actually the current state for all precompiles.  We couldn’t ban a precompile in EOF and expect contracts not to find a way to use it.
So I’m personally in favor of this.

Author: axic
Date: 2023-02-09T00:37:29.537Z

During the Edelweiss interop there have been numerous discussions around EOF. We explored the idea how to properly reduce introspection, and discussed potential roadmaps. This document tries to give a glimpse into that process:



HackMD



EOFv1.1 aka EOF rollout Cancun+Prague - HackMD
# EOFv1.1 aka *EOF rollout Cancun+Prague* ###### tags: `EOF`  During the Edelweiss interop we discus

Author: jtriley-eth
Date: 2023-03-18T04:28:48.960Z

Would it make sense to have the code section sizes in the type section rather than in the header?
At the time of writing, the header is dynamically sized based on the number of code sections, this complicates header validation. In addition, this splits up function metadata a bit. We need to check the header to get the function’s size, then the type section to get the functions’ inputs, outputs, and max stack depth, then finally the code section to get the function’s instructions.
If the code size (u16) is stored in the type section, then we could have all of the function metadata in the same place.

Current container:
container  := header, body
header := magic, version, kind_type, type_size, kind_code, num_code_sections, code_size+, kind_data, data_size, terminator
body := type_section, code_section+, data_section
type_section := (inputs, outputs, max_stack_height)+

Proposed container:
container  := header, body
header := magic, version, kind_type, type_size, kind_code, num_code_sections, kind_data, data_size, terminator
body := type_section, code_section+, data_section
type_section := (inputs, outputs, max_stack_height, size)+


With the proposed schema, the header will always be 13 bytes, simplifying header parsing and allowing functions to be validated as the type section is parsed without having to refer back to the header.

Author: axic
Date: 2023-03-23T00:26:21.875Z

It seems like this wasn’t posted here, but since December we had a "Unified EOF Specification (Unified EOF specification - HackMD) describing the changes of EIP-3540/3670/4200/4750/5450/6206.
After the Edelweiss Interop discussions we have posted a rollout discussion document.
This week the above two have been merged into a single specification: the “Mega EOF Endgame Specification”.
It explains all the changes needed to achieve banning of code and gas introspection/observability.

Author: axic
Date: 2023-03-24T09:39:07.338Z

For anyone interested joining the discussions, there are bi-weekly calls called “EOF Implementers Call”, the next one is EOF Implementers Call #11 · Issue #748 · ethereum/pm · GitHub

Author: axic
Date: 2023-03-24T09:39:59.621Z

jtriley-eth:

Would it make sense to have the code section sizes in the type section rather than in the header?


I think this was discussed during the header format discussions in December, but I can’t remember the reasons, perhaps @gumb0 or @matt can?

Author: gumb0
Date: 2023-04-03T17:33:15.898Z

It would bring certain benefits for sure, the reasons we decided against something like this were, I think:

Desire to keep section headers definition general (and future-proof) enough, avoiding very special treatment of some sections. So currently we have generally just two kinds of sections: single-instance section, defined by one size in the header, and multiple-instance sections defined by array of sizes.
It seems like a useful property to be able to find the start and end of any section after parsing only the header, without the need to parse any of the section bodies. The information about the structure of the container is encapsulated in the header.

Note also that with the new creation instructions proposal we extend the format with another array of sections - container sections - and it is similarly defined as number + array of sizes in the header.

Author: jtriley-eth
Date: 2023-04-03T18:38:11.122Z

Is the type section strictly meant to contain subroutine metadata, or can it be extended to allow for arbitrary multi-instance sections? If the latter, then adding container sections can follow the same pattern.
While finding the section start/end from just the header seems useful, it seems negligible to parse the header and subsequent type section for this particular use case, and a dynamic header seems to be more of a challenge for single pass parsers than a benefit. It can be done either way, but the current way seems more complex for implementors.

Author: purplehat
Date: 2023-04-25T20:28:57.374Z

Howdy!
I was looking into "Mega EOF Endgame" Specification - HackMD + EIP-3540: EOF - EVM Object Format v1 and it was a bit unclear to me when the following items from the megathread are targetted for EIP-wise:

If the target account of EXTCODECOPY is an EOF contract, then it will copy 0 bytes.
If the target account of EXTCODEHASH is an EOF contract, then it will return 0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5 (the hash of EF00, as if that would be the code).
If the target account of EXTCODESIZE is an EOF contract, then it will return 2.

Is the EIP that this is targeted for still TBD?
IIUC this is not the behavior effective as of EIP-3540 itself based on this section from EIP:


EXTCODECOPY/EXTCODESIZE/EXTCODEHASH with the EOF target contract - works as with legacy target contract


EXTCODESIZE returns the size of entire target container

EXTCODEHASH returns the hash of entire target container

EXTCODECOPY can copy from target’s code section

EXTCODECOPY can copy from target’s data section

EXTCODECOPY can copy from target’s EOF header

EXTCODECOPY can copy entire target container
Results don’t differ when executed inside legacy or EOF contract



However, I’m not sure if there are other EIPs targeted for the same hard fork that would have this impact or if that is a later stage,
Not sure if this is better suited to ask here or in the Core Devs Discord, so opted to post here and cross-link in the Discord – my apologies if I missed it in my search here and there is a better place to post this.

Author: gumb0
Date: 2023-04-26T16:19:51.823Z

purplehat:

Is the EIP that this is targeted for still TBD?


Correct, these changes are not EIPified yet.
They are targeted for the same fork, so this part if EIP-3540 can be viewed as outdated.

Author: purplehat
Date: 2023-04-26T17:12:57.654Z

Fantastic – thank you for clarifying!

Author: purplehat
Date: 2023-04-26T17:28:08.385Z

Including here for broader visibility our plans for upgrading/migrating our contracts for on-chain art storage to support the EOF v1 hardfork plans.
future-proof-bytecode-storage-multiContract.drawio (1)1221×1071 164 KB
https://github.com/ArtBlocks/artblocks-contracts/issues/422#issuecomment-1523789750
Primarily sharing this here for visibility, perhaps for other on-chain art teams who may stumble upon this EIP discussion thread, but if any folks have feedback as to how we may be misunderstanding the EOF hardfork path here, please do reach out here in Discourse, via Twitter (I’m @purphat), or in the ETH R&D Discord (I’m purplehat.eth#7327)

Author: radek
Date: 2023-10-14T10:26:15.728Z

Hi all, is the EOF abbreviation really good fit?
EOF = end of file in IT for decades.
Apologies to jump into the discussion with such a pittance, but new devs might have a lot of issues when searching for tutorials, articles, SO, etc…
PS: not advocating “FOE”

Author: WhileyDave
Date: 2023-10-23T23:18:48.565Z

Hi all,
I was just looking at the dependencies for EIP3540, and it seems that EIP3540 requires EIP5450 which requires EIP4200 and EIP4750.  That means EOFv1 couldn’t be launched without also including: (1) static branching opcodes (RJUMP, etc); (2) subroutine calls (CALLF, RETF).  The reason for this is that the stack validation algorithm relies on static control flow.  This is necessary because of the strict requirement that unreachable code is not permitted.
It seems to me that you could cut things differently to reduce the dependencies whilst still achieving the overall goal.  Here’s an alternative:

Launch EIP3540 (EOFv1) and EIP5450 (Stack Code Validation) together on their own (i.e. without EIP4200 & EIP4750).  In a subsequent fork, launch EIP4200 and EIP4750 (and perhaps e.g. EIP663) as non-breaking changes to EOFv1.

By “non-breaking changes” I mean that no EOF version change is required.  We are just updating EOFv1 after the fact to make the release more incremental.
The current code validation rules (EIP5450) are a problem though.  We need a code validation algorithm that does not rely on static branching.  Instead, it needs to ensure arbitrary (junk) bytes are not permitted within a code section (otherwise e.g EIP4200 is a breaking change).  That’s easy enough, and doesn’t need reachability (and is e.g. what I believe the JVM does). Something like this:

Every byte within a code section is either a valid opcode for an instruction or part of the immediate operands of an instruction.

This prevents arbitrary bytes from existing within a code section.  Yes, it means instructions may be unreachable. But it also means that EIP4200 or EIP4750 can be deployed after the fact as non-breaking changes.  Furthermore, code validation requires a single linear scan (i.e. does not require a worklist algorithm as per EIP5450).

Author: shemnon
Date: 2023-10-24T23:44:48.375Z

What compilers would target code-validated only EOF?  how would that reduce total bytes?
I think the minimally viable variant is

EOF Container
Code Validation
Stack Validation
Static Jumps
Code Sections (with CALLFI/JUMPFI)
EIP-663 - EXCHANGE, EXCHANGE2, and DUPN
Data Opcodes
Easy opcode bans: CALLCODE, SELFDESTRUCT, PC
For EOF1 we would still permit CREATE[2] from memory.  And a restriction that only EOF containers can CREATE[2] EOF contracts that I think was already in most big-EOF code.

Then we could do a follow on with EOF2 in a future hard fork, possibly wire it in early so L2s and such can use it:

Ban Code Introspections

Ban EXTCODE opcodes
Ban EXTCODE into EOF2 contracts
Add FACTORYCREATE and EXTCREATE opcodes (neè CREATE3/4)


Ban Gas Observability

Ban old CALL series
Add LCALL, LDELEGATECALL, LSTATICCALL



This would be all the “breaking” changes (container and validation) and the MVP for compiler use in EOF1.  Every compiler ask would ideally be in EOFv1 at launch.
EOF2 would just be (mostly) a different selection of opcodes with the same validation core logic.  The subcontainer section would be added, but we could also activate FACTORYCREATE in EOF1 as well.
So we can do it in two steps, with only one set of validator logic between the two.  Simply a data-driven switch to change the opcode table for validation.

Author: WhileyDave
Date: 2023-10-25T00:57:15.768Z

What compilers would target code-validated only EOF?

Surely, it doesn’t matter whether or not they target it by default at the beginning?  Presumably they would have an option for early adopters.  The point of code-validated only EOF is to get EOF through the process and deployed.
Once this is done we can start rolling out non-breaking changes, such as static jumps and DUPN, SWAPN.  With these instructions deployed, compilers would start to target EOF by default (as it would be in their interest).  Even CALLF and RETF could be rolled out as a non-breaking change (provided that JUMP/JUMPDEST was retained).  Then, later, JUMP / JUMPDEST are retired in a breaking change taking us to EOFv2 (possibly with other goodies such as removing code/gas introspection piggybacked on board).
All I am saying is that your MVP is not actually the MVP.  There is another (smaller) option.  The nice thing about this option is that it allows us to roll out non-breaking changes and, in doing so, incentivise compiler writers to adopt.  It means they can take their time and there is less pressure.  Anyway, its just a thought.

Author: WhileyDave
Date: 2023-10-25T01:00:09.519Z

shemnon:

EIP-663 - EXCHANGE, EXCHANGE2, and DUPN


Personally, I wouldn’t include these in the EOFv1 proposal … because you don’t need to.  Once EOFv1 is live, these can deployed as non-breaking changes to EOFv1.  Given that discussions are still ongoing about what the right combination of instructions is (SWAP_N, SWAP_N_M, etc) … it seems like a decision that can be deferred at no cost.

Author: shemnon
Date: 2023-10-25T21:12:45.521Z

IIRC vyper considers EXCHANGE to be critical for avoiding code size regression.  If it’s just something that adds more code size reduction instead of preventing regression then it can be moved out of the MVP.

Author: shemnon
Date: 2023-10-25T21:14:45.529Z

WhileyDave:

All I am saying is that your MVP is not actually the MVP. There is another (smaller) option.


If the compilers won’t target it, it’s not viable. I don’t want to ship the sequal to blake2b because we cannot assume future forks changing EOF will indeed happen, each step needs to be able to stand on its own. We can iterate with solidity and vyper to figure out where that line is.

Author: WhileyDave
Date: 2023-10-25T22:57:23.528Z

shemnon:

we cannot assume future forks changing EOF will indeed happen, each step needs to be able to stand on its own


Well, fair enough I understand that.  But, once the container format + code validation is in place, the risk/reward trade off for EIP’s like EIP4200, EIP663 is much more favourable.  They become very minor updates.

Author: shemnon
Date: 2023-10-26T23:44:32.908Z

EIP-4200, static jumps, is essential to the first draft.  Without it we would need to keep the old JumpDest evaluation rules in. It also facilitates getting rid of mandatory jumpdests, which can be up to 20% of code size. Not having it in V1 means the code validator would need to be different in future versions.
It’s because of subtle interdependencies like this that the list looks trimmable but really isn’t for an MVP that compilers would actually target.

Author: WhileyDave
Date: 2023-10-29T19:46:42.330Z

shemnon:

Not having it in V1 means the code validator would need to be different in future versions.


Yes, you use a lightweight code validator and retain JUMP / JUMPDEST in EOFv1.  Then, phase in static jumps / subroutines / xchg as non-breaking changes to EOFv1.  Finally, switch to EOFv2 as you have it (i.e. removing JUMP / JUMPDEST, etc).  As part of moving to EOFv2 you can upgrade the code validator.
Look, I’m not trying to suggest this is an ideal approach.  Its not!  Going straight to EOFv2 would be much nicer.  But, you are considering two options (mega EOF vs staged EOFv1 => EOFv2), and I’m simply saying: there is a third option which gets you to the same place (EOFv2).  This initially requires only two EIPs and, therefore, could potentially be launched alongside something else.  If you don’t need to use this option, great — I’ll be very happy with that.  But, having all the options on the table might be helpful.

Author: chfast
Date: 2023-11-09T11:18:59.728Z

This is the original approach with first deploying EIP-3540 and EIP-3670. However, then the feedback was that it is undesirable to have EIPv1 and EIPv2 live in the future.

Author: WhileyDave
Date: 2023-11-10T11:35:50.516Z

chfast:

This is the original approach with first deploying EIP-3540 and EIP-3670


Well, I thought the EOFv1 specification included EIP-4200, EIP-4750, EIP-5450, and EIP-6206.  It was a lot more than just two EIPs.  I’m suggesting something even more lightweight which is literally just EIP-3540 and EIP-3670.

Author: shemnon
Date: 2023-11-10T15:07:44.825Z

We need static jumps in there as well (EIP-4200), otherwise we are committing to an eternity of technical debt where there are variants of EOF that accept dynamic jumps.
There is no sensible world where we support dynamic jumps in EOF.
The problems with splitting off those others (especially stack validation - 5450) is we then have multiple validation scenarios for subsequent versions of EOF, all of which have to be maintained for eternity.  Just like stripping off static jumps.
Requiring clients to maintain multiple EOF validation functions multiples the surface through which consensus errors can enter.  the impact is exponential, not additive.

Author: WhileyDave
Date: 2023-11-11T09:56:25.390Z

We need static jumps in there as well (EIP-4200), otherwise we are committing to an eternity of technical debt where there are variants of EOF that accept dynamic jumps.

Well, if you go with EOFv1 first and then EOFv2 in a second fork you are committing to a similar level of technical debt (i.e. supporting two major EOF versions).

There is no sensible world where we support dynamic jumps in EOF.

Well, if that was the only way to get EOF across the line … would you do it?  The end game (EOFv2) is the same in all cases (i.e. doesn’t have dynamic jumps).

The problems with splitting off those others (especially stack validation - 5450) is we then have multiple validation scenarios for subsequent versions of EOF, all of which have to be maintained for eternity. Just like stripping off static jumps.

There are options if maintaining multiple code validators is really a sticking point.  For example, you could drop EIP5450 altogether or simplify it (e.g. by relaxing the reachability requirement).  This EIP seems to be a nice to have, not a must have.  Again, I’m not suggesting this is ideal (it isn’t) just that it seems possible. There are trade offs and compromising is never pretty.  I appreciate you don’t believe the trade off makes sense in this case — fair enough.
In summary, let’s clarify the different strategies:


(One Stop) This is mega EOF.  Do it all in one fork — great!


(Two Stop) This is EOFv1 in one fork, followed by EOFv2 in a subsequent fork.


(Three Stop) Initially two EIPS in one fork.  Then, non-breaking changes in a subsequent fork taking us to a weakened version of EOFv1.  Finally, in a third fork, you get to EOFv2.


The three stop strategy looks unattractive because it takes more stops (and for reasons you’re highlighting above).  But, if you couldn’t get the two stop strategy started in Prague, but could get the three stop strategy started … then it works out the same.

Author: gcolvin
Date: 2023-11-29T21:36:04.825Z

WhileyDave:


There is no sensible world where we support dynamic jumps in EOF.

Well, if that was the only way to get EOF across the line … would you do it? The end game (EOFv2) is the same in all cases (i.e. doesn’t have dynamic jumps).


Dynamic jumps must die.  Nothing is more important.

Author: wjmelements
Date: 2023-12-20T22:24:53.885Z

EOF should not be released partially just to release it earlier. There is not an urgent need for EOF yet more versions mean more complexity. Building out a partial version with a usefulness lifespan of only a year is bad for sustainability.

Author: wjmelements
Date: 2023-12-21T20:27:51.768Z

How would you do a jump table or a switch statement then? With dynamic jumps it is constant complexity; without it is logarithmic.

Author: chfast
Date: 2023-12-21T20:42:10.474Z

With the RJUMPV instruction.

Author: chfast
Date: 2023-12-21T20:42:51.907Z

The current spec is now in https://github.com/ipsilon/eof/blob/main/spec/eof.md.
Patches welcome.

Author: gcolvin
Date: 2024-02-16T06:39:04.877Z

There has been an urgent need to be rid of dynamic jumps ever since the EVM was invented, and EOF Functions are the third or fourth proposal to be rid of them.  Every time a proposal fails a solution is delayed 2 or 3 years, people working on the problem move on, and new core devs have to be educated and convinced.  It never ends.
The problem is simple.  So long as the EVM has dynamic jumps you cannot traverse the control flow graph in linear time – it can take quadratic time.  This should be obvious.  When you hit a dynamic jump you cannot, in general, know the possible destinations without executing the code.  The complexity becomes the product of the number of JUMPs by the number of JUMPDESTs.  But anything that takes more than linear time is a DoS vulnerability unless it done offline.
That means, among other things, that you cannot validate safety properties like the lack of stack of underflow.  Worse, from my point of view, you cannot translate EVM stack code to register code, either for faster interpretation or for compilation to machine code.  And even working offline they cause problems: static analyses like symbolic execution become vulnerable to “quadratic path explosion,”  and some approaches to zkEVMs are forced to resort to parallel execution.
Dynamic jumps must die.  Now.

Author: cyrus
Date: 2024-03-15T16:07:48.444Z

Will the final EOF proposal get an entirely new EIP number? I’ve searched around and can’t find one.

Author: gumb0
Date: 2024-03-15T17:21:13.978Z

No, current plan is to keep it as a collection of EIPs. But there is also unified spec at https://github.com/ipsilon/eof/blob/main/spec/eof.md

Author: jtriley-eth
Date: 2024-05-02T16:22:35.869Z

Just want to confirm because I’ve missed some discussions on this, but it looks like the changes previously meant to be in EOF2 are all merged into EOF1, is this correct?

Author: wjmelements
Date: 2024-05-02T21:26:15.636Z

I like the gas parameters for security because I can use them to prevent re-entrancy for eth payments. Without them I would need the PAY opcode for the same level of security.

Author: MicahZoltu
Date: 2024-05-03T05:51:06.241Z

This pattern breaks some contract wallet recipients and shows why (IMO) we need to remove GAS parameters.  A wallet should be able to do internal accounting when it receives ETH.
If we believe that contracts should not be able to do internal accounting on receipt of ETH then we should make that explicit at the EVM layer by providing a mechanism to send ETH without the recipient getting an opportunity to execute code.  This used to be possible in a very hacky way with self destructing, but if we feel that pattern is good we should just have an ETH_SEND opcode that doesn’t transfer execution over to the destination.

Author: wjmelements
Date: 2024-05-03T19:27:06.082Z

I can imagine another solution. There could be a new type of call (PAYCALL) that allows sub-frames to delegatecall and staticcall but not call and paycall. It would be more powerful than PAY because the recipient can update their own storage, but they can’t cause mischief in other accounts. It would differ from staticcall in that it would allow storage and log for the recipient account.
But I don’t think recipient reaction and right of refusal are important (see ERC20) and I prefer PAY for simplicity. The only accounts with good eth recipient action without calldata are WETH and UniswapV1, and I wouldn’t use PAY in those circumstances. The others I’ve seen are just wasting my gas or trying to steal my money.

Author: charles-cooper
Date: 2024-05-04T13:15:31.655Z

Yes! Let’s do PAY! (and/or EIP-7609)

Author: charles-cooper
Date: 2024-05-14T02:50:09.067Z

(removed, meant to reply to the whole thread as opposed to any specific comment)

Author: charles-cooper
Date: 2024-05-14T02:52:12.530Z

I have been providing feedback to the ipsilon team over the last year and have helped to draft / improve some of the EOF proposals. I am in general in favor of any changes to the EVM which improve execution efficiency, provide features for compilers and make it more future proof. That being said, I think there are two areas in the design (current: eof/spec/eof.md at 890ef566b86e8f25215f5cad65e171e2c1de71d4 · ipsilon/eof · GitHub) which I think could benefit from more work before mainlining it.

VLQ. Using variable length quantities instead of fixed-size quantities, both in headers and as instruction immediates. This is a nice-to-have, but I think it will save significant pain if any limits (e.g. codesize limits) need to be upgraded in the future, since they can be implemented in a “soft” limits change, rather than needing a format change. It also can provide some codesize savings (I estimate 2-5% improvement), as well as potentially save opcodes (e.g., SWAP_N vs EXCHANGE) since we don’t need to optimize opcodes for different immediates sizes anymore. I wrote about this more here: update EOF container specs with variable-length encodings by charles-cooper · Pull Request #37 · ipsilon/eof · GitHub. Again, I think it could be ok if EOF shipped without VLQs since it can theoretically be changed later, but the cost of changing this later after mainlining would be high since it would require a format change. Therefore I think the benefit to changing it now (which, per feedback on the above PR, the only downside of which is needing to redo some testing work) outweighs the cost.
Global code section. Right now EOF provides subroutine functionality mainly through code sections. However, to my knowledge an EIP-2315 style design which has a single global code section (effectively allowing cross jumping between routines, which EOF does not allow) was not seriously considered. The benefit of a global code section is mainly about efficiency of compiler-generated code; there are, by definition, some code-sharing optimizations available to a global code section which are not available to the nonglobal code section design. This is a deeper and more permanent(!) design issue that I’m not sure it would be possible to easily fix in a future hard fork. It may be that the benefit of disallowing cross-jumping outweighs the cost to compilers / code generators, at this stage I’m not sure, in any case I don’t think the analysis has been done yet. But in my mind this is a serious issue that should at least be investigated and addressed in the rationale section of the EOF proposals.

Author: gcolvin
Date: 2024-05-17T03:23:13.630Z

I agree technically on VLQs – you get better code size now and you future-proof a system that in principle will run forever.  I can’t speak to schedule issues, but I imagine they are tight.  If it helps – and I’ve forgotten the details of the VLQ scheme you are using and of the EOF immediate size issues – but some schemes would let you specify a fixed-size field with unused bits now which could become variable length later when those bits start getting used.
If someone wants to pick up the 2315 ideas in the future I don’t think that EOF forecloses them.  Somewhere there is a old PR of mine proposing that EOF code sections not be functions, but modules with multiple EOF-defined entry points and no cross-module jumps, but with EOF-2315 supported inside of modules.  We never discussed it much, but something along those lines should still be doable.
EIP-2315 wasn’t seriously considered in part because it I never seriously proposed it again.  It’s last-minute rejection simply set my research back too far, and I became content to join a startup doing other VM work and in my spare time get the proposal to where I wanted it and make sure that EOF functions provide at least the same level of init-time validation, which earlier versions did not.

Author: frangio
Date: 2024-06-20T23:50:26.965Z

There are popular ERCs such as ERC-721 (NFTs) that rely on checking extcodesize(addr) > 0. It would not be possible to implement them as EOF contracts.
Has this been discussed before?

Author: shemnon
Date: 2024-06-21T15:54:25.114Z

If the intent is to differentiate EOAs from Contracts this runs headlong into the AA and Smart Contract wallets also being smart contracts.  While it used to be a valuable check current user practice has significantly curtailed it’s value, as an NFT collection that only allows EOAs to own the NFT will be chasing a shrinking market share.
So it has been discussed, and the conclusion was that the AA roadmap removes the value of that check.

Author: frangio
Date: 2024-06-21T20:09:31.395Z

shemnon:

an NFT collection that only allows EOAs to own the NFT


This is not what the check is used for in these ERCs (see ERC-721, ERC-1155).
The logic they specify is roughly this: when an asset is transferred to a recipient that has code, invoke recipient.onReceived(...), which must succeed and return a specific magic value, otherwise the transfer is reverted.
The purpose is not to prevent smart contracts from owning the asset, but to prevent errors where the asset is transferred to the wrong contract with no way to recover it. This is an error that happens a lot, so many developers consider this an important feature. Smart contract wallets are supposed to implement onReceived (and they do, see for example Safe).
It’s also used to allow a recipient contract to react to a transfer and do something with the asset as soon as it’s received.
We can debate whether these are good design patterns or not, but I think they are at least legitimate and don’t conflict with the AA roadmap (unlike patterns that force the use of EOAs). The fact is that what is arguably the second most important ERC used on the network relies on this ability and the specification can’t be changed.
So are we okay with ERC-721 being unimplementable on EOF(v1)? My assumption is that we want EOF to become the standard way to deploy new contracts, if so this should be addressed.
It would be enough to add an opcode to check if an account has code set (or to change the semantics of EXTCODESIZE on EOF to return 0/1, but I assume that’s not a good idea). Granular code introspection is not necessary.

Author: shemnon
Date: 2024-06-21T20:43:10.087Z

I think this may be the use case we were struggling to find when we were discussing it.
One option is to change the error numbers to distinguish a return from a contract from a return to a non-contract address (empty or EOA).
i.e. instead of
0 - success
1 - revert
2 - failure

we could do
0 - successful call
1 - successful non-call
254 - failure (-2 uint 8)
255 - revert (-1 uint 8)

So we can determine from the call what happened.  The downside is it would eliminate the (ISZERO  RJUMPI[n]) check and require a couple of exra opcodes per call (PUSH1[127] LT RJUMPI[n]) but allow an executed onReceived to be distinguished from a transfer or no-op call.  Although in cases where you only want to accept successful and want to hot-path that handling ISZERO still works
Other alternatives that were rejected

Use only PAY to do transfers, this prevents some calls from requiring payment, such as WETH
Require Value when calling a non-contract and if not failure, it felt too special cased.

I’ll bring this up in the EOF call this wendsday, as I think it is the example we were missing.  Solidity had brought it up but the collective seeme persuaded by the AA argument.

Author: frangio
Date: 2024-06-21T21:10:16.547Z

shemnon:

One option is to change the error numbers to distinguish a return from a contract from a return to a non-contract address (empty or EOA).


Yes this would work!



 shemnon:

The downside is it would eliminate the (ISZERO RJUMPI[n]) check and require a couple of exra opcodes per call


Yeah this is unfortunate, I think it makes sense to prioritize keeping that code sequence short. Another alternative could be to switch back to returning 0/1 (0: success), and instead of returning the status code immediately from the *CALL instructions add a separate opcode CALLSTATUS to put it on the stack if necessary. I assume it will very rarely be used anyway?
As a side note, in EIP-7069 it’s really not clear to me when the failure status 2 will be returned on the stack. I can’t find a definition of “failure”. This part in particular confuses me: “9. Fail with status code 1 returned on stack if any of the following is true […]”, is that correct or is it an error and it should return status 2?

Regarding another point:

EOF1 contracts can only DELEGATECALL EOF1 contracts

Is this restriction still necessary given that SELFDESTRUCT has been mostly neutralized in EIP-6780: SELFDESTRUCT only in same transaction?
There is a potentially severe failure mode for EOF upgradeable proxies: if the proxy target is upgraded from an EOF to a legacy contract, and the upgrade logic is located in the target, the proxy becomes frozen and unusable. This error could be prevented by having the upgrade code always test that DELEGATECALL works, but this seems like a footgun that should be avoided from the outset.
The rationale proposes this restriction as a way to prevent attacks/errors like the Parity Multisig, but actually introduces a new way in which a proxy contract could end up irrecoverably broken!

Author: Michaels
Date: 2024-06-22T07:59:23.071Z

From this reference, extcodesize of EOF code called from legacy code should return 2. I think that suffices, no?

Author: Michaels
Date: 2024-06-22T08:17:10.710Z

Given EXTDELEGATECALL’s restrictions. I’ve also thought of is how EOF contracts can recognize other EOF contracts to know if EXTDELEGATECALL is okay to make without wasting initial gas
So currently, compilers add a check of extcodesize before making calls since calls to EOAs return 1. From this reference EXTDELEGATECALL to legacy code also behaves as though its an EOA. This means that EOF code would need to be able to detect other EOF code. Doing extcodesize won’t work as legacy code has size too.
The only solution i could think of was EXTCODECOPY the first 2 bytes and asserting that it is 0xEF00. That’s means a cost of a few more opcodes during EXTDELEGATECALL for EOF

Author: frangio
Date: 2024-06-22T18:16:57.693Z

The two issues I’ve described are about EOF contracts, where EXTCODESIZE is not available, so neither of those are relevant.

Author: Michaels
Date: 2024-06-22T23:18:53.031Z

Yeah you’re right, missed that part (extcodesize etc deprecated)

Author: radek
Date: 2024-06-24T09:23:50.416Z

frangio:


EOF1 contracts can only DELEGATECALL EOF1 contracts

Is this restriction still necessary given that SELFDESTRUCT has been mostly neutralized in EIP-6780: SELFDESTRUCT only in same transaction?


Also highly interested to know this. Since this has a huge impact on interoperability and de facto splits EVM “lego” to 2 worlds.

Author: cameel
Date: 2024-06-24T17:03:19.164Z

frangio:

There are popular ERCs such as ERC-721 (NFTs) that rely on checking extcodesize(addr) > 0. It would not be possible to implement them as EOF contracts.


I don’t think the lack of extcodesize check will affect these EIPs. Technically, yes, they both state that a correct ‘safe transfer’ function implementation must check whether the target is a contract and not call it if it is not. This check will indeed be impossible on EOF. But practically speaking, this check is redundant. Proceeding with a call in case of EOA will simply result in a success with no return data. As long as the transfer function does check the return value, this will still result in a revert. And specifically in Solidity, unless intentionally implemented as a low-level call, the compiler itself will issue a revert when the size of returned data does not match what the called function declares.
As much as I think that having a way to perform this check would still be useful as an extra sanity check, it does not seem like not having it would prevent EIPs like this from being implemented. At most, the wording of those EIPs may need to be revised to stop requiring this check on EOF (or in general).

Author: frangio
Date: 2024-06-24T17:20:27.569Z

cameel:

As long as the transfer function does check the return value, this will still result in a revert.


But in the case of an EOA recipient it should not!
The extcodesize check is not redundant, it’s necessary to implement the semantics.

Author: cameel
Date: 2024-06-24T20:07:31.829Z

Ah, you’re totally right. I misread the purpose of the check in these EIPs. It’s there to skip the onReceived() calls without reverting. Without it the EIPs will still work for contracts but not for EOAs.
So yeah, this does seem like a new use case for the check. I still think it does not make the EIPs unimplementable though, it just neuters some of their safeguards, which previously wasn’t considered a strong enough reason for an additional call return code.
The way you’d implement it without the check would be for ‘safe transfer’ to always issue a call and check whether it’s an EOA based on the status and return data. If the call is successful and the return data is empty, it’s probably an EOA. Of course this will have some false positives, e.g. if it just happens to be a contract has an onERC1155Received() function that returns nothing. Or if the contract intercepts the call in its fallback function.
I don’t think this is qualitatively different from the case of the extcodesize check on high-level calls in Solidity that we brought up earlier. Still, if this additional case is enough to sway the decision towards making this case properly distinguishable, I’m all for it.

Author: shemnon
Date: 2024-06-26T19:08:36.073Z

EOF Call 52 notes

Clients

Besu and EVMONE are adapting to recent spec changes
Nethermind and Geth are progressing with their implementations, currently working on state test issues
EELS has started an implementation
No updates from Reth or EthJS


Compilers

No updates from Solidity or Vyper


Spec Updates

Discussed the need to differentiate EOAs from contract accounts, for ERC-721’s safe transafer from method

Two major options: return codes (as outlined above) and reworking EXTCODESIZE

Return codes could  be tweaked to set high bits for flags.  bit 7 - failure, bit 6 - was an EOA bits 0-3 status.
However, this could impose overhead for contracts that don’t care about EOA status
Ipsilon will ping @rodiazet to see if we can quantify the overhead we would see out of solidity
EXTCODESIZE could be recast like DIFFICULTY/RANDOAO was recast.
A new IS_CONTRACT opcode would overlay it, returns 0 if it has no code and 1 if it does, regardless of size.  Compiler logic that only tests codesize > 0 would still work.
The downside is this is an infringement of banning code introspection. While not a bad one as it is a simple question of “do you execute code”




Discussed changing max_stack_height in the types section in light of the requirements for input_size already existing, so that a double counting can be removed from CALLF checks.
There were concerns about a non-essential change going in this late, however @chfast was not on the  call it was decided to push it out two weeks when he can be present to advocate for his change
Banning EOFCREATE and RETURNCONTRACT referencing the same container was finalized


Testing updates

We discussed the issues around system contract actions such as beacon block, block hash, and withdrawals/deposits/consolidations.
A new field will be added to the “env” section of t8n requests indicating if those block level action should or should not be performed.  This will allow state tests to evaluate just the TX and not the whole block.
We also discussed differentiating initcode and runtime code in EOF container validation tests. a new field will be added allowing test consumers to differentiate.  PRs will be forthcoming.


Devnet

Because we do not have multiple clients ready we will not be launching a devnet in June. Target is now July.
Kurtosis is ready to support EOF networks.  Assertor tests need to be written to deomnstrate basic readiness for EOF create transactions.



Next call is in 2 weeks, on 10 July.  Danno and Piotr will be absent.

Author: chfast
Date: 2024-07-01T10:56:43.115Z

The max_stack_height change is rather cleanup but it also leads to bigger change removing the need for runtime access to the type section (please discuss the details there). In my opinion this is worth the effort especially for the code chunking.

Author: chfast
Date: 2024-07-01T11:15:43.684Z

This is late, but I’d like to answer the VLQ question in the context of instructions. For EOF header this is probably fine, but I’m not sure how much it matters there.
For instructions VLQ doesn’t look good. EOF makes bytecode translation much easier (JIT/AOT compilers) but also improves situation for interpreters by a lot by removing jumpdest analysis. I.e. interpreters can start executing just after reading the EOF header.
Using VLQ to encode instruction immediates makes the interpretation more complex because for some instruction VLQ decoding is involved and it is rather impractical to cache the results or preprocess the code. For some instructions like RJUMPV VLQ cannot be used normally because random access to the table is needed so we need a workaround in this case.

Author: charles-cooper
Date: 2024-07-02T17:01:25.290Z

sorry, missed the last meeting as I was taking a vacation!



 shemnon:

bit 6 - was an EOA


I think this is the same as IS_CONTRACT opcode, since you can always (static)call an address and check the status code to deteremine if it is an EOA. But I guess you can always bust out and call a specially designed legacy contract to query the code of a given address right?



 shemnon:

The downside is this is an infringement of banning code introspection. While not a bad one as it is a simple question of “do you execute code”


I think this should be run by the AA folks tbh and see what they think, as I have seen a lot of calls for “shouldn’t be able to tell difference between EOA and contract”, not sure if the ERC721 safeTransfer case has been considered.

Author: charles-cooper
Date: 2024-07-02T17:11:46.060Z

Hmm I think for almost all instructions with immediates there is an improvement, since there is no limit on the addressability size. For example, with VLQ we could add fewer stack instructions; a single opcode with variable length could be used for SWAPN/EXCHANGE. And from a performance point of view VLQ is an improvement since in common code patterns it improves codesize.
RJUMPV is indeed a special case which would need its own encoding to preserve the property of random-access to the table. (But I don’t think it’s a particularly hard problem to solve, it just needs a bit of spec work).
By the way, these are already all discussion points that were addressed in the VLQ proposed addition to EOF update EOF container specs with variable-length encodings by charles-cooper · Pull Request #37 · ipsilon/eof · GitHub.

Author: SamWilsn
Date: 2024-07-12T13:48:55.233Z

I’m extremely late to the party, but am I reading EIP-3540 correctly? It seems that both the entire EOF blob and one of the section kinds are called “containers”. That seems like confusing terminology.

Author: thedavidmeister
Date: 2024-08-03T07:57:36.398Z

hey, so i implemented an onchain interpreter
i wanted to confirm that
https://github.com/rainlanguage/rain.interpreter/blob/main/src/lib/eval/LibEvalNP.sol#L146

would be implementable with RJUMPV and retain constant time function calls here
it looks like RJUMPV might be able to replace my hand rolled assembly, which would be nice, but i also might be missing something here and if i can’t do what i need in EOF that would be a problem for me

Author: shemnon
Date: 2024-08-14T13:32:11.139Z

You would need to change your function encoding to an int enum from 0 to 255.  And you would be limited to 256 functions.  Or you would need to stack dispatchers, one for the first byte, jumping to one for the second, etc.

Author: wjmelements
Date: 2024-09-11T22:41:02.191Z

EIP 3540 does not have rationale for removing EXTCODE access. It should be mentioned and alternatives should be discussed.

Author: pdobacz
Date: 2024-09-12T07:50:08.102Z

Agreed on the missing rationale, will track this. The currently proposed substitute is EIP-7761 HASCODE instruction, alternatives to it are also discussed there.

Author: wjmelements
Date: 2024-10-02T21:39:04.472Z

How will eth_getCode behave for EOF? Will it return EF00 like EXTCODECOPY, the full object, or the code segment?

Author: shemnon
Date: 2024-10-03T20:20:08.888Z

By the name you are referring to the JSON-RPC API eth_getCode, correct?
The EIPs only handle in EVM execution, so the JSON-RPC specs are out of scope and no change in the APIs is proposed.
That being said, I expect it should return the entire EOF code object as it exists in the account trie. The concerns about code introspection are only for introspection within the EVM, not from the outside. For cross-layer concerns like the JSON-RPC, I don’t see how we could possibly enforce non-introspection, as you could just re-play all the transactions (which executing contracts don’t have access to).

Author: u59149403
Date: 2024-11-18T06:22:21.807Z

Please, include Solidity ABI to EOF. Currently we need to download ABI from Etherscan if we need it, i. e. we have to use centralized service. This is suboptimal. And not all contracts have ABI available at Etherscan. Also, there are a lot of automatically generated contracts.
So, please include ABI to EOF. This will enhance transparency and debuggability.
You may say that I should not mix EVM and Solidity. Yes, I agree. So, let’s do this: EOF includes additional section for “language-specific type info” (which is agnostic to language) and Solidity compiler always generates its ABI and it puts to this section.
Also: EOF is great moment for implementing native tokens (EIP-7809). See my arguments why native tokens are cool: https://ethereum-magicians.org/t/eip-7809-native-tokens/21615/9

Author: shemnon
Date: 2024-11-19T15:59:11.161Z

I do not realistically see an explicit ABI section being adopted on mainnet.  Possibly on some rollups on an optional basis, but I expect there would be other devs who would want the code representations as lean as possible.

Author: wjmelements
Date: 2024-11-20T06:17:48.362Z

You can ask the solidity devs to put their ABI in the data section but their 4byte ABI is non-standard  and neither can nor should be mandatory. Opting into this pattern can make your own code more accessible but there will always be non-compliance.
I recommend you take this suggestion to the solidity team. I’m sure they will be happy to make this the default.

Author: cameel
Date: 2024-11-20T11:46:26.751Z

This is already solved in Solidity by contract metadata. Metadata contains ABI definitions among other things. It can’t realistically be embedded directly in the contract due to how bulky the uncompressed JSON data is (too easy to hit the EIP-170 code size limit), but the hash is there and the full metadata is meant to be made widely available via distributed filesystems like IPFS or Swarm.
You also don’t need to use centralized services like Etherscan. You can use Sourcify, which makes all of its data publicly available, also on IPFS.

Author: kuzdogan
Date: 2024-11-20T12:20:59.257Z

Adding to @cameel’s comments, you can visualize the metadata encoding and the contents at playground.sourcify.dev

Author: radek
Date: 2025-03-06T08:16:17.303Z

frangio:

EOF1 contracts can only DELEGATECALL EOF1 contracts


I also understood calls are also prohibited.
Is the separation of EVM legacy and EOF still valid?
If so, how is it expected to be interoperable with the key stablecoins?

Author: pdobacz
Date: 2025-03-06T08:26:58.713Z

Only EXTDELEGATECALL from EOF1 to legacy contract is prohibited, per today’s spec. EXTCALL and EXTSTATICCALL are unrestricted. Also legacy can *CALL EOF1 contracts without restrictions.

Author: radek
Date: 2025-03-06T21:05:17.931Z

Thank you for the clarification. It would be better if such statement was made in full in the EIP.
I.e. we will be able to upgrade the implementation contracts behind UUPS and other proxies (and Diamond libraries), is that correct?
To understand - such stablecoins need to keep the same entrypoint address (mostly proxy now) and basically the storage layout. Implementation contracts can be replaced.

Author: shemnon
Date: 2025-03-12T21:01:10.494Z

If stablecoins depend on delegatecalling arbitrary adresses not under control of the stablecoin contract then they will not be able to upgrade.  Note that this is widely considered to be a security risk.
If however it is delegate calling to contracts controlled by the stablecoin, those addresses need to be EOF contracts.  I expect that any such upgrade would deploy the new delegate contracts and then upgrade the core contract, where the core contract is the single switching point for new logic.

Author: chfast
Date: 2025-03-12T21:12:53.107Z

Solidity is running a service for source code verification and matadata: https://sourcify.dev
It publishes the ABI/metadata of a contract e.g. on IPFS.
In EOF an anchor to such data may be potentially stored in the metadata section: EIP-7834: Separate Metadata Section for EOF.

Author: pcaversaccio
Date: 2025-03-13T14:19:33.599Z

A blog post on why we should reconsider EOF: Ethereum is turning into a labyrinth of unnecessary complexity with EOF - Let’s reconsider EOF.

Author: pcaversaccio
Date: 2025-03-21T15:57:29.120Z

I have co-authored a new and longer EOF deep-dive: EOF: When Complexity Outweighs Necessity. We break down its supposed benefits and argue they’re more “nice-to-haves” than essential upgrades. Instead of adding complexity, we highlight cleaner, less disruptive solutions that achieve the same goals. EOF’s objectives are solid—but there’s a smarter way to get there.
Please give it a read and let us know your thoughts in this thread.

Author: cameel
Date: 2025-03-27T12:36:20.251Z

Here’s Solidity’s perspective: The Case for EOF | Solidity Programming Language
In short, we believe the current level of complexity to be justified and mostly overstated. EOF tackles the underlying problems instead of sweeping them under the rug. Some of the proposed alternatives would address some individual pain points for us, but that’s missing the bigger point and only making things worse long-term. We see it not just as a funky prerequisite that’s blocking EIP-663, but as a bundle of tightly related and long overdue fundamental fixes.
Yeah, it won’t make legacy EVM go away, but for most of the stack EOF will quickly become the only part that really matters.

Author: pdobacz
Date: 2025-04-08T13:02:12.656Z

Note: The spec of EIP-3540 is getting a minor update, planned for the eof-devnet-1: Update EIP-3540: Change to max_stack_increase by chfast · Pull Request #9600 · ethereum/EIPs · GitHub

Author: pdobacz
Date: 2025-04-09T10:19:31.327Z

Another minor update, planned for eof-devnet-1 (section kind for data section 0x04 → 0xff) Update EIP-3540: Change kind_data from 0x04 to 0xff by pdobacz · Pull Request #9502 · ethereum/EIPs · GitHub

Author: pdobacz
Date: 2025-04-09T14:07:31.967Z

Final eof-devnet-1 update - 4-byte container size to accommodate increasing codesize limits Update EIP-3540: Make container section size 4 bytes wide by gumb0 · Pull Request #9581 · ethereum/EIPs · GitHub

Author: charles-cooper
Date: 2025-04-10T14:15:58.892Z

I posted this in the agenda for today’s ACDE meeting, and it seems that ACD has already decided on moving forward with EOF for Fusaka. However, I wanted to post this here to record Vyper’s position on this future reference so that it doesn’t get lost in Github issues.


github.com/ethereum/pm








All Core Devs - Execution (ACDE) #209



        opened 05:04PM - 27 Mar 25 UTC




          timbeiko
        




          ACD
        

          Execution
        




# All Core Devs - Execution (ACDE) #209

* [April 10, 2025, 14:00-15:30 UTC](htt…ps://www.timeanddate.com/worldclock/converter.html?iso=20250410T140000&p1=1440&p2=37&p3=136&p4=237&p5=923&p6=204&p7=671&p8=16&p9=41&p10=107&p11=28)
* 90 minutes
* Stream: https://youtube.com/live/_j4XPNQxDPc

# Agenda 

- Pectra 
    - EIP-7702 receipts
      - https://github.com/ethereum/execution-apis/pull/640
      - https://ethereum-magicians.org/t/eip-7702-set-eoa-account-code/19923/382
- [Fusaka](https://eips.ethereum.org/EIPS/eip-7607)
    - Scope finalization
        - PFI vs. CFI vs. SFI 
        - Client Team preferences
            - [Besu](https://hackmd.io/@RoboCopsGoneMad/H1z1lky6Je)
            - [Reth](https://www.paradigm.xyz/2025/02/what-comes-after-ethereums-pectra-hard-fork)
            - [Erigon](https://x.com/GiulioRebuffo/status/1900854414174204182)
            - [Nethermind](https://x.com/M25Marek/status/1896911082628727213) 
            - [Geth](https://ethereum-magicians.org/t/eip-7607-fusaka-meta-eip/18439/12?u=matt) 
        - Synchronous vs. async presentations
            -  EIP-7819 
            - [EIP-7918](https://ethereum-magicians.org/t/eip-7918-blob-base-fee-bounded-by-execution-cost/23271) 
- [Reconfiguring AllCoreDevs](https://ethereum-magicians.org/t/reconfiguring-allcoredevs/23370)

--- 

Facilitator email: tim@ethereum.org






The TL;DR is:

Vyper doesn’t see substantial benefits from EOF
EOF creates a large amount of work for us, with practically no benefit
It breaks developer assumptions, and requires them to hold two mental models when developing for EOF vs non-EOF
It takes attention and energy away from other improvements which the EVM badly needs, such as fixing memory and transient storage pricing (and with it, lowering the barrier to protecting against reentrancy), fixing the 63/64ths rule, code size limit improvements and PAY opcode
All the benefits around static jumps can be shipped in a much less invasive way, which is a lot less burden for us to implement and maintain (and probably allow us to generate better code!), and without breaking developer assumptions

In summary, I believe that moving forward with EOF would be a serious mistake for Ethereum and hurt its competitiveness. It demonstrates to app and tooling/compiler developers that we do not care about the stability of the VM, and permanently (or at least for multiple years) increases the maintenance burden of apps and compilers across EOF and non-EOF ecosystems.

Author: shemnon
Date: 2025-04-10T20:05:09.032Z

For the record, I also will post some of my responses from that github thread.

The EVM Resource Pricing Working group operates independently of the EOF efforts. I assert we are not holding them back.  See their last meeting notes here.  I look forward to the proposals from this working group and the changes the “Gaspocalypse” will bring.
The EIPs listed for a less invasive way were all separately propsoed and rejected by the ACD process.  This includes EIP-615, EIP-1702, and EIP-2315. EOF was crafted with the reasons for rejection in mind.  Note that EIP-4200 was always presented as a part of EOF.

I am personally of the opinion that a strong container format is essential for the continued competitiveness of the EVM, as other smart contract VMs have brought up the lack features EOF provides in a number of contexts as to why their solution is better.

Author: charles-cooper
Date: 2025-04-13T14:48:19.407Z

shemnon:


The EVM Resource Pricing Working group operates independently of the EOF efforts. I assert we are not holding them back. See their last meeting notes here. I look forward to the proposals from this working group and the changes the “Gaspocalypse” will bring.



That’s simply not true. A litany of EIPs were rejected for Fusaka the other day just on the basis that EOF is already enough to implement for a single hard fork, including a memory repricing EIP.



 shemnon:


The EIPs listed for a less invasive way were all separately propsoed and rejected by the ACD process. This includes EIP-615, EIP-1702, and EIP-2315. EOF was crafted with the reasons for rejection in mind. Note that EIP-4200 was always presented as a part of EOF.



I don’t think this is a strong argument against those EIPs. There is a lot of precedent where EIPs were rejected for one hard fork but included at a later date.



 shemnon:

I am personally of the opinion that a strong container format is essential for the continued competitiveness of the EVM, as other smart contract VMs have brought up the lack features EOF provides in a number of contexts as to why their solution is better.


I’ll take your word for it, but developers are still preferring to use the EVM. I’d hazard to say that developers prefer to use the EVM due to its stability and network effects. EOF runs counter to that stability. This is not about ossification or not ossification, but if upgrades are truly needed, can we upgrade in a smart way?

Some more responses that I had drafted for that github thread (but wanted to avoid spamming after timbeiko stated it would not be on the call agenda):


The attitude I have heard expressed on ACDE and EOF calls is very much that, going forward, ACD will not ship any more improvements to non-EOF EVM. So in practice, non-EOF EVM will be a no-go as a compilation target for mainnet.

There hasn’t been a formal policy for that yet. The best argument for only adding new opcodes inside the EOF container is the claim that nobody will use them outside an EOF container.

Yes, people will absolutely want to use PAY outside of EOF. The case for this is pretty clear if you think about it from the perspective of the user. If they write code for EOF and then need to deploy it on a non-EOF-supporting chain, then the code will either not deploy or worse, behave differently than it was intended to (depending on how PAY is exposed in the high-level language). This is one of my chief arguments against the current EOF package, which is that it breaks developer assumptions, sometimes in subtle ways, and often without even meaning to.
I already have people coming to me complaining that they have to recompile code for L2s that don’t support PUSH0 – and that one is just an opcode which has the same semantics as PUSH1 0, but just doesn’t deploy pre-Shanghai. What is going to happen when their code deploys, but actually has completely different semantics(!)?

EIP-2315 does not prevent the natural flow of code from entering or exiting a subroutine context. While there is disagreement as to wether or not it is a good thing or a bad thing it was one of the technical arguments that people opposing it (including @lightclient) found persuasive.

Given that @matt also opposes EOF, I’m not sure that this really moves the needle on the argumentation for it or against it.

EIP-4200 requires immediate data, which breaks the current jumpdest model, and would require a separae validation regime.

These three EIPs give you full freedom from JUMPDEST analysis.

I agree it is a simpler design, but for the problems they solve they are too simple and will inhibit good solutions to other issues the EVM faces.

Simple solutions are good! Also, I’m not sure which issues are inhibited. For every single issue I’ve seen that EOF is supposed to solve, my take is that it’s either solvable without the full EOF package, or the fact that EOF has to coexist alongside non-EOF, forever, means that the problem is presumptively solved, but in practice, only halfway (e.g. gas introspection).

Author: OlivierBBB
Date: 2025-04-13T15:28:43.643Z

jochem-brouwer:

I am also fairly sure that with PUSH if not all data can be read (e.g. the contract 60, PUSH1 where it is not clear what data should be pushed) then also STOP is executed.


The yellow paper specifies that the PUSHX instruction is carried out using the available bytes and right padding with zeros if the bytecode runs out. This is consistent with how (EXT)CODECOPY append 0’s to the right of byte code when the actual byte code runs out.

Author: gcolvin
Date: 2025-04-16T03:40:55.993Z

@charles-cooper   EOF provides the one benefit I most care about:  It becomes possible to traverse the flow of control of an EVM program in linear time.  In the current EVM that is impossible.  Which means that linear-time validation, symbolic execution, and compilation of EVM code is impossible.  My own work has been stymied for ten years now for that reason, and lots of other people’s work has been made difficult to impossible as well.  So I’ll put up with a lot to get that benefit.
The current version of EIP-2315 depends on the earliest parts of EOF to separate code from data and provide for immediate arguments and relative jumps anyway.  Do you find those parts of EOF be a problem?
The remainder of 2315 amounts to an alternative to EOF Functions and Stack Validation.  Subroutines in 2315 take up less space and can be better optimized, but provide fewer safety benefits.  Do you find EOF Functions to be a problem?
The rest of EOF I’d be fine with postponing, but if EOF Functions don’t get in I fear that nothing will get in before I die.

Author: gcolvin
Date: 2025-04-17T18:42:38.074Z

charles-cooper:

EIP-2315 does not prevent the natural flow of code from entering or exiting a subroutine context. While there is disagreement as to wether or not it is a good thing or a bad thing it was one of the technical arguments that people opposing it (including @lightclient) found persuasive.


Preventing global jumps is obviously an impediment to optimization.  And given a stack validation function it provides little by way safety – validation of EVM code provides discipline at the semantic level that high-level languages provide by syntactic restrictions.  High-level languages disallow global jumps and lots of other constructs on the theory that it’s better to protect the programmer and let the optimizer do the dirty work.   But EVM code is the output of an optimizer, and EOF needs special opcodes to provide just some of the optimizations that global jumps can provide.