EVM Bytecode Decompiler
A Symbolic Ethereum Virtual Machine (EVM) interpreter and decompiler, along with several other utils for programmatically extracting information from bytecode.
Forked from MrLuit/evm. For more info, see Detached Fork.
Install
Install using your package manager or Browser's script
tag
yarn
yarn add @acuarica/evm
npm
npm install @acuarica/evm
WIP
Browser<script src="https://cdn.jsdelivr.net/gh/acuarica/evm@f88b20a/lib/EVM.js"></script>
Features
- Convert bytecode to opcodes
- Read information like events or functions from either bytecode or TX data
- Extract the IPFS or swarm hash (when present) from bytecode
- Check whether an opcode exists and is reachable within bytecode execution
- Detect whether contracts are compliant to certain ERCs
Main Methods and Properties
-
bytecode
- Get raw bytecode (not really useful; same as input) -
metadata
- Get IPFS or Swarm hash (if present) for contract metadata -
evm.opcodes
- Returns opcodes including pc and pushData (if included) -
evm.jumpdests
- Get map of program counters from JUMPDEST opcodes -
getFunctions()
- Parse functions from their signatures in bytecode -
getEvents()
- Parse events from their signatures in bytecode -
containsOpcode(opcode)
- Check whether an opcode exists and is reachable within bytecode -
decompile()
- Decompile bytecode into readable Solidity-like pseudocode -
isERC165()
- Detect whether contract is ERC165-compliant
Usage
Converting Bytecode to Opcodes
Node.js
const { EVM } = require('evm');
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider('https://api.mycryptoapi.com/eth'));
web3.eth.getCode('0x06012c8cf97BEaD5deAe237070F9587f8E7A266d').then(code => {
/* CryptoKitties contract */
const evm = new EVM(code);
console.log(evm.getOpcodes()); /* Get opcodes */
});
Browser
const { EVM } = window.EVM_Utils;
const web3 = new Web3(window.web3.currentProvider);
web3.eth.getCode('0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359', function (err, code) {
/* DAI contract */ if (err) throw err;
const evm = new EVM(code);
console.log(evm.getOpcodes()); /* Get opcodes */
});
Decompiling a Contract
Node.js
const { EVM } = require('evm');
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider('https://api.mycryptoapi.com/eth'));
web3.eth.getCode('0x06012c8cf97BEaD5deAe237070F9587f8E7A266d').then(code => {
/* CryptoKitties contract */
const evm = new EVM(code);
console.log(evm.getFunctions()); /* Get functions */
console.log(evm.getEvents()); /* Get events */
console.log(evm.decompile()); /* Decompile bytecode */
console.log(
evm.containsOpcode('SELFDESTRUCT')
); /* Check whether contract contains a SELFDESTRUCT */
console.log(evm.isERC165()); /* Detect whether contract is ERC165-compliant */
});
Browser
const { EVM } = window.EVM;
const web3 = new Web3(window.web3.currentProvider);
web3.eth.getCode('0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359', function (err, code) {
/* DAI contract */ if (err) throw err;
const evm = new EVM(code);
console.log(evm.getFunctions()); /* Get functions */
console.log(evm.getEvents()); /* Get events */
console.log(evm.decompile()); /* Decompile bytecode */
console.log(
evm.containsOpcode('SELFDESTRUCT')
); /* Check whether contract contains a SELFDESTRUCT */
console.log(evm.isERC165()); /* Detect whether contract is ERC165-compliant */
});
Extracting data from transaction WIP
Node.js
const { Transaction } = require('evm');
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider('https://api.mycryptoapi.com/eth'));
web3.eth
.getTransaction('0xd20a8d888a3f29471ea41ea77cc2d95ccd79ade1eaad059e83524e72b9adf962')
.then(transactionData => {
const transaction = new Transaction();
transaction.setInput(transactionData.input);
console.log(transaction.getFunction()); /* Get function */
});
Browser
const { Transaction } = window.EVM;
const web3 = new Web3(window.web3.currentProvider);
web3.eth.getTransaction(
'0xd20a8d888a3f29471ea41ea77cc2d95ccd79ade1eaad059e83524e72b9adf962',
function (err, transactionData) {
if (err) throw err;
const transaction = new Transaction();
transaction.setInput(transactionData.input);
console.log(transaction.getFunction()); /* Get function */
}
);
Detached Fork
This GitHub repo was originally a fork of https://github.com/MrLuit/evm. It served as a great starting point for this project. The fact that it is lightweight and written in TypeScript, make it ideal for embedding in other applications.
However, as we started to support and decompile newer contracts, we realize it was quite outdated. Besides not being able to process newer smart contracts, for some, the bytecode analysis algorithm did not terminate. That's the reason we forked that repo.
We did a major overhaul of the codebase, adding new features, refactoring the whole project and adding both testing and documentation.
As we added changes, we realized it did not make sense to keep it a forked repo.
Moreover, when sending new PRs, the default base
repo is the upstream repo, which is not what we want in our case.
This behavior is both error prone and annoying.
That's why, as of Apr 17, 2023, this project is no longer a fork of MrLuit/evm.