What are Stateful Precompiles?
Learn about Stateful Precompiles in Avalanche L1 EVM.
When building the MD5 and Calculator precompiles, we emphasized their behavior. We focused on building precompiles that developers could call in Solidity to perform some algorithm and then simply return a result.
However, one aspect that we have yet to explore is the statefulness of precompiles. Simply put, precompiles can store data which is persistent. To understand how this is possible, recall the interface that our precompile needed to implement:
We also examined all parameters except for the AccessibleState
parameter. As the name suggests, this parameter lets us access the blockchain's state. Looking at the interface of AccessibleState
, we have the following:
Looking closer, we see that AccessibleState
gives us access to StateDB, which is used to store the state of the EVM. However, as we will see throughout this section, AccessibleState
also gives us access to other useful parameters, such as BlockContext
and snow.Context
.
StateDB
The parameter we will use the most when it comes to stateful precompiles, StateDB, is a key-value mapping that maps:
- Key: a tuple consisting of an address and the storage key of the type Hash
- Value: any data encoded in a Hash, also called a word in the EVM
A Hash in go-ethereum is a 32-byte array. In this context, we do not refer to hashing in the cryptographic sense. Rather, "hashing a value" means encoding it to a hash, a 32-byte array usually represented in hexadecimal digits in Ethereum.
As you can see in the interface of the StateDB, the two functions for writing to and reading from the EVM state all work with the Hash type.
- The function GetState takes an address and a Hash (the storage key) as inputs and returns the Hash stored at that slot.
- The function SetState takes an address, a Hash (the storage key), and another Hash (data to be stored) as inputs.
We can also see that the StateDB interface allows us to read and write account balances of the native token (via GetBalance/SetBalance), check whether an account exists (via Exist), and some other methods.
BlockContext
BlockContext
provides info about the current block. In particular, we can get the current block number and the current block timestamp. Below is the interface of BlockContext
:
An example of how we could leverage BlockContext
in precompiles: building a voting precompile that only accepts votes within a certain time frame.
snow.Context
snow.Context
gives us info regarding the environment of the precompile. In particular, snow.Context
tells us about the state of the network the precompile is hosted on. Below is the interface of snow.Context
: