Flow was built from the ground up for mainstream-scale decentralized applications, and introduced a novel multirole architecture as well as Cadence, a resource-oriented programming language designed specifically for smart contracts. While Cadence provides powerful benefits to developers, its biggest drawbacks have been the inability to take advantage of the apps and tooling already in existence on the Ethereum blockchain –– until now.
This proposal introduces a path to full EVM equivalence on Flow, giving developers the complete flexibility to deploy any Ethereum dApp with no code changes to take full advantage of the functionality native to Flow. Trusted tools and protocols like Uniswap, Opensea, Metamask, Chainlink Oracle, Layerzero, AAVE, Curve, Remix, and Hardhat will be able to function on Flow out of the box, while developers will still be able to write Cadence smart contracts to extend and build on the Solidity smart contracts with full composability.
- EVM on Flow allows developers to benefit from the existing network effects and tooling in Solidity and EVM while taking advantage of Flow’s mainstream-focused features for onboarding and user experience as well as the unique capabilities of Cadence.
- An application developer can start by deploying a project using EVM, and their users can use EVM-native wallets.
- If that application developer wants to add new Cadence-specific features at some point, they can do so, and any users who want to switch to a Flow-native wallet can get access to those new features.
- But, critically, users who don’t value those features can continue to use the old functionality via their existing wallets. Provided the new features are purely additive, the two cohorts can still interact with each other seamlessly.
- Cadence code can interact with the EVM environment synchronously. This allows Cadence code to compose with EVM code powerfully and easily.
- EVM code and clients will be able to work with the EVM environment on Flow (via gateways) with no customization beyond setting a different RPC endpoint and chain-id. This is a standard practice amongst EVM chains and is well adopted and understood.
Cadence is a resource-oriented programming language designed specifically for smart contracts and digital assets, with security and safety guarantees that greatly simplify the development of secure smart contracts.
There are a truly remarkable number of features on Flow that are possible only because we undertook the effort of building up the Cadence execution environment up from scratch:
- Native, protocol-level account abstraction, including account linking
- Support for standard hardware secure enclaves (such as those found in iPhones and most Android devices)
- Scripted transactions, allowing a single tx to interact with any number of smart contracts, with custom logic between calls
- Resource-oriented programming, providing a dramatically improved security model and better user experience
- Transaction pre- and post-conditions that prevent transaction phishing and approval scams
- True ownership, where a user’s assets are stored and discoverable within their own account
- Native multi-sig support
- Transactions with multiple signers, allowing direct, secure asset-swaps without any smart contracts (among other interesting use cases for one-off trustless cooperative action)
- Prevent entire classes of bugs via a strong static type system, design by contract, and capability-based access control.
- Permissionless composability enabled by Attachments feature (Live on Testnet).
- Secure source of on-chain randomness for Cadence smart contracts.
The proposed implementation of EVM on Flow will allow for developers to easily compose the above features and capabilities of Cadence on top of existing tooling and smart contracts developed in Solidity for Ethereum and other EVM-compatible networks.
When bringing EVM to a new network like Flow, simply targeting compatibility is insufficient. Existing smart contracts need to work directly on the new platform without any concerns about API compatibility, unexpected side-effects, or alternate security concerns. Client-side tools, like wallets and indexers, need to function without adjustments. Bridges and client libraries want to depend on the same set of assumptions that hold true on Ethereum. If it’s not 100%, bug-for-bug compatible, you’re in danger of missing the point of providing support for EVM in the first place.
This is why approaches like cross-compiling, for example, are not suitable. Unless we have EVM equivalence, there is not a good enough path for any of the supporting tools, SDKs, and infrastructure from Ethereum that would be helpful for real-world dapps on Flow to move over. Dapp developers will necessarily find themselves making non-trivial changes to ensure their Solidity code works as expected, while also needing to rewrite major parts of their supporting infrastructure and client code.
An introduction to this problem is available in this article from the Optimism team in 2021. It draws a distinction between “compatibility” (the ability to interpret EVM bytecode) and “equivalence” (the ability for EVM tools, code, and infrastructure to work with minimal changes). The critical paragraph:
EVM compatibility is not the same as EVM equivalence, and settling for mere compatibility means that you are forced to modify, or even completely reimplement, lower-level code that Ethereum’s supporting infrastructure also relies on. If [new platforms] want to surf Ethereum’s wave of infrastructural network effects, they must become EVM equivalent.
Here we outline an EVM execution environment that lives inside the Cadence execution environment on Flow. Developers can pass it signed Ethereum transactions and they run exactly as they would on Ethereum itself, with the same signature checking and encoding requirements. To deploy EVM smart contracts, developers construct a transaction exactly as they would to publish a smart contract on Ethereum. Inside the EVM environment, gas costs are charged as defined in the Ethereum Yellow Paper. The security model is identical, the account model is identical, the state and event Merkle proofs are identical.
Because this environment is running inside a secure smart contract, it is as verifiable and secure as any other smart contract on Flow. It doesn’t need to run any kind of complicated consensus algorithm or worry about mempools and networking. It inherits all of that functionality from the rest of Flow.
As discussed above, EVM equivalence is about much more than byte-code compatibility. To provide full EVM equivalence, we will create “gateway” software that exposes the standard EVM API (“Ethereum JSON-RPC”) to respond to requests from EVM-aware clients. Transactions submitted to these gateways will be wrapped in a Cadence transaction and submitted to the EVM instance on Flow without the EVM client software needing to be aware of the interim step. (Recall that these are cryptographically signed transactions that will have their signatures verified by Flow. The trust assumptions and security model of gateways exactly match those of a standard Ethereum node.)
State and event queries (with proofs) can be provided by gateways as well. They would use a Cadence script to fetch results from within the EVM environment and then respond to the clients exactly matching the Ethereum RPC requirements.
It’s important to acknowledge how localized and non-disruptive these changes are to the rest of Flow. We have a single new smart contract to run the EVM runtime environment and a new kind of client (the gateways) that interact with the existing Flow Access API without modification. Integrating the EVM runtime environment into the Flow execution environment and the gateway is a lot of work, but we can leverage a whole lot of existing open source code from the Ethereum community to handle the hardest parts. We can provide full EVM equivalence on Flow without disrupting any existing applications or tools built with Cadence.
(NOTE: EVM on Flow will be implemented by embedding one of the many existing EVM Golang implementations into the Flow node implementation instead of undertaking a new EVM implementation in Cadence. This saves a great deal of effort and minimizes implementation risk, while ensuring maximum performance. Critically, the interface to using EVM on Flow will be exposed via a Cadence smart contract, and the required changes are still highly localized and minimally disruptive to the rest of the project.)
Our implementation of EVM on Flow allows for Cadence smart contracts to securely extend EVM functionality –– full composability.
Composability between the EVM and Cadence environments on Flow will be facilitated via “bridged accounts”. A bridged account is a resource object that lives in Cadence that controls a bridged address inside the EVM environment. From the perspective of the EVM, the bridged address looks like a smart contract. (It is not an EOA – “externally owned address” – that has an associated key.) It will work properly with any EVM smart contract that doesn’t make assumptions that only hold true of EOAs.
EVM actions submitted through a bridged account don’t require a signature, nor do they pay EVM gas fees or require a nonce. Instead, the Cadence code holding the bridged account resource can just pass in a target address and “calldata” (the way in which Ethereum transactions indicate what smart contract function they want to call) and EVM on Flow will execute that transaction using the associated bridged address as
msg.sender to the initial recipient).
Bridged addresses can hold any asset that can be represented within the standard EVM, including ERC-20 and ERC-721. They have a native token balance, and can call methods on any smart contract, including transferring any assets to any other address (including other bridged addresses).
Because a bridged account is a native Cadence resource object, it can be held by end-users in their account to be controlled directly by their Flow wallet software, or they can be held by Cadence smart contracts to allow them to extend or make use of any functionality deployed inside the EVM.
EVM code can trigger functionality in Cadence by making a function call on the bridged address of any bridged account. The bridged account in Cadence can register a callback that can process the message from EVM and take the appropriate action.
Making function calls from Cadence into EVM is powerful, but the full power of this approach is only unlocked by introducing asset bridging functionality into the EVM runtime environment.
Anyone can request that a token should be bridged between the two environments. From Cadence, anyone can pass in a Cadence type object that implements the FungibleToken or NonFungibleToken specification. The EVM runtime environment will check that the type matches the required interface, and ensure that there isn’t already a bridging contract in place inside the EVM. Once these prerequisites are satisfied, a new ERC-20 or ERC-721 contract will be deployed inside the EVM to act as a bridged asset for the source type.
Similarly, anyone can pass in a smart contract address inside the EVM which implements ERC-20 or ERC-721, and if it implements the required interface and isn’t already bridged, a new Cadence type implementing the appropriate FT or NFT interface will be deployed in Cadence to act as a bridged asset.
Once the bridged asset contract is set up (which only happens one time), anyone holding any of these assets can bridge them between the environments. The bridged account object in Cadence will have deposit() and withdraw() methods that will allow simple, immediate bridging of assets between the two environments. The user doesn’t even need to know which environment originally defined the base asset, the way they interact with ERC-721 defined inside the EVM will be exactly the same as how they interact with the native NonFungibleToken types on Flow.
There are some tricky aspects of this approach that we’ll gloss over for now. How do we create an incentive for gatekeepers to operate? How do we map EVM gas fees into Cadence execution effort? How does the emulator account manage its minimum balance to account for its storage? What constitutes the “current block” and blockhash for EVM code?
For some of these questions we have solid answers, others preliminary ideas, and – of course – more questions are surely going to come up as we continue building. Please weigh in below as we work through these details.
By using EVM on Flow, developers can leverage the current network effects of the Ethereum Virtual Machine while easily building beyond its limitations and capitalizing on Flow’s benefits:
Mainstream user experience with native protocol-level account abstraction, account linking and mobile-first experiences with support for standard hardware secure enclaves.
Leverage modular blockchain architecture that solves the common scalability & and security issues with a protocol-native data availability stack, proposer builder separation, separation of compute, consensus & and settlement – all without sharding. This provides superior security, developer experience, improves composability and interoperability of the entire ecosystem.
Unlock the freedom to build powerful smart contracts leveraging Cadence’s resource-oriented paradigm, providing an improved security model and enabling increasingly complex smart contract functionality for developers.
Rather than asking Web3 developers to reinvent the wheel, EVM on Flow paves a path to tap into the unique features of Flow and Cadence while building on and extending the full network effects of the Ethereum ecosystem.
Flow is uniquely suited to support multiple execution environments. Because Flow is built on a separation of execution and consensus, it’s easy to imagine a parallel set of Execution Nodes and Verification Nodes that process EVM transactions while leveraging the same Collection Nodes and Consensus Nodes that sequence both the Cadence and EVM transactions. One consensus layer supporting two execution layers.
This EVM execution environment could be EVM equivalent, from the RPC API to the account model. Yes, it would still be the case that EVM transactions wouldn’t get access to the powerful features of Flow outlined in the first section, but the Flow EVM would be no less capable than other EVM chains while gaining the scalability, data availability, and efficiency advantages of the Flow multi-node architecture.
The problem with this approach brings us right back to the reason we designed Flow in 2018. Two execution environments sharing a consensus layer, but executed in parallel, is a version of “sharding”. And it was our deep concerns with how sharding limits composability that prompted us to design Flow in the first place.
We believe that the most important superpower of smart contract blockchains is composability: The ability of smart contracts to safely and permissionlessly build on other smart contracts to extend their functionality. This is somewhat possible in a sharded execution environment, but it’s incredibly difficult, highly limited, and significantly error prone.
Yes, parallel VMs would allow for trustless bridging between the two computing environments, but it wouldn’t allow for direct interactions between smart contracts. One of the coolest examples of Flow’s composability advantage is that it is trivial to make a trade on a DEX and use the resulting tokens to buy an NFT all within a single, atomic transaction. Very powerful! But, in a version of Flow with dual execution environments, if the DEX was in Cadence and the NFT marketplace was in EVM, you couldn’t do this atomically. You’d have to swap your tokens in Cadence, bridge the tokens to Solidity, and then use the EVM to buy the NFT. (Four total transactions!).
That simple example might not seem so bad, but if you imagine more complex forms of composability with more smart contracts involved, things start to get really difficult.
Even worse is that there is no good transition story for users and app developers. Let’s say you have a great Solidity application and you deploy it in Flow’s EVM shard. As time goes on, you might decide you want to add some Cadence features, like account linking or native mobile support. There is no gradual transition, you’d need to recreate your entire application (including off-chain elements) to work with Cadence. All of your users would need to bridge their assets and switch/upgrade their wallets at the same time. And even this painful switch would only be possible if all other smart contracts or infrastructure you depend on has already been ported to the Cadence environment.