Skip to main content

What Is a Precompile? Built-In EVM Contracts Explained

EVM precompiles are hardcoded contracts at fixed low addresses that run native client code — faster and cheaper than Solidity for cryptographic operations.

Written by Eco

What Is a Precompile?

A precompile is a contract built directly into the EVM client at a fixed low address. Unlike a regular smart contract, a precompile contains no EVM bytecode. The EVM client executes it as native code — Go, Rust, or C++ depending on the client — which makes it orders of magnitude faster and cheaper for operations that would be prohibitively expensive in Solidity.

Ethereum currently ships ten precompiles at addresses 0x01 through 0x0a. When any contract sends a call to one of those addresses, the EVM does not fetch bytecode from state. It routes the call to a native function registered for that slot. The result comes back as calldata, indistinguishable from a regular external call from the caller's perspective.

Precompiles exist because certain cryptographic primitives — elliptic curve operations, hash functions, modular exponentiation — require enormous amounts of computation. Implementing them in Solidity would cost millions of gas per call and make entire categories of cryptographic verification economically impossible onchain. Native execution brings those costs down by two to three orders of magnitude.

Learn more about how Ethereum executes code at the Ethereum Foundation EVM documentation.

How Precompiles Work

Precompiles sit in a reserved section of the EVM address space below 0x0a. Any call routed to those addresses bypasses the normal bytecode lookup. The EVM client checks whether the destination is a registered precompile, and if so, runs the corresponding native function directly. Gas is deducted, inputs are validated, and the output is returned as calldata — the calling contract sees a successful low-level call with a return value.

From Solidity, calling a precompile looks identical to calling any other contract:

(bool success, bytes memory result) = address(0x02).staticcall(abi.encode(data));

The key difference is what happens after the EVM routes that call. For a normal contract, the EVM loads bytecode from the state trie and begins interpreting opcodes. For a precompile, it runs a native function that was compiled into the client binary itself. There is no bytecode, no storage slot lookup, and no interpreter overhead.

Gas costs for precompiles are specified in the EIP that introduces them, not derived dynamically from opcode pricing. The ecRecover precompile at 0x01 costs a flat 3,000 gas. An equivalent Solidity implementation of ECDSA recovery would cost somewhere between 500,000 and 1,500,000 gas and would still be less secure because it would expose intermediate values. The ModExp precompile at 0x05 uses a formula tied to the size of the input parameters rather than a flat fee, reflecting the variable cost of modular exponentiation.

Precompile gas pricing has historically been contentious. Several precompiles were originally under-priced relative to their actual CPU cost, which made them vectors for denial-of-service attacks during the 2016 Shanghai attacks. EIP-150 and EIP-1108 repriced several of them. This history is why new precompile proposals now include detailed benchmarking data alongside the specification.

See the full EIP-1108 repricing rationale for how the community approached gas cost corrections.

Ethereum's Current Precompiles

Ethereum mainnet ships ten precompiles covering ECDSA recovery, three hash functions, modular arithmetic, elliptic curve operations on the BN254 curve, and the BLAKE2 compression function. Each was introduced by a specific EIP and serves a concrete cryptographic use case.

Address

Name

Function

Introduced

Primary Use Case

0x01

ecRecover

ECDSA signature recovery

Frontier

Recover signer address from a secp256k1 signature; used in every wallet and multisig

0x02

SHA256

SHA-2 256-bit hash

Frontier

Bitcoin bridge verification, IPFS CID validation, general-purpose hashing

0x03

RIPEMD160

RIPEMD-160 hash

Frontier

Bitcoin address derivation (RIPEMD160(SHA256(pubkey))); cross-chain bridging

0x04

Identity

Data copy (memcopy)

Frontier

Cheap memory copy in assembly; used as a utility by other contracts and compilers

0x05

ModExp

Modular exponentiation

EIP-198

RSA signature verification, Fermat primality tests, cryptographic accumulator schemes

0x06

ecAdd

BN254 curve point addition

EIP-196

ZK proof verification (Groth16, PLONK); point arithmetic for pairing-based cryptography

0x07

ecMul

BN254 scalar multiplication

EIP-196

ZK proof verification; computing public inputs for pairing checks

0x08

ecPairing

BN254 pairing check

EIP-197

Groth16 and PLONK final pairing check; the core of most onchain ZK verifiers

0x09

BLAKE2f

BLAKE2b F compression

EIP-152

Zcash interoperability; Filecoin proof verification; BLAKE2-family hashing

0x0a

KZG point evaluation

KZG polynomial commitment check

EIP-4844

Validating blob data availability proofs introduced by proto-danksharding

