Author: Vitalik, founder of Ethereum; Translation: Golden Finance 0xjs
From the beginning of the Ethereum project, there has been a strong philosophy to keep the Ethereum core as simple as possible and to do as much work as possible by building protocols on top of it. In the blockchain space, the debate of "executing on L1" vs. "focusing on L2" is often thought to be mainly about scaling, but in reality, similar issues exist in the context of serving many types of Ethereum user needs: digital asset exchange, privacy, usernames, advanced cryptography, account security, censorship resistance, front-running protection, and so on. However, recently some people have shown an interest in cautiously formalizing more of these features into the Ethereum core protocol.
This article will explore some of the philosophical reasoning behind early Ethereum protocol minimalism, as well as some of my more recent reflections on these ideas. The goal is to gradually build a framework for better identifying goals that might be worth considering in forming certain features in the protocol.
Early Protocol Minimalism Philosophy
Early in the history of “Ethereum 2.0” (as it was then called), there was a strong desire to create a clean, simple, and beautiful protocol that did as little work as possible on its own, leaving almost everything to the users to build on top of it. Ideally, the protocol would be just a virtual machine that only needed to be called once to verify a block.

A very rough reconstruction of a whiteboard diagram Gavin Wood and I made in early 2015 when we were discussing what Ethereum 2.0 would look like.
The “state transition function” (the function that processes a block) will be just a VM call, and all other logic will be implemented via contracts: some system-level contracts, but mostly user-provided contracts. A very nice property of this model is that even an entire hard fork can be described as a single transaction to the block processing contract, which will be approved by off-chain or on-chain governance and then run with upgraded permissions.
These 2015 discussions are particularly applicable to two areas we care about: account abstraction and scaling. In the case of scaling, the idea is to try to create a maximally abstracted form of scaling that feels like a natural extension of the above diagram. A contract can call some data that most Ethereum nodes don't store, and the protocol will detect this and resolve the call through some very general scaling computation function. From the perspective of the virtual machine, the call will go into some separate subsystem and then magically return with the correct answer some time later.
This way of thinking was briefly explored, but quickly abandoned because we were too focused on validating whether any form of blockchain scaling was possible. However, as we will see later, the combination of data availability sampling and the ZK-EVM means that one possible future for Ethereum scaling may actually look surprisingly close to this vision! With account abstraction, on the other hand, we knew from the outset that some kind of implementation was possible, and so immediately began investigating how to make the pure starting point of “transactions are just calls” as close to reality as possible.

