You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In Geth, precompiles are a set of stateless contracts. But unlike Solidity code, their logic isn't in the smart contract code itself—they are part of the Geth client. You can think of a precompile as an interface that can execute Go code that resides within the Geth client.
How Precompiles Are Implemented in Geth
The logic for precompiles in Geth is located in the core/vm/contracts.go file.
All precompile implementations must follow the PrecompiledContract interface.
typePrecompiledContractinterface {
RequiredGas(input []byte) uint64// RequiredPrice calculates the contract gas useRun(input []byte) ([]byte, error) // Run runs the precompiled contract
}
Precompiles for the Cancun update are registered here:
// PrecompiledContractsCancun contains the default set of pre-compiled Ethereum// contracts used in the Cancun release.varPrecompiledContractsCancun=PrecompiledContracts{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
common.BytesToAddress([]byte{0xa}): &kzgPointEvaluation{},
}
Implement a Simple Add Precompile
In this tutorial, we will implement a simple precompile that adds two numbers:
interfaceAddPrecompile {
function add(uinta, uintb) externalviewreturns (uintresult);
}
Step 1: Create a new contracts_add.go file
Create a contracts_add.go file under the core/vm folder. This file will contain the logic for our precompile.
/*`contracts_add.go` file implements a simple add precompileinterface AddPrecompile { function add(uint a, uint b) external view returns (uint result);}*/package vm
// addContractAddr defines the precompile contract address for `add` precompilevaraddContractAddr=common.HexToAddress("0x0100000000000000000000000000000000000001")
// add implements the PrecompiledContract interfacetypeaddstruct{}
// RequiredGas returns the gas needed to execute the precompile function.//// It implements the PrecompiledContract.RequiredGas method.func (p*add) RequiredGas(input []byte) uint64 {
returnuint64(1024)
}
// Run contains the logic of the `add` precompile//// It implements the PrecompiledContract.Run method.func (p*add) Run(input []byte) ([]byte, error) {
// todoreturnnil, nil
}
Step 2: Unpack Input and Pack Output
In the Run method, the input is ABI-encoded, where the first 4 bytes represent the function selector. We need to unpack the input to get the uint a and uint b. The output should also be ABI-encoded.
varaddABI=`[ { "inputs": [ { "internalType": "uint256", "name": "a", "type": "uint256" }, { "internalType": "uint256", "name": "b", "type": "uint256" } ], "name": "add", "outputs": [ { "internalType": "uint256", "name": "result", "type": "uint256" } ], "stateMutability": "view", "type": "function" } ]`// parseABI parses the abijson string and returns the parsed abi object.funcparseABI(abiJSONstring) abi.ABI {
parsed, err:=abi.JSON(strings.NewReader(abiJSON))
iferr!=nil {
panic(err)
}
returnparsed
}
// unpackAddInput unpacks the abi encoded input.funcunpackAddInput(input []byte) (*big.Int, *big.Int, error) {
parsedABI:=parseABI(addABI)
functionInput:=input[4:] // exclude function selectorres, err:=parsedABI.Methods["add"].Inputs.Unpack(functionInput)
iferr!=nil {
returnnil, nil, err
}
iflen(res) !=2 {
returnnil, nil, fmt.Errorf("invalid input for function add")
}
// convert to the actual function input typea:=abi.ConvertType(res[0], new(big.Int)).(*big.Int)
b:=abi.ConvertType(res[1], new(big.Int)).(*big.Int)
returna, b, nil
}
// packAddOutput pack the function result into output []bytefuncpackAddOutput(result*big.Int) ([]byte, error) {
parsedABI:=parseABI(addABI)
returnparsedABI.Methods["add"].Outputs.Pack(result)
}
Step 3: Implement the Core Logic in the Run Method
Now let’s complete the Run method with the core addition logic.
// Run contains the logic of the `add` precompile//// It implements the PrecompiledContract.Run method.func (p*add) Run(input []byte) ([]byte, error) {
iflen(input) <4 {
returnnil, fmt.Errorf("missing function selector")
}
// unpack inputa, b, err:=unpackAddInput(input)
iferr!=nil {
returnnil, err
}
// calculate result of a + bresult:=new(big.Int).Add(a, b)
// pack result into outputoutput, err:=packAddOutput(result)
iferr!=nil {
returnnil, err
}
returnoutput, nil
}
Step 4: Register the Precompile
Next, register the new precompile along with the others in the Cancun release. Open core/vm/contracts.go and add your precompile:
// PrecompiledContractsCancun contains the default set of pre-compiled Ethereum// contracts used in the Cancun release.varPrecompiledContractsCancun=map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
common.BytesToAddress([]byte{0xa}): &kzgPointEvaluation{},
// register the `add`` precompileaddContractAddr: &add{},
}
Test the precomile
Step 1: Run Geth in Development Mode
To test the modified Geth with the new precompile, you can run Geth in development mode by adding this command to the Makefile and running make devnet:
devnet:
go run ./cmd/geth/ --dev --http --http.addr "0.0.0.0" --http.port 8545 --http.api "personal,eth,net,web3,admin,debug" --allow-insecure-unlock console -cache.preimages=true --http.corsdomain "https://remix.ethereum.org"
You can also build the modified Geth with make geth and run: ./build/bin/geth --dev --http --http.addr "0.0.0.0" --http.port 8545 --http.api "personal,eth,net,web3,admin,debug" --allow-insecure-unlock console -cache.preimages=true --http.corsdomain "https://remix.ethereum.org"
Test the Precompile with Remix
Open Remix.
Connect to your local Geth network by selecting Custom - External Http Provider and using the default http://127.0.0.1:8545 endpoint.
Interact with the precompile at the address 0x0100000000000000000000000000000000000001 using the following Solidity interface:
interfaceAddPrecompile {
function add(uinta, uintb) externalviewreturns (uintresult);
}
Call the add function and test it!
The text was updated successfully, but these errors were encountered:
In this tutorial, we will walk through how to create your own precompile and add it to Geth.
You can check out the full code in this gist.
What Is a Precompile in Geth?
In Geth, precompiles are a set of stateless contracts. But unlike Solidity code, their logic isn't in the smart contract code itself—they are part of the Geth client. You can think of a precompile as an interface that can execute Go code that resides within the Geth client.
How Precompiles Are Implemented in Geth
The logic for precompiles in Geth is located in the
core/vm/contracts.go
file.All precompile implementations must follow the
PrecompiledContract
interface.Precompiles for the Cancun update are registered here:
Implement a Simple Add Precompile
In this tutorial, we will implement a simple precompile that adds two numbers:
Step 1: Create a new
contracts_add.go
fileCreate a
contracts_add.go
file under thecore/vm
folder. This file will contain the logic for our precompile.Step 2: Unpack Input and Pack Output
In the
Run
method, the input is ABI-encoded, where the first 4 bytes represent the function selector. We need to unpack the input to get the uint a and uint b. The output should also be ABI-encoded.Step 3: Implement the Core Logic in the Run Method
Now let’s complete the
Run
method with the core addition logic.Step 4: Register the Precompile
Next, register the new precompile along with the others in the Cancun release. Open
core/vm/contracts.go
and add your precompile:Test the precomile
Step 1: Run Geth in Development Mode
To test the modified Geth with the new precompile, you can run Geth in development mode by adding this command to the Makefile and running make devnet:
You can also build the modified Geth with
make geth
and run:./build/bin/geth --dev --http --http.addr "0.0.0.0" --http.port 8545 --http.api "personal,eth,net,web3,admin,debug" --allow-insecure-unlock console -cache.preimages=true --http.corsdomain "https://remix.ethereum.org"
Test the Precompile with Remix
http://127.0.0.1:8545
endpoint.0x0100000000000000000000000000000000000001
using the following Solidity interface:Call the add function and test it!

The text was updated successfully, but these errors were encountered: