First of all, I do want to congrats the Polkadot community and especially Parity for the significant progress in the Polkadot Hub plan. A major part of this plan is the new Ethereum compatibility layer revive
/ pallet-revive
, running Ethereum-alike contracts on PolkaVM.
With the recent discussions on the issues of Ethereum and especially Ethereum compatibility, I do want to again provide my perspective on this. I want to first establish two important premises, and then I’ll explain in more detail what I mean by the title that “almost compatibility” is unsafe.
Premises
Audit and testing are the most significant cost of Web3 development
The part that takes most time in Web3 development is not the coding itself, but the significant amount of audit and testing that follows it to make sure things are safe. It may take only a week or two to write an EVM engine, but it takes a year or two before one is confident to use it in production for Ethereum mainnet. Polkadot makes things “upgradable”. For many parachain launches, “sudo” is also available. But this does not change the situation much, because an adversary can always act faster than you can react.
Safety is important. Millions of dollars of real values are at stake.
EVM is subtle
EVM is the Javascript of Web3. It’s not a well-designed language, but it’s used everywhere. Its stack-based design may be simple, but its complexity in terms of the many ways that you can write unsafe things should not be underestimated.
The important message I want to convey here is that its unsafety is usually really subtle, especially if one tries to make changes to the specification of EVM. You may already have a million “happy paths”, but it only takes one “sad path” for a disaster to happen.
As an exercise for the reader and as an example for what I mean above, take a look at EIP-1283, and try to figure it out yourself why the specification is unsafe, and why eventually EIP-2200 was instead deployed. This is only gas changes. Issues like this happen all the time in Ethereum history.
Full compatibility, almost compatibility, some compatibility
When dealing with Ethereum compatibility, there are three approaches
you can take:
- Full compatibility: replicate everything in EVM, make sure things always run the same. All code run.
- Almost compatibility: try to replicate everything in EVM, make subtle changes when it’s not possible. Most code run.
- Some compatibility: replicate EVM “in spirit”, but generally require developers to rewrite their contracts. Off-chain dapps can be re-used by RPC compatibility layer.
Polkadot’s pallet-evm
, Solana’s Neom EVM are examples of the “full compatibility” approach. revive
is an example of the “almost compatibility” approach. solang
is an example of the “some compatibility” approach.
Audits and testings are the most significant cost, but “almost compatibility” means it must start again from scratch
If everything is the same (“full compatibility”), then past audits and testings can generally be reused. However, because EVM is subtle, “most things are the same” is no better than “some things are the same”. The audits will have to be re-done again. The significant difference in gas cost schema, the changed semantics of CALL
and CREATE
, and the new design of precompiles, mean that there are a few more attack vectors opened (while a few old attack vectors closed).
Of course, the first implications that every reader can get from the above, is that “full compatibility” has an advantage on this over “almost compatibility” and “some compatibility”. But the more important message I want to convey here is that “almost compatibility” has no advantage over “some compatibility”. If audits and testings are the most significant cost, then for both cases this cost will have to be paid again in full. “Almost compatibility” may help a few new developers get up to speed quicker and may get us a few more hackathons, but it does not help with real-world production-ready deployments.
EVM is subtle, therefore some “forced” rewrite is necessary
Because of the many ways that EVM can fail and all the subtle details in the EVM specification, in the case of “almost compatibility” and “some compatibility”, you want to force the smart contract developers to pay attention. If not everything is the same (not “full compatibility”), allowing developers to deploy contracts unchanged is generally not a good idea. A contract may work perfectly fine on Ethereum, but it may be another multisig hack on Polkadot, and if that happens, core devs will be blamed (because it’ll be benchmarked against Ethereum). It’s therefore important to “force” the developer to pay some attention to properly “adopt” their contract for Polkadot.
The sweet spot is to have something that is of familiar syntax, but also different enough that nothing should directly compile. The smart contract itself is usually a tiny part of the overall dapps, and with a familiar syntax, a rewrite is not hard to do (and as discussed above, the audit and testing has to be re-done, anyway). “Almost compatibility” has no advantage over “some compatibility”, and one could argue that it’s rather worse because it encourages bad practices (blindly deploy) and makes things unsafe.
Ethereum is a moving target, and thus the maintenance burden of “almost compatibility” is particularly high
You may or may not like the way Ethereum AllCoreDevs governance is structured, and you may think things are moving slow, but hard fork improvements are still deployed every year. New Solidity/YUL versions generally follow Ethereum upgrades because it’s developed for it. The maintenance burden of “almost compatibility” is thus particularly high.
For “full compatibility”, we can generally take the code already written for Ethereum. For “some compatibility”, we are on our own schedule and because we don’t expect things on Ethereum to directly compile, we can choose to take whatever features we like or not. However, for “almost compatibility”, we can only choose one of the two hard choices – either we don’t follow Ethereum upgrades, in this case “almost compatibility” will gradually become “some compatibility” and new Solidity contracts will not compile on Polkadot any more, or we follow Ethereum upgrades, in this case we must try to adopt our existing changes. If this touches anything we have already changed (gas schemas, CALL/CREATE
, etc), the adoption may become challenging.
Either “full compatibility” or “some compatibilty” is better than “almost compatibility”
In conclusion, “full compatibility” or “some compatibility” is generally better choices than “almost compatibility”.
- “Almost compatibility” still requires full re-auditing and re-testing, and therefore it has no advantage over “some compatibility” in this aspect.
- “Almost compatibility” is unsafe because it encourages blind deployment. On the other hand, the on-chain code is generally a small part of the dapp codebase that a re-write may always be preferable. Therefore it may be a worse choice over “some compatibility” in this aspect.
- “Almost compatibility” has a higher maintenance burden because Ethereum is a moving target and direct code reuse is no longer possible.