npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

sevm

v0.6.15

Published

A Symbolic Ethereum Virtual Machine (EVM) bytecode decompiler & analyzer library & CLI

Downloads

6,325

Readme

Symbolic EVM Bytecode Decompiler

splash preview

GitHub Workflow Status nycrc config on GitHub npm npm GitHub install size Demo

A Symbolic Ethereum Virtual Machine (EVM) interpreter and decompiler, along with several other utils for programmatically extracting information from bytecode.

[!NOTE] Forked from MrLuit/evm. For more info, see Detached Fork.

Features

:construction: Under heavy development. Feel free to open an issue if something is not right. :construction:

  • Lightweight with virtually no dependencies, ~60kb minified
  • Embedded functions and events signature database optional
  • Convert bytecode to opcodes
  • Extract events or functions information from bytecode
  • Extract the IPFS or swarm hash (when present) from bytecode using cbor-js
  • Check whether an opcode exists and is reachable within bytecode execution
  • Detect whether contracts are compliant to certain ERCs

Install

Install using your package manager or Browser's script tag

yarn

yarn add sevm

npm

npm install sevm

Browser

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/sevm.js"></script>

or if you're interested only in the CLI Tool, install globally in your system

npm install --global sevm

sevm supports both ESM import and Node's CommonJS require. Also it can be used in browsers, where all classes and functions can be found under the sevm global object.

ESM

import { Contract } from 'sevm';

// 00 opcode is STOP https://www.evm.codes/#00?fork=shanghai
const contract = new Contract('0x00');
console.log(contract.solidify());

CJS

const { Contract } = require('sevm');

// 00 opcode is STOP https://www.evm.codes/#00?fork=shanghai
const contract = new Contract('0x00');
console.log(contract.solidify());

script tag

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/sevm.js"></script>

<script>
  console.log('sevm exported symbols', sevm);

  // 00 opcode is STOP https://www.evm.codes/#00?fork=shanghai
  const contract = new sevm.Contract('0x00');
  document.getElementById('code').innerHTML = contract.solidify();
</script>

API    TypeDoc

Main Methods and Properties

Usage

These examples use the import syntax and ethers.js is used to fetch bytecode from public EVM-based networks.

Decode Bytecode into Opcodes

import { EtherscanProvider as Provider } from 'ethers';
import { Contract } from 'sevm';

// CryptoKitties Contract
// https://etherscan.io/address/0x06012c8cf97BEaD5deAe237070F9587f8E7A266d#code
const bytecode = await new Provider().getCode('0x06012c8cf97BEaD5deAe237070F9587f8E7A266d');
const contract = new Contract(bytecode);
const opcodes = contract.opcodes();
console.log(opcodes.map(opcode => opcode.format()));

Decompile a Contract

import { EtherscanProvider as Provider } from 'ethers';
import { Contract } from 'sevm';
import 'sevm/4bytedb';

// WETH Contract
// https://etherscan.io/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
const bytecode = await new Provider().getCode('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2');

const contract = new Contract(bytecode).patchdb(); // Lookup for 4byte matches
console.log(contract.solidify()); //Decompile bytecode to Solidity

You can use the contract.yul() method to decompile the bytecode into Yul-like format.

Detect Functions, Events and ERC compliance

import { EtherscanProvider as Provider } from 'ethers';
import { Contract } from 'sevm';
import 'sevm/4bytedb';

// CryptoKitties Contract
// https://etherscan.io/address/0x06012c8cf97BEaD5deAe237070F9587f8E7A266d#code
const bytecode = await new Provider().getCode('0x06012c8cf97BEaD5deAe237070F9587f8E7A266d');

const contract = new Contract(bytecode).patchdb();
console.log('functions', contract.getFunctions());
console.log('events', contract.getEvents());
console.log('isERC 165', contract.isERC('ERC165')); /* Detect whether contract is ERC165-compliant */

Extract Contract Metadata

import { JsonRpcProvider } from 'ethers';
import { Contract } from 'sevm';

// USDC Token Proxy on Avalanche Testnet
// https://testnet.snowtrace.io/address/0x5425890298aed601595a70AB815c96711a31Bc65#code
const provider = new JsonRpcProvider('https://api.avax-test.network/ext/bc/C/rpc');
const bytecode = await provider.getCode('0x5425890298aed601595a70AB815c96711a31Bc65');

const contract = new Contract(bytecode);
console.log(contract.metadata);

