Skip to content

Instantly share code, notes, and snippets.

@roninjin10
Created June 14, 2025 20:01
Show Gist options
  • Save roninjin10/f9719805b23b1e2d6f5f320d5a07198c to your computer and use it in GitHub Desktop.
Save roninjin10/f9719805b23b1e2d6f5f320d5a07198c to your computer and use it in GitHub Desktop.
Revm and ethereumjs browser friendly precompiles

Below is a detailed breakdown of how Ethereum’s built-in precompiled contracts are handled in two major EVM implementations—EthereumJS (TypeScript/JavaScript) and Revm (Rust)—including where the code lives, what libraries it leverages, how it’s compiled (or not) to WebAssembly, and illustrative code snippets.


1. Ethereum Precompile Specification

Ethereum precompiled contracts are “native” contracts at fixed addresses that perform complex operations more efficiently than EVM bytecode. The original set at addresses 0x010x09 is defined in Appendix E of the Yellow Paper, and subsequent EIPs have added more:

  1. 0x01 ECRECOVER: ECDSA public‐key recovery
  2. 0x02 SHA256: 256-bit SHA-2 hash
  3. 0x03 RIPEMD160: 160-bit RIPEMD-160 hash
  4. 0x04 IDENTITY: returns the input unchanged
  5. 0x05 MODEXP: big-integer modular exponentiation (EIP-198)
  6. 0x06 ECADD: alt_bn128 point addition (EIP-196)
  7. 0x07 ECMUL: alt_bn128 scalar multiplication (EIP-196)
  8. 0x08 ECPAIRING: alt_bn128 pairing check (EIP-197)
  9. 0x09 BLAKE2F: BLAKE2 compression (EIP-152)
  10. 0x0A–0x0F BLS12-381 operations (point add/mul, pairing, field mapping) (EIP-2537)
  11. 0x14 KZG point evaluation (EIP-4844)
  12. Plus various utility/system precompiles (e.g., 0xF8 epochSize, 0xFC fractionmulExp, 0xFD transfer) (rareskills.io)

2. EthereumJS (TypeScript/JavaScript)

2.1. Location & Modules

In the @ethereumjs/evm (formerly monorepo’s packages/evm) ecosystem, precompile implementations live under the src/precompiles/ folder. A typical list (from a representative CDN snapshot) is:

01-ecrecover.ts  
02-sha256.ts  
03-ripemd160.ts  
04-identity.ts  
05-modexp.ts  
06-ecadd.ts  
07-ecmul.ts  
08-ecpairing.ts  
09-blake2f.ts  
0a-bls12-g1add.ts  
0b-bls12-g1mul.ts  
0c-bls12-g1multiexp.ts  
0d-bls12-g2add.ts  
0e-bls12-g2mul.ts  
0f-bls12-g2multiexp.ts  
10-bls12-pairing.ts  
11-bls12-map-fp-to-g1.ts  
12-bls12-map-fp2-to-g2.ts  
14-kzg-point-evaluation.ts  
f8-epochsize.ts  
fc-fractionmulexp.ts  
fd-transfer.ts  
index.ts  
types.ts  

(cdn.jsdelivr.net)

2.2. Libraries & Origins

  • ECDSA, Hashes, BLAKE2 operations use the ethereum-cryptography library (which itself wraps audited pure-JS implementations of SHA-2, RIPEMD-160, secp256k1, BLAKE2) (github.com).
  • ModExp and BN254 (ecadd, ecmul, ecpairing) are written in TypeScript, originally ported from Geth’s Go code and often use bn.js or ffjavascript for big-int and finite-field math.
  • BLS12-381 modules (0a0f) leverage either pure-JS implementations in @noble/bls12-381 or optional high-performance WASM via blst-wasm, depending on build flags.
  • KZG (EIP-4844) initially used a WASM-based KZG library but, as of the v4.0.0 EVM release, was switched to a pure JS solution (e.g., [micro-eth-signer’s KZG impl]) to simplify packaging (github.com).
  • Utility precompiles (epochSize, fractionmulExp, transfer) are simple TS functions hardcoded in index.ts.

2.3. WASM Compilation

EthereumJS’s precompiles are authored in TypeScript/JavaScript and run directly on Node.js or in the browser; they are not separately compiled to WASM. The only WASM usage comes via optional libraries (e.g., blst-wasm for BLS or Wasm-based KZG), loaded at runtime through ESM imports. Developers can also inject custom WASM primitives via @ethereumjs/common’s Custom Cryptography Primitives API if they desire hardware-accelerated versions (npmjs.com, npmjs.com).

2.4. Invocation in the VM