The 0x0a point evaluation precompile shipped with EIP-4844 (Cancun upgrade, March 2024) and is used by rollups to verify that blob data was correctly committed. It is the first precompile added specifically to support L2 scaling rather than general-purpose cryptography.

Full parameter specifications for all precompiles are in the Yellow Paper appendix and individual EIPs linked from the Ethereum EIPs index.

Why Precompiles Matter for Cryptography

The BN254 precompiles at 0x06, 0x07, and 0x08 are what make ZK proof verification economically viable onchain. A Groth16 verifier needs roughly three pairing operations, each of which involves hundreds of field multiplications over a 254-bit prime field. Without ecPairing, a single proof verification would cost tens of millions of gas and price out any practical application. With the precompile, it costs around 180,000 gas.

This matters directly for cryptographic proof systems like zkSync, Polygon zkEVM, and StarkNet, which post ZK validity proofs to Ethereum mainnet. The verifier contracts on L1 call ecPairing thousands of times per day. Without that precompile, L2 finality via ZK proofs would be economically impossible at any reasonable gas price.

ECDSA recovery via ecRecover is equally foundational. Every transaction signature check, every EIP-712 typed-data signature, and every multisig verification routes through this precompile. The alternative would be implementing ECDSA recovery in Solidity, which is both expensive and fragile — the secp256k1 field arithmetic alone requires dozens of mulmod and addmod operations per call.

Hash function precompiles matter for cross-chain work. Bitcoin uses SHA256 and RIPEMD160 for address derivation. Any contract that wants to verify a Bitcoin transaction or proof of reserve needs access to those hash functions at a cost that makes verification economically sensible. SHA256 at 0x02 costs 60 gas plus 12 gas per 32-byte word — compared to the keccak256 opcode at 30 gas per 32-byte word, it is slightly more expensive, but it runs the actual SHA-256 algorithm rather than Ethereum's native keccak variant.

The EIP-197 rationale explains in detail why the BN254 pairing check cannot be implemented efficiently without native execution.

RIP-7212: The secp256r1 Precompile

RIP-7212 adds a precompile for signature verification on the secp256r1 curve (also called P-256), targeting Ethereum L2s before eventual mainnet inclusion. P-256 is the curve used in Apple Secure Enclave, Android hardware keystores, passkeys, and WebAuthn. Supporting it natively onchain means users can sign blockchain transactions directly from device biometrics without a separate seed phrase or hardware wallet.

Ethereum's native ECDSA operations use secp256k1, a different elliptic curve chosen by Satoshi Nakamoto for Bitcoin and inherited by Ethereum. The two curves have different parameters, and ecRecover at 0x01 is hardcoded for secp256k1 only. A Solidity implementation of secp256r1 verification exists (the Daimo team published one), but it costs approximately 330,000 gas per verification. A native precompile brings that to around 3,450 gas — roughly the same cost as a simple ETH transfer.

At 3,450 gas, passkey-based wallet operations become practical. A user authenticates with Face ID or Windows Hello, the device signs a transaction using its hardware-backed P-256 key, and a contract verifier confirms the signature via the precompile in a single call. No seed phrase, no separate signing device, no browser extension required.

RIP-7212 was designed as a RIP (Rollup Improvement Proposal) rather than an EIP so that L2s could adopt it immediately without waiting for Ethereum mainnet governance. zkSync Era, Polygon zkEVM, Base, and other OP Stack chains have shipped or are shipping implementations. The expectation is that demonstrated adoption on L2s strengthens the case for eventual Ethereum mainnet inclusion via a standard EIP.

The canonical specification is at RIP-7212 on the Ethereum RIPs repository.

Custom L2 Precompiles

L2 networks are not constrained to Ethereum's precompile set. They can add custom precompiles at addresses outside the 0x01-0x0a range, or override the behavior of existing ones, as long as the changes are reflected in their sequencer and prover implementations. Several major L2s have shipped custom precompiles that expose capabilities specific to their architecture.

Arbitrum One exposes a suite of precompiles under the ArbSys contract at 0x0000000000000000000000000000000000000064. These give Solidity contracts access to Arbitrum-specific data: the L2 block number, the L1 block number the sequencer is currently incorporating, send-to-L1 message functionality, and cross-chain gas estimation. Applications that need to time-lock transactions against L1 finality use ArbSys.arbBlockNumber() to distinguish Arbitrum's fast L2 blocks from the slower L1 cadence.

zkSync Era includes a set of system contracts (a related pattern) plus several precompiles for its zkEVM that handle compression, proof hints, and elliptic curve operations on curves relevant to its proving system. The P256Verify precompile on zkSync is an implementation of RIP-7212 functionality deployed ahead of the standard finalization.