Advanced Usage

Hooks

import { EVM, London } from 'sevm';

// contract Test {
//     event Deposit(uint256);
//     fallback () external payable {
//         emit Deposit(tx.gasprice);
//     }
// }
const bytecode = '608060408190524581527f4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e3842690602090a16040805145815290517f4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e384269181900360200190a1604080513a815290517f4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e384269181900360200190a100';

const evm = new EVM(bytecode, new class extends London {
    /** @override */
    GASPRICE = (/** @type {import('sevm').Operand} */ state) => {
        super.GASPRICE(state);
        console.log('top', state.stack.top);
    };
}());

evm.start();

Advanced Hooks

A contract might embed another contract it creates and deploys. Using hooks you can extract the embedded contract.

import { Contract, type Opcode, Shanghai, type State } from 'sevm';
import type { DataCopy, Create } from 'sevm/ast';
import 'sevm/4bytedb';

// contract Token {
//     event Deposit(uint256 value);
//     fallback() external payable {
//         emit Deposit(3);
//     }
// }
// contract Test {
//     fallback() external payable {
//         new Token();
//     }
// }
const bytecode = '6080604052604051600e90602d565b604051809103906000f0801580156029573d6000803e3d6000fd5b5050005b60918061003a8339019056fe6080604052348015600f57600080fd5b50607480601d6000396000f3fe60806040527f4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e3842660036040518082815260200191505060405180910390a100fea2646970667358221220f03e9c56a5b70c1ba8fadf7d234cbca48d388951bfc7d402f0c92e4cb2afcd9f64736f6c63430007060033a264697066735822122053e5ffa19b83ac364f3adf6836d3758c98ae850b0fc9f5059124bf85d12c8d7264736f6c63430007060033';

let constructorContract: Contract, tokenContract: Contract;
const testContract = new Contract(bytecode, new class extends Shanghai {
    override CREATE = (state: State) => {
        super.CREATE(state);
        const bytecode = (state.stack.top as Create).bytecode!;

        constructorContract = new Contract(bytecode, new class extends Shanghai {
            override CODECOPY = (state: State, _opcode: Opcode, evm: { bytecode: Uint8Array }) => {
                const dest = state.stack.top?.eval();
                super.CODECOPY(state, _opcode, evm);

                if (dest?.isVal()) {
                    const m = state.memory[Number(dest.val)] as DataCopy;
                    tokenContract = new Contract(m.bytecode!);
                }
            };
        }());
    };
}());

console.log('// Test contract -- factory');
console.log(testContract.solidify());
console.log('// Token contract -- constructor');
console.log(constructorContract!.solidify());
console.log('// Token contract -- deployed bytecode');
console.log(tokenContract!.patchdb().solidify());

CLI Tool

sevm comes with a CLI tool to examine bytecode from the command line.

The CLI tool is completely independent from the rest of the library. This means that if you intend to use sevm as part of your application as a bundle, you won't include the CLI tool nor any of its dependencies.

sevm --help

$ sevm --help
sevm <cmd> <contract>

Commands:
  sevm metadata <contract>   Shows the Metadata of the contract[1]
  sevm abi <contract>        Shows the ABI of the contract[2]
  sevm selectors <contract>  Shows the function selectors of the contract[3]
  sevm dis <contract>        Disassemble the bytecode into Opcodes
  sevm cfg <contract>        Writes the cfg of the selected function in `dot` fo
                             rmat into standard output
  sevm sol <contract>        Decompile the contract into Solidity-like source
  sevm yul <contract>        Decompile the contract into Yul-like source[4]
  sevm config                Shows cache path used to store downloaded bytecode

Options:
  --version  Show version number                                       [boolean]
  --color    Displays with colors, use `--no-color` to deactivate colors
                                                       [boolean] [default: true]
  --patch    Patches the Contract public functions and events with signatures fr
             om https://openchain.xyz/, use `--no-patch` to skip patching
                                                       [boolean] [default: true]
  --help     Show help                                                 [boolean]

Examples:
  sevm abi 0x00000000000C2E074eC69A0dFb299  shows the ABI of the ENS registry co
  7BA6C7d2e1e                               ntract
  sevm decompile 0x00000000000C2E074eC69A0  decompiles the ENS registry contract
  dFb2997BA6C7d2e1e

