logoAcademy

What are Precompiles?

Learn about Precompiled Smart Contracts in EVM.

Precompiled contracts are a way to execute code written in the low-level coding language Go from the EVM. Go is much faster and more efficient than Solidity.

Overview

If you are familiar with Python programming, you might be familiar with a similar concept. Many Python functions and libraries are written in the programming language C, since it is much more efficient than Python. Python developers can import these precompile modules and call these functions just as if they were written in Python. The only difference is that the modules are faster and more efficient.

Precompiles can be called from a Solidity smart contract in the same way, as if they were another contract written in Solidity. The EVM keeps a list of addresses reserved and mapped to the precompiles. When a smart contract calls a function of a contract with an address on that list, the EVM executes the precompile written in Go instead of the smart contract.

For instance, if we mapped the address 0x030...01 to the SHA256 precompile that hashes its input using the SHA256 Hash Function, we can call the Precompile like this:

// 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 are calling the precompile with the address we are defining the interface of our SHA256 precompile and calling it in our contract MyContract.

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

PrecompiledContract Interface

When implementing a precompile in the Avalanche L1-EVM, we have to implement the following function of the StatefulPrecompiledContract Interface 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)
}

Let's ignore what stateful means as well as the first parameter accessibleState for now. We will cover this in the later section Stateful Precompiles.

From the function specifying the logic of our precompile we have 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: How much gas was supplied to the call of the precompile
  • readOnly: A boolean flag to indicate if the interaction is only reading or modifying the state.

The precompile implementation has the return the following values:

  • ret: All return values encoded in a byte array
  • remainingGas: How much gas remains after the execution
  • err: If an error occurs, return it. Otherwise return nil

On this page