OP Stack chains can add custom precompiles via predeploys, which are contracts deployed at specific addresses in the genesis block. These are not technically precompiles in the EVM client sense — they have bytecode — but they serve a similar purpose for chain-specific functionality. The OP Stack L2ToL1MessagePasser at 0x4200000000000000000000000000000000000016 is an example of this pattern.

Arbitrum Stylus, which allows contracts written in Rust and C++ to run alongside the EVM, blurs the precompile boundary further. Stylus programs execute in a WASM runtime rather than the EVM interpreter, but they can be called from Solidity contracts using the same interface as a precompile call. High-performance cryptographic libraries that would be expensive in EVM bytecode can be deployed as Stylus contracts and called at near-native speeds.

The Arbitrum precompile reference is maintained at Arbitrum developer documentation.

Adding New Precompiles to Ethereum

Adding a precompile to Ethereum mainnet requires a full EIP, client implementation across all major EVM clients (go-ethereum, Nethermind, Besu, Erigon), benchmarking, security review, and inclusion in a hard fork. The bar is high because precompiles are permanent: once deployed, they cannot be removed without breaking backward compatibility. Changing their behavior is equally difficult because any smart contract that calls a precompile may depend on exact output encoding.

The evaluation criteria the Ethereum core developer community applies include: whether the operation is cryptographically fundamental (not just convenient), whether a Solidity library alternative would be prohibitively expensive in practice, whether the precompile can be priced accurately using available benchmarking methods, and whether it serves a broad enough set of use cases to justify the maintenance burden across all clients.

Several precompile proposals have been rejected or stalled on these grounds. A BLS12-381 precompile (EIP-2537) has been in discussion for years because BLS signatures are used in Ethereum's own consensus layer, but the final gas pricing has been difficult to pin down across heterogeneous client hardware. EIP-7212 for secp256r1 stalled at mainnet level partly because the L2 path offered faster deployment without requiring mainnet consensus.

The governance process creates a useful pressure valve: L2s can experiment with custom precompiles, prove demand and implementation soundness, and then bring a well-tested proposal to the mainnet EIP process. RIP-7212 is the first large-scale test of this pipeline. If L2 adoption is widespread and the gas cost models prove accurate, the path to mainnet inclusion becomes significantly shorter.

EIP-7701, a proposed native account abstraction precompile, represents the next major candidate. It would allow the validation logic for account abstraction to run as a precompile rather than a bytecode contract, reducing the cost of ERC-4337-style wallet operations. The proposal is in active discussion as of 2025.

The full EIP process for precompiles is documented at EIP-1 (EIP purpose and guidelines).

Frequently Asked Questions

What is the difference between a precompile and a smart contract?

A smart contract stores bytecode in the Ethereum state trie, and the EVM interprets that bytecode opcode by opcode when the contract is called. A precompile has no bytecode in state. The EVM client runs a native function written in Go, Rust, or C++ when the precompile address is called. Precompiles are faster and cheaper for computationally intensive operations, but they cannot be deployed permissionlessly. They require a hard fork to add or modify.

Can a precompile be called from any Solidity contract?

Yes. Any contract can call a precompile using a low-level call, staticcall, or delegatecall directed at the precompile's fixed address. Some precompiles, like ecRecover at 0x01, are also wrapped in Solidity global functions (the ecrecover() built-in). Others require direct assembly or a Solidity interface definition pointing at the fixed address. Gas is deducted normally, and the call can revert if inputs are malformed.

Why does Ethereum use BN254 instead of secp256r1 for ZK precompiles?

BN254 (also called alt-bn128) has efficient pairing operations, which are necessary for Groth16 and PLONK proof systems. secp256r1 is a signature curve without pairing support, so it serves a different purpose: ECDSA verification for passkeys and device authentication. ZK proof systems need pairing-friendly curves. Ethereum has both the BN254 precompiles for ZK verification and (on L2s via RIP-7212) the secp256r1 precompile for passkey signing.

Are precompile addresses the same on all EVM-compatible chains?

Ethereum's core precompiles (0x01 through 0x0a) are standard across most EVM-compatible chains that aim for full compatibility. However, chains can choose not to implement certain precompiles, add custom ones at higher addresses, or change gas costs. L2s frequently add custom precompiles above the standard range. When deploying contracts that call precompiles to a new chain, developers should verify both that the precompile exists and that it behaves identically to the Ethereum mainnet version.

What happens if you call a precompile address that does not exist?

On Ethereum, calling an address below 0x0a that is not a registered precompile — or calling a precompile with malformed inputs — returns a failure and consumes the gas sent with the call. The calling contract receives false from the low-level call return value and can handle the failure in its own logic. Addresses above 0x0a with no deployed contract behave as empty accounts: the call succeeds and returns empty bytes, which can cause subtle bugs if the caller does not check return data length.

Related Reading

Did this answer your question?