[1] See https://docs.soliditylang.org/en/latest/metadata.html for more informati
on regarding Metadata generated by the Solidity compiler.
[2] See https://docs.soliditylang.org/en/latest/abi-spec.html#abi-json for more
information regarding the ABI specification.
[3] See https://docs.soliditylang.org/en/latest/abi-spec.html#function-selector
for more information regarding Function Selectors
[4] See https://docs.soliditylang.org/en/latest/yul.html for more information re
garding Yul.

dis

$ sevm dis --help
sevm dis <contract>

Disassemble the bytecode into Opcodes

Positionals:
  contract  path or address where to locate the bytecode of the contract
                                                             [string] [required]

Options:
  --version     Show version number                                    [boolean]
  --color       Displays with colors, use `--no-color` to deactivate colors
                                                       [boolean] [default: true]
  --patch       Patches the Contract public functions and events with signatures
                 from https://openchain.xyz/, use `--no-patch` to skip patching
                                                       [boolean] [default: true]
  --help        Show help                                              [boolean]
  --with-stack  Include the current stack next to each decoded opcode
  --with-trace  Include the trace of staments at the end of each basic block

EVM Bytecode Function and Event Signature Hashes

sevm comes with a collection of Ethereum function and event signature hashes. They are available through the sevm/4byte module. It looks up in the signature and events database for matching hashes.

When a matching function or event is found in a Contract, it patches the function or event with the corresponding signature.

The sevm/4byte is completely independent from the main module. It is not loaded by default in the main module. It needs to be imported explicitly. This allows the user to create a bundle without the lookup database provided thet want to use another solution.

Contributing

.dataset

This folder contains a dataset of contract bytecodes deployed in a public network.

It is attached as a Git submodule and it is used in test/dataset.test.ts, which loads every contract bytecode and runs the Solidity and Yul decompilation. This ensures that the analysis works on real contracts and that is does not enter an infinite loop while interpreting a bytecode cycle.

examples

The examples folder contains code examples that showcase sevm features. These code examples are the ones embedded in this document.

To ensure these examples don't get outdated, test/examples.test.ts runs every example script in the examples folder to verify they are compiled and executed properly. Moreover, their output is recorded into test/__snapshots__/examples.snap.md and compared against in subsequent tests.

scripts

Contains utility scripts that automates the development process.

test

Tests run using Mocha and can be executed with

yarn test

Each top-level test name begins with :: so it is easier to filter out tests (using Mocha's -f/--fgrep or -g/--grep flags). For example, to run only tests for the step module use

yarn test -g ::step

__snapshots__

To ensure the test output does not change unexpectedly, we use an ad-hoc snapshot testing solution similar to Jest's. The snapshots are stored in test/__snapshots__.

To re-generate the snapshot artifacts in a test after an intentional implementation change, set the environment variable UPDATE_SNAPSHOTS=1. For example

UPDATE_SNAPSHOTS=1 yarn test -g ::mainnet

It is usually better to filter (-f/-g flags) which snapshot test cases get re-generated to avoid any unwanted unintentional updates.

When a test is creating or re-generating a snapshot, the indicator icon 📸 is appended to the test title. For example

        ✔ should match Solidity snapshot 📸

On the other hand, when a test is comparing against a previously created snapshot, the indicator icon 🎞️ is appended to the test title. For example

        ✔ should match Solidity snapshot 🎞️

4byte.test.ts

This test needs to be manually enabled because it depends on a network connection (to query the OpenChain API to look for method signatures).

Set the environment variable ENABLE_4BYTE_TEST=1 to enable this test. When executing it, you may want to run only this test, for example

ENABLE_4BYTE_TEST=1 yarn test -g ::4byte

dataset.test.ts

This test needs to be manually enabled because its take longer than the rest of test suite. It is not ergonomic to include it in the main test suite.

Set the environment variable ENABLE_DATASET_TEST=1 to enable this test. When executing it, you may want to run only this test, for example

ENABLE_DATASET_TEST=1 yarn test -g ::dataset

examples.test.ts

This test needs to be manually enabled because it depends on a network connection (to fetch code from a live network).

Set the environment variable ENABLE_EXAMPLES_TEST=1 to enable this test. When executing it, you may want to run only this test, for example

ENABLE_EXAMPLES_TEST=1 yarn test -g ::examples

types

This folder contains vendor types to refine or declare types of libraries. It is used either in tests or internally in the library, i.e., these types are not re-exported and hence not part of the public library API.

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.