Here, there is a lot of code happening between and after the code that processes the transaction and makes the underlying EVM call to the sending address. How can we reduce this code to as close to zero as possible?
One of the main things that happens in one of these pieces of code is validate_transaction(state, tx), which among other things checks that the transaction's nonce and signature are correct. The actual goal of the account abstraction from the beginning was to allow users to replace basic nonce incrementing and ECDSA verification with their own verification logic, so that it would be easier for users to use things like social recovery and multi-signature wallets. Therefore, reshaping apply_transaction to make it a simple EVM call was not just a "clean code for the sake of clean code" task; rather, it was about moving logic into the user's account code to give users who need flexibility.
However, the insistence on keeping apply_transaction containing as little formal logic as possible ended up introducing a number of challenges. To understand why, let’s dive into one of the early account abstraction proposals, EIP 86:
specification
If block.number >= METROPOLIS_FORK_BLKNUM, then: 1. If the signature of the transaction is (0, 0, 0) (i.e. v = r = s = 0), it is considered valid and the sender address is set to 2 ** 160 - 1. 2. Set the address of any contract created by the creation transaction to sha3(0 + init code) % 2 ** 160, where + indicates concatenation, replacing the previous address formula sha3(rlp.encode([sender, nonce])) 3. Create a new opcode, 0xfb, CREATE_P2SH, which sets the creation address to sha3(sender + init code) % 2 ** 160. If a contract already exists at this address, it fails and returns 0, just like the initialization code ran out of Gas.
Basically, if the signature is set to (0, 0, 0), the transaction actually becomes "just a call". The account itself will be responsible for parsing transactions, extracting and verifying signatures and nonce, and paying fees. Click here to view an early version of this code. Click here to view the validate_transaction code that this account code will replace. At one time, apply_transaction was simply reduced to a process of just one EVM call. In other words, Ethereum's protocol will perform transaction verification and leave the specific transaction logic to the user's own account code to provide flexibility.
However, trying to make apply_transaction contain as little formal logic as possible ended up introducing a number of challenges. To understand why, let’s look in detail at one of the early account abstraction proposals, EIP 86.
Note that miners need to have a strategy for accepting these transactions. This strategy needs to be very discriminating, otherwise they might accept transactions that pay no fees (for validate_transaction code that will be replaced with pre-account code), or even transactions that have no effect (e.g. because the transaction has already been included and the nonce is no longer valid). A simple approach would be to whitelist the codehashes of account codes that they accept transactions to; approved code would include logic for paying the miner's transaction fee. However, this may be overly restrictive; a more relaxed but still effective strategy would be to accept any code that fits the same general format as above, consumes only a limited amount of gas to perform nonce and signature checks, and guarantees that the transaction fee will be paid to the miner. Another strategy would be to attempt to process any transaction that requests less than 250,000 gas, in addition to other approaches, and only include it if the miner's balance would increase appropriately after executing the transaction.
If EIP-86 were included as is, it would reduce the complexity of the EVM, but at the great expense of increasing the complexity of other parts of the Ethereum stack, requiring essentially the exact same code to be written elsewhere, while introducing entirely new and bizarre possibilities, such as the possibility that the same transaction with the same hash could appear on-chain multiple times, not to mention multiple invalidation issues.
Multiple invalidation problems in the account abstraction. One transaction included on-chain can invalidate thousands of other transactions in the mempool, making the mempool susceptible to cheap flooding.
Account abstraction evolved gradually from there. EIP-86 became EIP-208, which became this post on ethresear.ch about the “Tradeoffs in the Account Abstraction Proposal”, and then half a year later became this post on ethresear.ch. Ultimately, all of this resulted in EIP-2938, which is actually quite workable.
However, EIP-2938 is not minimal. This EIP includes:
A new transaction type
Three new transaction-scoped global variables
Two new opcodes, including the very bulky PAYGAS opcode, handles gas price and gas limit checking, acts as an EVM execution breakpoint, and simultaneously temporarily stores ETH for fee payments.
A complex mining and replay strategy, including a list of banned opcodes during the transaction validation phase
In order to push for account abstraction without involving Ethereum core developers who were busy optimizing Ethereum clients and implementing the merge, EIP-2938 was ultimately redesigned as ERC-4337 completely outside the protocol.
ERC-4337. It does rely entirely on EVM calls to do everything!
Because it’s an ERC, it doesn’t require a hard fork and is technically “outside the Ethereum protocol.” So… problem solved? Well, it turns out, not quite. The current mid-term roadmap for ERC-4337 actually involves eventually turning much of ERC-4337 into a series of protocol features, which is a useful example of why this path is being considered.
ERC-4337 is solidified at the protocol layer
Some key reasons have been discussed for finally introducing ERC-4337 into the protocol:
Gas efficiency: Any operation performed inside the EVM incurs some level of VM overhead, including inefficiencies when using expensive gas features such as storage slots. Currently, these additional inefficiencies add up to at least ~20,000 gas, and often more. Pushing these components into the protocol is the easiest way to eliminate these issues.
Code bug risk: If there is a sufficiently horrible bug in the ERC-4337 "entry point contract", all ERC-4337 compatible wallets may see all their funds wiped out. By replacing the contract with a protocol function, an implied responsibility to fix code bugs is created and the risk to user funds is eliminated.
Support for EVM opcodes such as tx.origin. ERC-4337 natively makes tx.origin return the address to which a "bundler" that packages a set of user operations into a transaction is sent. The native account abstraction can solve this problem by making tx.origin point to the account that actually sent the transaction, making it work the same as EOA.
Censorship resistance: In the case of PBS, one challenge is that it is easier to censor individual transactions. In a world where every transaction is visible to the Ethereum protocol, this problem can be greatly mitigated through inclusion lists, which allow the proposer to specify a list of transactions that must be included in the next two slots in almost all cases. However, out-of-protocol ERC-4337 wraps "user actions" inside a single transaction, making user actions invisible to the Ethereum protocol; therefore, inclusion lists provided by the Ethereum protocol will not provide censorship resistance for ERC-4337 user actions. Formalizing ERC-4337 and making user actions an "official" transaction type would solve this problem.
It’s worth digging a little deeper into the issue of gas efficiency. In its current form, ERC-4337 is significantly more expensive than a “basic” Ethereum transaction: a transaction costs 21,000 gas, while ERC-4337 costs about 42,000 gas. The document lists some of these reasons:
There are many separate storage read/write costs to pay, which for EOA are bundled into a single payment of 21,000 gas:
Edit the storage slot containing the pubkey+nonce (~5000)
UserOperation calldata cost (~4500, reduced to ~2500 with compression)
ECRECOVER(〜3000)
Warm up the wallet itself (~2600)
Warm up the receiver account (~2600)
Transfer ETH to receiver account (~9000)
Edit storage to pay for it (~5000)
Access the storage slot containing the agent (~2100), then the agent itself (~2600)
In addition to the aforementioned storage read/write costs, the contract also needs to perform "business logic" (unpacking the UserOperation, hashing it, shuffling variables, etc), whereas EOA transactions are handled "for free" by the Ethereum protocol.
Gas is required to pay for logs (EOA does not issue logs)
One-time contract creation cost (base cost is ~32000, plus 200 gas per byte of code in the proxy, plus 20000 gas to set up the proxy address)
In theory, it should be possible to make in-protocol costs and extra-protocol costs of storage access match by manipulating the EVM gas cost system; there's no reason why transferring ETH should cost 9000 gas when other storage edit operations are much cheaper. In fact, two EIPs related to the upcoming Verkle tree transition actually attempt to do this. But even if we do this, there's one huge reason why formal protocol features will inevitably be much cheaper than EVM code, no matter how efficient the EVM becomes: formalized code doesn't need to pay gas to be preloaded.
A fully functional ERC-4337 wallet is large. This implementation, compiled and placed on-chain, takes up ~12,800 bytes. Sure, you could deploy that massive code once and use DELEGATECALL to allow every single wallet to call it, but that code would still need to be accessible in every block that uses it. According to the upcoming Verkle tree gas cost EIP, 12,800 bytes would take up 413 chunks, and accessing those chunks would require paying 2x WITNESS_BRANCH_COST (3,800 gas total) and 413x WITNESS_CHUNK_COST (82,600 gas total). That’s not even mentioning the ERC-4337 entrypoint itself, which as of version 0.6.0 is 23,689 bytes on-chain (which, according to the Verkle tree EIP rules, takes ~158,700 gas to load).
This leads to a problem: the gas cost of actually accessing this code has to be distributed between transactions in some way. The approach currently used by ERC-4337 is not very good: the first transaction in the bundle absorbs the one-time storage/code reading cost, making it significantly more expensive than the rest of the transactions. Making these commonly used shared libraries part of the protocol, accessible to everyone without paying a fee, is a way to solve this problem.
What can we learn from this example about when things become fixed in protocols more generally?
In this example, we can learn some lessons about the more general principles of when to summarize things.
It’s a good idea to move complexity to the edge, especially when there are high costs to solidify. This becomes apparent in the “Decentralized Account Abstraction” roadmap, as loading the standardized wallet code requires 244,100 gas, and aggregation (check out my talk from this summer for more details) can require hundreds of thousands of gas for ZK-SNARK verification, plus the on-chain costs of proof verification. Imposing these costs on users is nearly impossible, as it would introduce many market inefficiencies, which is cleanly addressed by making certain features available to all users as protocol features without fees.
A community response to code errors. If a set of code snippets is used by all users or a wide class of users, it is usually more reasonable for the blockchain community to take responsibility for fixing possible errors through hard forks. ERC-4337 introduces a large amount of globally shared code, and in the long run, fixing errors in that code is better done through hard forks rather than causing users to lose large amounts of ETH.
Sometimes, leveraging the power of the protocol directly can enable a more powerful form of functionality. For example, censorship resistance can be better guaranteed within the protocol, but for user-level operations to truly benefit from the protocol, individual user-level operations need to be "readable" by the protocol. Another lesser-known example is the 2017 Ethereum Proof of Stake design with an authority abstraction, which was abandoned in favor of BLS because BLS supports an "aggregation" mechanism that must be implemented at the protocol and network level, which can make processing large numbers of signatures more efficient.
But it’s important to remember that even account abstraction, once established in the protocol, is still a huge “anti-establishment” relative to the status quo. Today, top-level transactions on Ethereum can only be initiated by externally owned accounts (EOAs) that are validated using a single secp256k1 elliptic curve signature. Account abstraction reflects this, and provides openness for users to define validation conditions. So in this story about account abstraction, we also see the biggest argument against sequestration: flexibility to meet the needs of different users.
Let’s try to round out the story by looking at some of the features that have been considered for implementation recently. In particular, we’ll focus on: ZK-EVMs, proposer-builder separation, private mempools, liquid stake, and new precompiles.
Curing ZK-EVMs
Let’s turn our attention to another possible target for establishment in the Ethereum protocol: ZK-EVMs. Currently, we have a large number of ZK-rollups that all have to write fairly similar code to verify blocks in ZK-SNARKs that perform similar Ethereum-like operations. There is a fairly diverse ecosystem of independent implementations: PSE ZK-EVM, Kakarot, Polygon ZK-EVM, Linea, Zeth, and many more.
One of the recent controversies in the EVM ZK-rollup space has involved how to handle the possibility of bugs in the ZK code. Currently, all running systems have some form of “security council” mechanism that overrides the proof system in the event of a bug. In a post last year, I attempted to create a standardized framework to encourage projects to clarify their level of trust between the proof system and the security council, and move towards reducing the power of the security council over time.