EthereumJS’s interpreter maintains a map of address → precompile function and dispatches calls accordingly. A simplified snippet from index.ts might look like:

// index.ts (pseudo-code) :contentReference[oaicite:5]{index=5}
import { ECRECOVER } from './01-ecrecover'
import { SHA256 }    from './02-sha256'
// … etc …
export const precompiles: Map<number, PrecompileFn> = new Map([
  [0x01, ECRECOVER],
  [0x02, SHA256],
  // … 
  [0x14, KZG_POINT_EVAL],
  [0xF8, EPOCH_SIZE],
])

During execution:

const fn = precompiles.get(toAddress)
if (fn) {
  const { output, gasUsed } = fn(input, gasLimit)
  // … apply output & deduct gas …
}

3. Revm (Rust)

3.1. Crate & Modules

Revm splits its precompiles into a dedicated crate, declared in its Cargo.toml as:

[dependencies]
precompile = { path = "crates/precompile", package = "revm-precompile", version = "19.0.0", default-features = false }

(github.com)

Within revm-precompile, the modules correspond one-to-one with EIPs:

blake2            – BLAKE2F (EIP-152)
bn128             – alt_bn128 (EIP-196,197)
hash              – SHA256 & RIPEMD160
identity          – IDENTITY
modexp            – MOD_EXP (EIP-198; repriced EIP-2565)
secp256k1         – ECRECOVER
secp256r1         – RIP-7212 secp256r1
kzg_point_evaluation – EIP-4844
bls12_381         – BLS12-381 (EIP-2537)
interface         – shared `PrecompileFn` types
utilities         – padding/ABI helpers

(reth.rs)

3.2. Implementation & Libraries

  • Rust native code in each module (e.g., src/blake2.rs, src/bn128.rs, src/bls12_381/…).
  • Uses num-bigint or arkworks crates for big-integer and elliptic-curve arithmetic.
  • BLS12-381 implementations typically wrap either blst (via FFI) or the pure-Rust ark-bls12-381.
  • KZG leverages the same underlying BLS12-381 point-evaluation code with specialized API.
  • All code is compiled by rustc; to target WebAssembly, you simply build with --target wasm32-unknown-unknown, producing a WASM module that includes the precompiles too.

3.3. Invocation in the EVM

Revm’s core maps addresses to functions via its PrecompileProvider. Internally:

// invocation example (pseudo-code) :contentReference[oaicite:8]{index=8}
use revm_precompile::blake2::run as blake2_run;
let (output, used) = blake2_run(input_bytes, gas_limit);
// then apply output & gasUsed back into the EVM context

Similarly for BN128:

use revm_precompile::bn128::ecadd::run as bn128_add;
let (res, gas) = bn128_add(input, gas_limit);

4. Summary Comparison

Precompile EthereumJS (TS) Revm (Rust)
ECRECOVER 01-ecrecover.ts using ethereum-cryptography (cdn.jsdelivr.net, github.com) revm_precompile::secp256k1::run (Rust, num-bigint) (reth.rs)
SHA256/RIP160 02-sha256.ts / 03-ripemd160.ts revm_precompile::hash::{sha256_run, ripemd160_run}
IDENTITY 04-identity.ts (returns unchanged input) revm_precompile::identity::run
MODEXP 05-modexp.ts (BigInt powmod) revm_precompile::modexp::run
BN128 06-ecadd.ts, 07-ecmul.ts, 08-ecpairing.ts revm_precompile::bn128::{ecadd, ecmul, pairing}
BLAKE2F 09-blake2f.ts revm_precompile::blake2::run
BLS12-381 0a0f, 1012 modules via @noble/bls12-381 or blst-wasm revm_precompile::bls12_381 modules using blst/arkworks
KZG Eval 14-kzg-point-evaluation.ts (micro-eth KZG) (github.com) revm_precompile::kzg_point_evaluation::run
System f8-epochsize.ts, fc-fractionmulexp.ts, fd-transfer.ts Not present (handled separately in EVM logic)

Further Reading & Sources

  • EVM Yellow Paper, Appendix E (precompile spec) (rareskills.io)
  • EthereumJS monorepo (precompile list via CDN) (cdn.jsdelivr.net)
  • JS-Ethereum-Cryptography library (TS crypto primitives) (github.com)
  • EthereumJS/common (WASM primitive integration) (npmjs.com, npmjs.com)
  • @ethereumjs/evm Changelog (KZG switch to JS) (github.com)
  • Revm Precompile Crate (modules list) (reth.rs)
  • Revm Cargo.toml (precompile crate path) (github.com)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment