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.
Ethereum precompiled contracts are “native” contracts at fixed addresses that perform complex operations more efficiently than EVM bytecode. The original set at addresses 0x01
–0x09
is defined in Appendix E of the Yellow Paper, and subsequent EIPs have added more:
- 0x01 ECRECOVER: ECDSA public‐key recovery
- 0x02 SHA256: 256-bit SHA-2 hash
- 0x03 RIPEMD160: 160-bit RIPEMD-160 hash
- 0x04 IDENTITY: returns the input unchanged
- 0x05 MODEXP: big-integer modular exponentiation (EIP-198)
- 0x06 ECADD: alt_bn128 point addition (EIP-196)
- 0x07 ECMUL: alt_bn128 scalar multiplication (EIP-196)
- 0x08 ECPAIRING: alt_bn128 pairing check (EIP-197)
- 0x09 BLAKE2F: BLAKE2 compression (EIP-152)
- 0x0A–0x0F BLS12-381 operations (point add/mul, pairing, field mapping) (EIP-2537)
- 0x14 KZG point evaluation (EIP-4844)
- Plus various utility/system precompiles (e.g.,
0xF8 epochSize
,0xFC fractionmulExp
,0xFD transfer
) (rareskills.io)
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
- 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 usebn.js
orffjavascript
for big-int and finite-field math. - BLS12-381 modules (
0a
–0f
) leverage either pure-JS implementations in@noble/bls12-381
or optional high-performance WASM viablst-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 inindex.ts
.
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).
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 …
}
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 }
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)
- Rust native code in each module (e.g.,
src/blake2.rs
,src/bn128.rs
,src/bls12_381/…
). - Uses
num-bigint
orarkworks
crates for big-integer and elliptic-curve arithmetic. - BLS12-381 implementations typically wrap either
blst
(via FFI) or the pure-Rustark-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.
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);
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 | 0a –0f , 10 –12 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) |
- 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)