WETH
Giáo trìnhExecution client

Precompiled Contracts

Các hợp đồng được biên dịch sẵn trong Ethereum

Precompiled contracts

Precompiled contracts are a set of special accounts, each containing a built-in function with a determined gas cost, often related to complex cryptographic computations. Currently, they are defined at addresses ranging from 0x01 to 0x0a.

Unlike contract accounts, precompiles are part of the Ethereum protocol and implemented by execution clients. This presents an interesting caveat - their EXTCODESIZE within EVM is 0. Nonetheless, they function like contract accounts with code when executed.

Precompiles are included in the initial "accessed_addresses" of a transaction as defined by EIP-2929 to save gas costs.

Precompiles vs. opcodes

Both precompiles and opcodes have the same goal of performing arbitrary computations. Following considerations are made when choosing between them:

  • Limited opcode space: The EVM opcode space of 1-byte is inherently constrained (256 opcodes, 0x00-0xFF). This necessitates judicious allocation for essential operations.
  • Efficiency: Precompiled contracts are natively executed outside the EVM, enabling efficient implementations for complex cryptographic computations that power many cross-chain interactions.

Quoting "A Prehistory of the Ethereum Protocol" by Vitalik:

The second was the idea of "precompiles", solving the problem of allowing complex cryptographic computations to be usable in the EVM without having to deal with EVM overhead. We had also gone through many more ambitious ideas about "native contracts", where if miners have an optimized implementation of some contracts they could "vote" the gasprice of those contracts down, so contracts that most miners could execute much more quickly would naturally have a lower gas price; however, all of these ideas were rejected because we could not come up with a cryptoeconomically safe way to implement such a thing. An attacker could always create a contract which executes some trapdoored cryptographic operation, distribute the trapdoor to themselves and their friends to allow them to execute this contract much faster, then vote the gasprice down and use this to DoS the network. Instead we opted for the much less ambitious approach of having a smaller number of precompiles that are simply specified in the protocol, for common operations such as hashes and signature schemes.

List of precompiles

AddressNameDescriptionSince
0x01ECRECOVERElliptic curve public key recoverFrontier
0x02SHA2-256SHA2 256-bit hash schemeFrontier
0x03RIPEMD-160RIPEMD 160-bit hash schemeFrontier
0x04IDENTITYIdentity functionFrontier
0x05MODEXPArbitrary precision modular exponentiationByzantium (EIP-198)
0x06ECADDElliptic curve additionByzantium (EIP-196)
0x07ECMULElliptic curve scalar multiplicationByzantium (EIP-196)
0x08ECPAIRINGElliptic curve pairing checkByzantium (EIP-197)
0x09BLAKE2BLAKE2 compression functionIstanbul (EIP-152)
0x0aKZG POINT EVALUATIONVerifies a KZG proofCancun (EIP-4844)

How it works

The beauty of precompiles lies in design of its interface, which is identical to external smart contract calls, allowing for a familiar interaction - from a developer's perspective, usage of precompile no different than an external call.

Gas costs for precompiles are directly tied to the input data – fixed inputs translate to fixed costs. To determine these costs, developers rely on a combination of reference implementations and benchmarks. Benchmarks typically measure execution time on specific hardware, while some, like MODEXP, define consumption directly in terms of gas usage per second. This meticulous approach aims to prevent denial-of-service attacks by ensuring predictable resource allocation.

Under the hood, client implementations leverage optimized libraries to execute precompiles. While this approach enhances efficiency, it introduces a potential security risk. If a bug is found within these libraries, it could disrupt the entire protocol layer. To mitigate this risk, rigorous testing is crucial (e.g. MODEXP test specs).

To prevent security vulnerabilities, precompiles are designed to avoid nested calls.

Calling precompiles

Like contract account, precompiles can be called using *CALL family of opcodes. The following assembly code example shows usage of SHA-256 precompile to hash the string "Hello":

// First place the parameters in memory
PUSH5 0x48656C6C6F // Hello in UTF-8
PUSH1 0
MSTORE

// Call SHA-256 precompile (0x02)
PUSH1 0x20 // retSize
PUSH1 0x20 // retOffset
PUSH1 5 // argsSize
PUSH1 0x1B // argsOffset
PUSH1 2 // address
PUSH4 0xFFFFFFFF // gas
STATICCALL

POP // Pop the result of the STATICCALL

// LOAD the result from memory to stack
PUSH1 0x20
MLOAD

which yields the hash:

185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969

▶️ Try it in the EVM playground!

Refer the wiki on EVM to understand how assembly code works.

Proposed precompiles

EIPs can introduce new precompiles as part of hard forks. There is general resistance in adding new precompiles as the testing surface and maintenance cost is high. To address this, a proposed approach involves prototyping precompiles on Layer 2 solutions first, and then integrating them into the mainnet only after they demonstrate stability and widespread adoption.

Following precompiles are currently proposed:

The introduction of new precompiles requires careful consideration of their network effects. A precompile with miscalculated gas cost could potentially cause denial of service by consuming more resources than anticipated. Additionally, a growing number of precompiles can lead to code bloat within the EVM clients, increasing the burden on validators.

The selection of cryptographic functions and their corresponding parameters for precompiles requires a thorough analysis to balance security and efficiency. These parameters are generally preset within the precompile logic as parametrizing these from user input could be a security concern. Moreover, optimizing security functions with a wide range of parameters is difficult for fast execution, which is the fundamental requirement of precompiles.

Removing precompiles

Discussions are ongoing regarding potential removal of precompiles that are outdated, underutilized, or hinder client software efficiency. The identity precompile (replaced by the MCOPY opcode), RIPEMD-160, and BLAKE functions are prime candidates for retirement.

However, instead of complete removal,these precompiles could be migrated to efficient smart contract implementations. This approach would ensure continued functionality but with a corresponding increase in gas costs.

Implementations

Research

A proposed approach called "progressive precompiles" aims to improve the deployment process. These precompiles would reside at deterministic CREATE2 addresses, allowing user contracts to interact with the same address regardless of whether the precompile is live on the mainnet or a specific L2. This approach ensures a smoother transition when native client precompiles become available.

Resources

On this page