I need to disassemble bytecode into opcodes across different EVM versions, to make it agnostic to the EVM version used during compilation?
As the second best option - is there a disassembler, that allows you to specify the EVM version to use? I did plenty of research online but haven't found anything like this. I'm currently using evmdasm: https://github.com/ethereum/evmdasm
But it was not updated for a long time, so, as an example of the problem - with the release of Shanghai, and the introduction of PUSH0
, for contracts, compiled with Shanghai EVM version I have opcode UNKNOWN_0x5f
(basically unrecognized opcode 0x5f), while same operation in contract compiled using Paris version, will look like PUSH1 0x00
.
Unification example:
Pushing 0, to stack in contract, compiled, using paris
evm version, will look like PUSH1 0x00
.
If you compile the same contract, using shanghai
evm version, it'll look like PUSH0
.
What I want is for the disassembler to make those opcodes look the same.
In this specific case it is easily achievable by doing, something like:
decompiled_code.replace('PUSH0', 'PUSH1 0x00')
to cast to paris implementation.
In other words:
decompile(x_shanghai_compiled) == decompile(x_paris_deployed) == decompile(x_other_evm_compiled)
For anyone interested. I found an existing package, that allows you to select evm version to use during disassembling:
https://github.com/crytic/pyevmasm
This package also contains a nice changelog of EVM versions in their tests, which you can use to make unifications, in places it is possible :)