# EVM Object Format (EOF): Managing the Bytecode Chaos by Alex Murashkin | Devcon SEA

- Channel: [Devcon](https://streameth.org/devcon)
- Date: 2025-10-09
- Duration: 26:06
- Topics: Science & Technology
- Watch: https://streameth.org/watch/yt-WKVgCoNp39g
- YouTube: https://www.youtube.com/watch?v=WKVgCoNp39g

## Description

Currently, EVM bytecode, while being powerful and simple, is lacking structure. This leads to many complexities when introducing new EIPs and maintaining backwards compatibility.

In this talk, we illustrate some use cases of the EVM Object Format (EOF). Next, we provide a quick overview of the main changes introduced by the EOF and related EIPs, along with code examples. Finally, we discuss potential benefits and drawbacks that could arise with the introduction of EOF

Speaker(s): Alex Murashkin
Skill level: Intermediate
Track: Core Protocol
Keywords: Ethereum Roadmap, Protocol Design, Security, upgrade

Follow us: https://twitter.com/efdevcon, https://twitter.com/ethereum, https://warpcast.com/devcon
Learn more about devcon: https://www.devcon.org/
Learn more about ethereum: https://ethereum.org/ 

Visit the https://archive.devcon.org/ to gain access to the entire library of Devcon talks with the ease of filtering, playlists, personalized suggestions, decentralized access on Swarm, IPFS and more.

Devcon is the Ethereum conference for developers, researchers, thinkers, and makers. 
Devcon SEA was held in Bangkok, Thailand on Nov 12 - Nov 15, 2024.
Devcon is organized and presented by the Ethereum Foundation. To find out more, please visit https://ethereum.foundation/

## Transcript

Hello everyone. Thank you for the intro and thank you for attending the session this morning. It's the first session, so I really appreciate everyone coming. Yeah, so I'm Alex from Quantstamp and together with my colleague Valerian we looked at how the bytecode chaos can be managed through introduction of EVM object format or EOF. And so what is EOF and why? So there was actually a really nice talk by Danno Ferrin on Tuesday which dives deeper into the motivation and I highly recommend watching that when you get a chance. I'll just briefly mention what this is about. So EOF is EVM object format. It's a new format of the EVM bytecode. That's usually formats have special magic markers, right? So in this case it's EF00. So if the bytecode starts with those characters those bytes it means that it's EOF code. It was originally considered for the Pectra upgrade and that's when Valerian and I firstly first looked at it. Then it got postponed so potentially it will be part of the one of the upgrades after that. But apart from the technical readiness are we actually ready for this upgrade? Because the questions to ask here is are we do we understand it well enough? What does it do, right? Is the tooling ready? What are the upside and downside of of this new format and how's the security side looking? So all these questions we were trying to to answer in this in this talk. And why well currently any bytecode can be deployed as a smart contract. There is no predefined meta structure. No well-defined versioning. So it's basically maybe some semantics can change over hard forks or new opcodes can appear with the with the new hard hard forks of the EVM. But there was no indication of the versioning that is actually part of the bytecode. And there is no thorough deploy time validation of the code. So which makes which makes a lot of other things tricky. So yeah and overall we have the following issues, right? So it's difficult to add and duplicate features many of us have experienced being the devs. And it's hard to analyze the code and also it's a little bit difficult for for the compilers. And also it's hard to fully solve any long-lasting I guess omissions in the original EVM because as actually as as then it was mentioning in the talk on Tuesday it's either you have to just keep making small tweaks or you have to just break everything at once and just make it make it right. And yeah the solution there is we add structure versioning and EVM is made better in that new version. While the legacy version would continue to to live on. And yeah so you you have is actually quite a complex upgrade. There is on the right side you see quite a few EIPs that are that go with it. And my colleague there tried to draw a diagram of how this is all pieced together and with some some arrows that signify some of the interactions between EOF and legacy. We do not have time to look at all of them, so I will just try to give a bit of highlights of those items that I've that I highlighted on the slide. And we'll give some code examples as well. So, let's move let's get to it. Well, on the left side you see a toy example. This is a very very simplistic example because we just wanted something simple to to go with to just showcase the differences between the legacy VM and the new EOF format. On the Solidity side, we just have an infinite loop contract which pretty much just loops forever until it runs out of gas. And this is how the Yeah, below you see how it could be represented as a bytecode. This is a very rough manual translation there. It's the actual bytecode would would be much larger because there will be additional checks included by the compiler. Yeah, so what we have here is um basically jump test instruction which is 5B. And then followed by push one with 0x00 which is the location to jump to. And then the finally finally the actual jump command. So, it's basically you tell where the jump destination is which is in this case just 0x00. And then you push that value onto the stack and then you jump to that location. So, it's pretty much the the infinite loop. That's how it's intended for this example. Now, let's look at EOF. Here we have a whole bunch of other things. Uh we now see the structure, right? There is the header and there is the body. In the header, we have EOF magic, uh which we've already seen on the previous slide, uh which is followed by the version, uh version one in this case. Then uh it's uh describes the sections. Uh in this case, we have the types section. Types is indicated as 01. And the length of that type section is four bytes. Same with the code section. Uh we have one code section, which is also four bytes long. And finally, there is the data section, and uh we will not dive deeper into that. We just kind of omitted that completely. So, there is no data section. Its length is zero. That's as far as the header goes. So, in the body, uh there are two subsections. One is the types and another one is code. So, uh in the type section, what's described is the number of inputs, uh number of stack elements. Well, in this case, there is a special uh character a special byte sequence uh just a 0x 80 if there are none, and the maximum stack height, which is two. So, the way I think of this type section, uh from my perspective, it's almost like a the signature of a function, but applies to the whole contract in this case. And finally, then we have the body section, and that is where we would have placed the bytecode from the left-hand side. That would would have been the the correct place for it. Uh but, as we see on the next slide, this bytecode is actually no longer valid in the EOF world. So, this is our like simple toy example that we will uh we'll go with. So, what is wrong about that uh bytecode that is highlighted in red? Well, the the thing is in EOF, static relative jumps replace the conventional uh dynamic uh jumps. And uh yeah, so in the legacy EVM, jumps are dynamic, which uh requires so-called uh destination code analysis since it is not clear beforehand whether the code at the destination is even uh executable or valid code. Uh so, that's why the analysis had to be uh done dynamically. And to simplify that, you we had to had to have this jumpdest instruction that we saw uh earlier to uh highlight that this is one of the valid destinations for the for the jump. Um yeah. So, yeah, as we see here in our toy example, uh that's how uh uh that's how the toy example will be translated from the legacy bytecode into the EOF bytecode. Right? So, on the uh yeah, in the middle here, we see uh jumpdest, push one, and jump. On the right side, uh we replace that absolute jump with a relative jump. And just for the purpose of this example, we say, "Well, how about we just jump to uh minus one, which is just going back." So, that's that way we kind of get the uh the infinite loop. Okay. Uh how about uh deployment of smart contracts? Well, uh in the legacy VM, what we already know, we have to use the create instructions and there is the concept of init code, which gets executed just once. Uh, and this will pretty much place the the bytecode onto the permanent storage. So, here on the left side, you see the init code, which looks fairly standard. It's basically just uh, pretty much highlighting the sections of the memory where the bytecode is and storing it and then there is the final return statement at the end. And the actual code is pretty much at the end here. So, it's the same bytecode as we've seen on the previous slide. Well, now how about EOF? Well, interestingly, EOF init code is also on itself, a EOF smart contract. So, EOF bytecode. So, what we have here is we have the header and the body. Uh, but the interesting thing here is that there is so-called subcontainer. And the subcontainer is pretty much the bytecode that we've seen on the previous slide of EOF. So, you see at the very, very bottom there, it says uh, subcontainer and it's exactly like we had on the on the previous slide. So, pretty much what you see on the right side here, that's that's what is at the bottom of this slide. And if we go over this a little bit, we have yeah, also all these different sections and in the header, we see that there is a definition of subcontainer, which is 03 and it's 22 bytes long. Uh and by the way, Foundry has this nice command now called uh cast decode EOF. So, if we run the bytecode through that command, here is the visualization that Foundry gives us. It is It is quite neat, pretty much another way of of looking of visualizing the bytecode. Uh yeah, and what about functions? Well, it turns out that functions can also be self-contained and encapsulated within EOF as different code sections. For example, in this example, we have just a subroutine, which doesn't do anything, just returns the the input back to the caller. And in the legacy code, that's how we would probably code it by hand. And in the in the uh EOF, what we have is we have the call F and read F uh instructions now, uh which um could allow us to pretty much do the function calls within the same EOF structure, just as just using different code subs sub subsections here. So, we have code section one and code section two here. Uh yeah, and the standard EIP 3670 defines how code validation is to be done. Then uh yeah, usually it involves basically just validating whether all the instructions are valid. And uh for example, self-destruct and all the legacy instructions will be uh deprecated. And yeah, so this jump dest analysis is no longer necessary there because all all jumps are replaced with the relative jumps. So, this should make things easier. Uh and yeah, well, I guess the question is what about interactions between the legacy and the EOF contracts? So, what which rules would apply there? Well, uh we just glanced at it and seems like some interactions are allowed and some are not allowed. For example, create create two will fail if you deploy try to deploy a byte byte code starting with uh 0xEF, which is actually already a part of the EVM as of as of now. Uh can um EOF contract deploy a legacy contract? No. So, this is this is not valid. Uh can a an EOF contract call a legacy contract? Uh yes, if you use XCALL and XSTATICCALL, it is allowed. Uh in case of delegate call or I guess replacement of delegate call, this is this is not allowed. Uh can legacy can a legacy code um call an EOF contract? Yes, it is possible and the intention there is that uh the existing um proxy contracts can be upgraded to use the EOF uh implementations. And also note there that EOF must not be enabled on chains where uh the EF00 prefixed byte codes are not valid um EOF. So, that's but that's already taken care of. Uh another modification is uh call instructions got uh revamped to remove the so-called uh gas observability because it's has been an issue uh before. And uh as of uh the EOF upgrade listed in that um EOF version, uh the control will uh the caller will no longer have control over the amount of gas that is passed as a part of the function call. So, that that was like one of the notable updates. And certain other rules were kept, for example, the 6364th rule and also max call depth, even though it was considered for removal. And a few other modifications as well. Well, how about support and implementation? To our knowledge, we are not core devs, we just like pretty much uh we are looking at the publicly available sources. So far, it seems like it's still in the works. It It's implemented in some of the clients, but in some other clients it is not fully implemented, including Geth and the so C. So, we did not try as much uh playing around with uh with this with these tools yet, but I think that this will be a different completely different story later. And uh while checking uh while checking for um materials for this talk, we found a few interesting uh tools, which include the fuzzer, include the some code performance benchmark, and also bytecode parsers, EVM bytecode parsers. And uh yeah, and some other useful sources. There are no uh URLs here, but let me know if you're interested, I I can just share the the whole slide with you with all the references. Uh yeah, and uh interestingly, we found uh a few performance analysis of uh the new uh format. Uh so, the build the builder BuildBear Labs have conducted some some tests, and the results uh are here, and they are available as uh at this URL as the source. So, the takeaway there is that um the execution time is actually also about 15% faster, apart from uh all these other uh we see some reductions uh the init code size, in deployed code size, and also the the gas usage for for deployment. Which is quite interesting. So, how about the security aspect? So, yeah, we had uh Yeah, we we looked and tried to find more data on this, but seems like so far these these are the the few points that we were able to find. So, one is removal of jump destination analysis, which has its own positive implications on analysis of the of the bytecode. Uh, stack operations have been revised. This upgrade is not EOF specific, and I noticed that there is another talk on EOF later today in this in this room, which potentially would dive deeper into into this topic. Uh, yeah, and static jumps, as we've already mentioned, and also the revision of gas observability. So, these are potential indirect gas benefits on the security. And for the for the stack operations revision stack operations revision aspect, it can prevent from this stack too deep issue that previously sometimes required rewriting your code and potentially make it make it simpler or make it more complicated depending on the the code that you have. And what are the drawbacks and risks? Well, I think it's In my opinion, it's mostly about complexity. So, it just we have so many new opcodes introduced. All of them need to be tested, and you also have 16 legacy opcodes that continue to exist, and you have 152 shared codes that will be existing in both versions. And yeah, I think that would be be to actually have um more publicly known security analysis of this change, but I'm sure this will this will appear since it's still a work in progress. So, I think that this is this should be good. Well, and uh so, in conclusion, basically we just uh tried to uh learn a little bit more about EOF and try to convey our understanding, our perspective uh on this and um of course all of this is uh subject to change and also we are not core devs, so it's totally possible that we got certain inaccuracies in our understanding, but nevertheless, I think that uh goal is to basically learn more about it and have the community a little bit more engaged in the in this topic. Uh yeah, and so, EOF should manage the bytecode chaos by versioning structure, relative jumps, and uh better stack management, and deploy time validation. And yeah, EOF uh is seen as more like an investment in the future, but I think the benefits will be clearer later in the in the long term. Uh that's it from my side. Thank you. Thank you, Alex. That was a very interesting uh talk. Uh so, we have uh quite a few questions. Uh if anyone else wants to ask a question, please scan the QR code there. Uh so, let's start with the most popular one. So, is code size an important concern, and does EOF uh help reduce code size? What about performance? Uh yeah, so we personally did not do the analysis on that uh front, but uh we saw other uh teams did that. So, for example, that uh slide that I had with uh Buildbear, uh Labs that did this analysis, and they did see the the in uh the code size and also uh improvement to performance. I think performance is definitely one of one of the aspects. So, yeah, I think that this um this this is looking promising on that front, but it will be clearer as we have more um I guess more people look into this topic. Great. Uh so, what's the Viper's progress towards EOF? Is it going to be supported? Uh to be honest, I do not know. Uh but maybe if somebody who actually looks uh works in that uh would be be good. Okay. So, what's the reason for relative jumps instead of directly indexing of bytes in the code array? Uh yeah, so relative jumps in my understanding give have certain benefits. Uh so, because it allows you to actually write the code that is more or less self-contained and in comes encapsulated. So, uh you don't always have Maybe you don't always care the absolute location of the code, but uh you pretty much can reference other sections of the code and also the code within the code section uh in the in a relative manner. So, I think that this is provides this type of uh benefit in my opinion. Great. So, I guess there is uh already a PR for EOF, which is good news. Uh I really like the next question. What are the arguments against EOF? Well, the main argument we've seen uh when looked at this talk is just the complexity, right? Because it's a very complex upgrade. There are so many things. It kind of makes sense because the from from Danno's talk on Tuesday, it's basically been pretty much an effort to try to make many changes all at once cuz you cannot just do all these kind of piecemealing the of the of the changes. You cannot just simply do it without breaking other things. So, it makes sense to kind of have a one major complex upgrade. And if you have the version in along with it, then you can pretty much have that um done uh in a non-breaking manner while continuing to have the the legacy code supported. Yeah, I think the complexity is uh probably like one one aspect. Mhm. Uh so, for wraps which invoke transactions with an arbitrary gas limit, will that uh param simply be ignored post the UAF? Uh yeah, so my understanding is that uh maybe it will not even be allowed to pass it, but uh I do not know the the details of how it's going to look like from the developers' perspective. But yes, definitely it seems like the concept of gas observability which is to be uh duplicated, so uh it will be probably not uh uh not possible to have to work with this low-level concepts as uh gas. Mhm. Uh so, uh the next question is about legacy opcodes, and I guess if they are going to be removed from existing EVM implemented implementations with Yeah, my understanding is that all the legacy byte codes would still continue to be supported. All right, so you cannot simply just, you know, completely remove it. In the EOF uh format, if your byte code starts with EF00, that's a different story, right? So, basically you would have you have uh um you have better control over which opcodes will be part of at uh new upgrade. Since it's a different pretty much format. Uh yes, uh from what I recall from my slides, there will be some uh legacy opcodes that are completely removed or replaced with a counterparts for the for the removal for example self-destruct, right? So, finally you can actually just Well, I guess removal here means just not including it in EOF. So, that's Great. And what happens because we don't have that much time. What happens if there is a legacy of code in EOF? It's going to be rejected or it's going to be in a runtime error? Uh yeah, so there is basically Yeah, we have looked at some interactions of EOF and legacy byte codes and some interactions are simply not even allowed. So, for example, you cannot deploy uh EOF or you cannot deploy a legacy contract from a EOF contract and and vice versa. So, it's kind of like they they draw the line somewhere where you you only can go in one direction and there is some disjoint um sets of interactions that that are possible. Great. And again, it was an amazing presentation. Let's let's thank Alex one more time. Thank you very much.