In the medium term, rollups could rely on multiple proof systems, and the Security Council would only have any power in the extreme case of two different proof systems disagreeing with each other.
However, there is a feeling that some of this work is redundant. We already have the Ethereum base layer, which has an EVM, and we already have working mechanisms for handling bugs in the implementation: if there is a bug, the client with the bug will update to fix the bug and the chain will proceed. From the perspective of the buggy client, from the perspective of multiple implementations in the Ethereum community, and from the perspective of off-chain social consensus, blocks may appear finalized, but at least we won’t see users lose funds. Similarly, if a rollup only wants to be and remain equivalent to the EVM, then when their internal ZK-EVM rules need to match upgrades to the Ethereum base layer, they should not implement their own governance, but should build directly on the Ethereum base layer, which knows when to upgrade and to what new rules.
Since these L2 ZK-EVMs are basically using the same EVM as Ethereum, can’t we somehow make “verifying EVM execution in ZK” a protocol feature? In the case of bugs and upgrades, can we handle this by applying Ethereum’s social consensus the same way we already do for the base layer EVM execution itself?
This is an important and challenging topic. There are some subtleties:
1. We want to remain compatible with Ethereum's multi-client philosophy. This means that we want to allow different clients to use different proof systems. This in turn means that for any EVM execution that uses one ZK-SNARK system for proof, we want to ensure that the underlying data is available so that proofs can be generated for other ZK-SNARK systems.
2. While the technology is not yet mature, we may want to conduct audits. In practice, this means exactly the same thing: if any execution is proven, we want the underlying data to be available so that users and developers can check it if something goes wrong.
3. We need faster proof times so that if one type of proof is generated, other types of proofs can be generated quickly so that other clients can verify them. This problem can be solved by creating a precompile with an asynchronous response after a certain time window (e.g. 3 hours), but this increases complexity.
4. We want to support not just copies of the EVM, but "almost-EVMs". Innovation at the execution level and extensions to the EVM are part of the appeal of L2. Given an L2 VM that differs only slightly from the EVM, it would be nice if the L2 VM used a native protocol, ZK-EVM, for the same parts as the EVM, and only relied on its own code to handle the different parts. This could be done by designing the ZK-EVM precompilation so that it allows the caller to specify a bitfield or list of a set of opcodes or addresses that are handled by an externally provided table, rather than the EVM itself. We could also make gas costs customizable to a limited extent.
One possible controversial topic with data availability when using native ZK-EVM is statefulness. ZK-EVMs would be much more data efficient if they didn’t have to carry around “witness” data. That is, if a particular data has been read or written in some previous block, we can simply assume that the prover has access to it and we don’t have to provide it again. This isn’t limited to just not reloading storage and code; it turns out that if a rollup compresses data correctly, making the compression stateful, this can save up to 3x the data compared to stateless compression.
This means that for ZK-EVM precompiles, we have two options:
1. Precompilation requires all data to be available in the same block. This means that the prover can be stateless, but it also means that a ZK-rollup using such a precompilation is more expensive than a rollup using custom code.
2. Precompilation allows pointers to data used or generated in previous executions. This allows ZK-rollup to be almost optimal, but it is more complex and introduces new types of state that must be stored by the prover.
What lessons can we draw from this? There’s a pretty good argument for establishing ZK-EVM verification in some way: rollups are already building their own custom versions, and it feels wrong that Ethereum is willing to put the weight of its multiple implementations and off-chain social consensus behind the EVM execution on L1, while L2, which does exactly the same job, has to implement a complicated gadget involving a security council. But on the other hand, there’s a big mess in the details: there are different versions of established ZK-EVMs, with different costs and benefits. Stateful vs. stateless is just the tip of the iceberg; trying to support “almost-EVMs” with custom code that has proofs for other systems may reveal a much larger design space. So establishing ZK-EVMs has both promise and challenges.
Solidified Proposer-Builder Separation (ePBS)
The rise of MEV makes block production an economic activity of scale, with savvy actors able to produce blocks that earn more revenue than the default algorithms, which simply monitor the memory pool for transactions and include them. The Ethereum community has so far attempted to deal with this problem by using out-of-protocol proposer-builder separation schemes like MEV-Boost, which allow regular validators (“proposers”) to outsource block construction to specialized actors (“builders”).
However, MEV-Boost imposes trust assumptions on a new class of participants, called relayers. Over the past two years, there have been a number of proposals to create “solidified PBSs.” What are the benefits of doing so? In this case, the answer is pretty simple: PBSs built by directly using the power of the protocol are stronger in terms of trust assumptions (in the sense that the trust assumptions are weaker) than PBSs built without them. This is similar to the case of price oracles built into the protocol, although in that case, there is also a strong counterargument.
Establishing a private memory pool
When a user sends a transaction, it immediately becomes public and visible to everyone, even before being included on-chain. This makes users of many applications vulnerable to economic attacks such as front-running: if a user makes a large trade on Uniswap, an attacker could put in a trade before them, increase the price of their purchase, and collect arbitrage profits.
Recently, there have been a number of projects dedicated to creating “private mempools” (or “crypto mempools”) that keep users’ transactions encrypted until they are irreversibly accepted into a block.
However, this scheme requires a specific type of encryption: in order to prevent users from flooding the system and pre-running the decryption process themselves when a transaction is actually irreversibly accepted, the encryption must be able to be automatically decrypted when a transaction is actually irreversibly accepted.
To implement this form of encryption, there are various different techniques, with different tradeoffs, described very well in an article by Jon Charbonneau (and in this video and slideshow):
Encryption to centralized operators, such as Flashbots Protect.
Time-locked encryption, a form of encryption that can be decrypted by anyone after a certain number of sequential computation steps, which cannot be parallelized.
Threshold cryptography, trusting a majority of the committee to decrypt the data. See the shuttered beacon chain concept for a specific proposal.
Trusted hardware, such as SGX.
Unfortunately, each of these has a variety of different weaknesses. For obvious reasons, central operators are unacceptable for inclusion in the protocol. Traditional time-lock cryptography is too expensive to run against a common memory pool for tens of thousands of transactions. A stronger primitive called delay cryptography allows efficient decryption over a certain number of messages, but it is difficult to build in practice, and attacks on existing constructions are sometimes still discovered. As with hash functions, we may need many more years of research and analysis before delay cryptography matures enough. Threshold cryptography requires trusting a majority of the committee not to collude, which is unacceptable in a setting where collusion can be undetectable (unlike a 51% attack where it is immediately known who participated). SGX introduces a dependency on a single trusted manufacturer.
While for every solution there is some subset of users willing to trust it, no solution is currently trustworthy enough to actually be accepted into Layer 1. Therefore, until at least until/unless latency cryptography improves or there is some other technological breakthrough, front-running into Layer 1 looks like a difficult proposition, even though it is a very valuable feature with many application solutions already emerging.
Solidify Liquid Mortgage
A common request from Ethereum DeFi users is to be able to use their ETH both for staking and as collateral in other applications. Another common request is simply for convenience: users want to be able to stake without the complexity of running a node and being always online (and protecting their staking keys which are now online).
By far, the simplest staking “interface” that satisfies both needs is just an ERC20 token: convert your ETH to “staked ETH”, hold it, and convert back again. In fact, liquidity staking providers like Lido and Rocketpool have emerged to do just that. However, there are some natural centralization mechanisms at play with liquidity staking: people naturally flock to the largest version of staked ETH because it’s the most familiar and liquid (and best supported by applications, which in turn support it because it’s more familiar and because it’s the one most users have heard of).
Each version of staked ETH needs to have a mechanism to determine who can become a base node operator. It can’t be unrestricted, because attackers will join and amplify their attacks with users’ funds. Currently, the top two are Lido, which has a DAO that whitelists node operators, and Rocket Pool, which allows anyone to run a node if they put down 8 ETH (i.e. 1/4 of their capital) as a deposit. Both approaches have different risks: The Rocket Pool approach allows an attacker to launch a 51% attack on the network and force users to pay most of the costs. With the DAO approach, if a single staked token dominates, that would result in a single, potentially attackable governance gadget controlling a large portion of all Ethereum validators. For protocols such as Lido, they have implemented countermeasures, but one layer of defense may not be enough.
In the short term, one option is to socially incentivize ecosystem participants to use a diverse set of liquidity providers to reduce the chances of any one becoming so large that it becomes a systemic risk. However, in the long term, this is an unstable balance, and there are dangers in relying too much on moral pressure to resolve the issue. A natural question arises: does it make sense to harden some kind of functionality into the protocol to reduce the concentration of liquidity?
The key question here is: what protocol functionality is being solidified? The problem with just creating a fungible “collateralized ETH” token in one protocol is that it either has to have solidified Ethereum global governance to choose who runs nodes, or it’s open to turning it into a tool for attackers.
One interesting idea is Dankrad Feist’s article on Liquidity Collateralization Maximalism. First, we have to accept the fact that if Ethereum is 51% attacked, only 5% of the attacked ETH will be penalized. This is a reasonable tradeoff; there are over 26 million ETH staked right now, and 1/3 of the attack cost (~8 million ETH) is completely excessive, especially considering how many “out of model” attacks there are that can be performed with much less cost. In fact, similar tradeoffs have been explored in the “super committee” proposal for implementing single-slot finality.
If we accept that only 5% of the attacking ETH will be slashed, then more than 90% of the staked ETH will be immune to slashing, so 90% of the staked ETH can be put into a fungible liquid stake token in the protocol, which can then be used by other applications.
This path is interesting. But it still leaves a question: what exactly is being staked? Rocket Pool works very similarly: each node operator puts up some capital, and liquid stakers put up the rest. We can simply tweak some constants to cap the maximum penalty at 2 ETH, and Rocket Pool’s existing rETH will become risk-free.
With simple protocol tweaks, we can do some clever things. For example, imagine we wanted a system where there were two “tiers” of staking: node operators (high collateral requirements) and depositors (no minimum requirements, can join and leave at any time), but we still wanted to give powers to a randomly sampled committee of depositors, such as proposing a list of transactions that must be included (for anti-censorship reasons), controlling fork choice during periods of inactivity leakage, or requiring signing blocks. This could be done by tweaking the protocol so that each validator must provide (i) a regular staking key and (ii) an ETH address that each slot can call to output a secondary staking key. The protocol would grant power to both keys, but the mechanism for selecting the second key in each slot could be left to the staking pool protocol. It might still be better to solidify something directly, but it’s worth noting that this “solidify some things, leave others to the user” design space exists.
Solidify more precompiled
Precompiles (or "precompiled contracts") are Ethereum contracts that implement complex cryptographic operations natively in client code, rather than in EVM smart contract code. Precompiles were a compromise adopted in the early days of Ethereum: since the VM overhead was too large for certain types of very complex and highly specialized code, we could implement some key operations that are valuable for important types of applications in native code to make them faster. Today, this basically includes some specific hash functions and elliptic curve operations.
There is currently a push to add a precompile for secp256r1, a slightly different elliptic curve than secp256k1 used for basic Ethereum accounts, and since it is widely supported by trusted hardware modules, its widespread use may improve wallet security. In recent years, there have also been efforts to add precompiles for BLS-12-377, BW6-761, generalized pairing, and more.
A counter-argument to these requests to add more precompiles is that many of the precompiles that were previously added (such as RIPEMD and BLAKE) ended up being used less than expected, and that we should learn from that. Rather than adding more precompiles for specific operations, we should probably focus on a more modest approach based on ideas like the EVM-MAX and dormant-but-always-wakeable SIMD proposals, which would allow EVM implementations to execute broad classes of code more cheaply. It might even be possible to remove and replace existing but rarely used precompiles with an EVM code implementation of the same function. That said, there may still be specific crypto operations that are so valuable to speed up that it would justify adding them as precompiles.
What can we learn from all of this?
The desire to harden as little as possible is understandable and good; it stems from the Unix philosophical tradition of creating minimal software that can be easily adapted to the varying needs of its users, avoiding the curse of software bloat. However, blockchains are not personal computing operating systems; they are social systems. This means that there are reasons to harden certain features into the protocol that go beyond the purely personal reasons above.
In many cases, these other examples reintroduce similar lessons to those seen in account abstraction. But some new lessons have also been learned:
Helps avoid centralization risk in other areas of the stack. Often, keeping the base protocol simple and uncomplicated pushes complexity to the ecosystem outside the protocol. From a Unix philosophy perspective, this is good. However, sometimes, the risk of an ecosystem outside the protocol is that it may become centralized, often (but not only) due to high fixed costs. Solidification can sometimes reduce de facto centralization.
Too much solidification can overly increase the trust and governance burden of the protocol. This was the theme of an earlier article on “Don’t Overload Ethereum Consensus”: if solidifying a particular feature weakens the trust model and makes Ethereum as a whole more “subjective”, it weakens Ethereum’s trusted neutrality. In these cases, it is better to keep that particular feature as a mechanism on top of Ethereum, rather than trying to bring it into Ethereum itself. Here, the crypto memory pool is the best example of something that may be too difficult to solidify, at least until/unless cryptographic technology improvements are delayed.
Too much solidification overcomplicates the protocol. Protocol complexity is a systemic risk, and adding too many features to the protocol increases that risk. Precompiles are the best example of this.
Solidification can be counterproductive in the long run because user needs are unpredictable. A feature that many people believe is important and will be used by many users may not actually be used much in practice.
In addition, the cases of liquidity staking, ZK-EVM, and precompilation show a middle ground possibility: minimum viable solidification. Rather than solidifying the entire feature, it is better to solidify specific parts that solve the key challenges of implementing that feature without being too subjective or narrow. This includes:
Rather than solidifying the entire liquidity staking system, it is better to change the penalty rules to make trustless liquidity staking more feasible.
Rather than hardening more precompilation, it would be better to harden EVM-MAX and/or SIMD to make a wider class of operations easier to implement efficiently.
Instead of solidifying the entire concept of Rollups, we can simply solidify EVM verification.
We can expand the diagram from earlier in this article as follows:
Sometimes it might even make sense to de-harden something. Removing rarely used precompiles is one example. As a support for forward compatibility with existing users, the mechanism might actually be strikingly similar to the mechanism for removing precompiles: one of these is EIP-5003, which would allow EOAs to convert their accounts in-place to contracts with the same (or better) functionality.
Which features should be brought into the protocol and which should be left to other layers of the ecosystem is a complex trade-off, and one that we should expect to evolve over time as our understanding of user needs and our suite of available ideas and technologies improves.
