logoAcademy

What are Precompiles?

Learn about Precompiled Smart Contracts in EVM.

Precompiled contracts allow the execution of code written in the low-level programming language Go from the EVM, which is significantly faster and more efficient than Solidity.

Overview

If you're familiar with Python, you might recognize a similar concept where many Python functions and libraries are implemented in C for efficiency. Python developers can import these precompiled modules and call functions as if they were written in Python. The main difference is that the modules execute faster and more efficiently.

Precompiles can be called from a Solidity smart contract just like any other contract. The EVM maintains a list of reserved addresses mapped to precompiles. When a smart contract calls a function of a contract at one of these addresses, the EVM executes the precompile written in Go instead of the Solidity contract.

For example, if we map the address 0x030...01 to the SHA256 precompile that hashes its input using the SHA256 hash function, we can call the precompile as follows:

// SPDX-License-Identifier: MIT
 
pragma solidity >=0.8.0;
 
interface ISHA256 {
    // Computes the SHA256 hash of value
    function hashWithSHA256(string memory value) external view returns(bytes32 hash);
}
 
contract MyContract {  
    ISHA256 mySHA256Precompile = ISHA256(0x0300000000000000000000000000000000000001);
    
    function doSomething() public {
        bytes32 hash = mySHA256Precompile.hashWithSHA256("test");
    }
}

In the code above, we call the precompile using the defined interface for our SHA256 precompile within MyContract.

Note that there is no implementation of the precompile in Solidity itself. This will only work if the precompile is implemented in Go and registered at the address 0x030...01.

PrecompiledContract Interface

When implementing a precompile in the Avalanche L1-EVM, the following function of the StatefulPrecompiledContract interface must be implemented in Go:

// StatefulPrecompiledContract is the interface for executing a precompiled contract
type StatefulPrecompiledContract interface {
    // Run executes the precompiled contract.
    Run(accessibleState AccessibleState, 
        caller common.Address, 
        addr common.Address, 
        input []byte, 
        suppliedGas uint64, 
        readOnly bool) 
        (ret []byte, remainingGas uint64, err error)
}

We will cover the meaning of "stateful" and the first parameter accessibleState later. For now, let's focus on the function that specifies the logic of our precompile, which provides access to the following data:

  • caller: The address of the account that called the precompile.
  • addr: The address of the precompile being called.
  • input: All inputs encoded in a byte array.
  • suppliedGas: The amount of gas supplied for the precompile call.
  • readOnly: A boolean flag indicating if the interaction is only reading or modifying the state.

The precompile implementation must return the following values:

  • ret: All return values encoded in a byte array.
  • remainingGas: The amount of gas remaining after execution.
  • err: If an error occurs, return it. Otherwise, return nil.

On this page