Precompile Registry on Moonbeam¶
Introduction¶
The Precompile Registry serves as a single source of truth for the available precompiles on Moonbeam. The Precompile Registry can be used to determine if an address corresponds to a precompile and whether or not a precompile is active or deprecated. This is particularly useful when there are upstream changes within the Substrate and Polkadot ecosystems that result in backward-incompatible changes to precompiles. Developers can design an exit strategy to ensure their dApp recovers gracefully in these scenarios.
The Precompile Registry also serves an additional purpose, as it allows any user to set "dummy code" (0x60006000fd
) for precompiles, which makes precompiles callable from Solidity. This is necessary as precompiles on Moonbeam, by default, don't have bytecode. The "dummy code" can bypass checks in Solidity that ensure contract bytecode exists and is non-empty.
The Registry Precompile is located at the following address:
0x0000000000000000000000000000000000000815
0x0000000000000000000000000000000000000815
0x0000000000000000000000000000000000000815
Note
There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the Security Considerations page for more information.
The Precompile Registry Solidity Interface¶
PrecompileRegistry.sol
is a Solidity interface that allows developers to interact with the precompile's methods.
PrecompileRegistry.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.3;
/// @dev The Precompile Registry contract's address.
address constant PRECOMPILE_REGISTRY_ADDRESS = 0x0000000000000000000000000000000000000815;
/// @dev The Precompile Registry contract's instance.
PrecompileRegistry constant PRECOMPILE_REGISTRY_CONTRACT = PrecompileRegistry(PRECOMPILE_REGISTRY_ADDRESS);
/// @author The Moonbeam Team
/// @title Precompile Registry
/// @dev Interface to the set of available precompiles.
interface PrecompileRegistry {
/// @dev Query if the given address is a precompile. Note that deactivated precompiles
/// are still considered precompiles and will return `true`.
/// @param a: Address to query
/// @return output Is this address a precompile?
/// @custom:selector 446b450e
function isPrecompile(address a) external view returns (bool);
/// @dev Query if the given address is an active precompile. Will return false if the
/// address is not a precompile or if this precompile is deactivated.
/// @param a: Address to query
/// @return output Is this address an active precompile?
/// @custom:selector 6f5e23cf
function isActivePrecompile(address a) external view returns (bool);
/// @dev Update the account code of a precompile address.
/// As precompiles are implemented inside the Runtime, they don't have a bytecode, and
/// their account code is empty by default. However in Solidity calling a function of a
/// contract often automatically adds a check that the contract bytecode is non-empty.
/// For that reason a dummy code (0x60006000fd) can be inserted at the precompile address
/// to pass that check. This function allows any user to insert that code to precompile address
/// if they need it.
/// @param a: Address of the precompile.
/// @custom:selector 48ceb1b4
function updateAccountCode(address a) external;
}
- isPrecompile(address a) - returns a bool indicating whether a given address is a precompile or not. Returns
true
for active and deprecated precompiles - isActivePrecompile(address a) - returns a bool indicating whether a given address is an active precompile or not. Returns
false
if a precompile has been deprecated - updateAccountCode(address a) - updates a given precompile's bytecode with dummy code (
0x60006000fd
) given the address of the precompile. Precompiles, by default, don't have bytecode associated with them. This function can be used to add dummy bytecode to bypass requirements in Solidity that check if a contract's bytecode is not empty before its functions can be called
Interact with the Precompile Registry Solidity Interface¶
The following sections will cover how to interact with the Registry Precompile from Remix and Ethereum libraries, such as Ethers.js, Web3.js, and Web3.py.
The examples in this guide will be on Moonbase Alpha. To test out the examples in this guide on Moonbeam or Moonriver, you will need to have your own endpoint and API key, which you can get from one of the supported Endpoint Providers.
Use Remix to Interact with the Precompile Registry¶
To quickly get started with Remix, the Precompile Registry contract has been loaded from GitHub. You can also create a new file in Remix and manually paste in the contents of the PrecompileRegistry.sol
contract.
Then you can take the following steps to compile, deploy, and interact with the Precompile Registry:
-
From the Compile tab, click on Compile PrecompileRegistry.sol to compile the contract. A green checkmark will appear upon successfully compiling the contract
-
From the Deploy and run transactions tab, you can load the Precompile Registry using its address:
- Make sure Injected Provider - Metamask is selected in the ENVIRONMENT drop down and you've connected MetaMask to Moonbase Alpha
- Ensure PrecompileRegistry is selected in the CONTRACT dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the Precompile in the At Address field
- Provide the address of the Precompile Registry for Moonbase Alpha:
0x0000000000000000000000000000000000000815
and click At Address - The Precompile Registry will appear in the list of Deployed Contracts
-
You can interact with any of the precompile's methods. Under Deployed Contracts, expand the Precompile Registry to view the list of methods. For example, you can use the isPrecompile function to check if an address is a precompile
Use Ethereuem Libraries to Interact with the Precompile Registry¶
To interact with the Precompile Registry's Solidity interface with an Ethereum library, you'll need the Precompile Registry's ABI.
Precompile Registry ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "a",
"type": "address"
}
],
"name": "isActivePrecompile",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "a",
"type": "address"
}
],
"name": "isPrecompile",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "a",
"type": "address"
}
],
"name": "updateAccountCode",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
Once you have the ABI, you can interact with the Registry using the Ethereum library of your choice. Generally speaking, you'll take the following steps:
- Create a provider
- Create a contract instance of the Precompile Registry
- Interact with the Precompile Registry's functions
Remember
The following snippets are for demo purposes only. Never store your private keys in a JavaScript or Python file.
import { ethers } from 'ethers'; // Import Ethers library
import ABI from './precompileRegistryABI.js'; // Import Precompile Registry ABI
const privateKey = 'INSERT_PRIVATE_KEY';
// Create Ethers provider and signer
const provider = new ethers.JsonRpcProvider(
'https://rpc.api.moonbase.moonbeam.network'
);
const signer = new ethers.Wallet(privateKey, provider);
// Create interface for the Precompile Registry
const precompileRegistry = new ethers.Contract(
'0x0000000000000000000000000000000000000815',
ABI,
signer
);
// Interact with the Precompile Registry
const isActivePrecompile = async () => {
const proxyPrecompile = '0x000000000000000000000000000000000000080b';
// Check if the Proxy Precompile is a precompile
const isPrecompile = await precompileRegistry.isPrecompile(proxyPrecompile);
// Should return 'Address is a precompile: true'
console.log(`Address is a precompile: ${isPrecompile}`);
// Check if the Proxy Precompile is an active precompile
const isActivePrecompile = await precompileRegistry.isActivePrecompile(
proxyPrecompile
);
// Should return 'Address is an active precompile: true'
console.log(`Address is an active precompile: ${isActivePrecompile}`);
};
isActivePrecompile();
import { Web3 } from 'web3';
import ABI from './precompileRegistryABI.js'; // Import Precompile Registry ABI
const privateKey = 'INSERT_PRIVATE_KEY';
// Create provider
const web3 = new Web3('https://rpc.api.moonbase.moonbeam.network');
// Create interface for the Precompile Registry
const precompileRegistry = new web3.eth.Contract(
ABI,
'0x0000000000000000000000000000000000000815',
{ from: web3.eth.accounts.privateKeyToAccount(privateKey).address }
);
// Interact with the Precompile Registry
const isActivePrecompile = async () => {
const proxyPrecompile = '0x000000000000000000000000000000000000080b';
// Check if the Proxy Precompile is a precompile
const isPrecompile = await precompileRegistry.methods.isPrecompile(
proxyPrecompile
).call();
// Should return 'Address is a precompile: true'
console.log(`Address is a precompile: ${isPrecompile}`);
// Check if the Proxy Precompile is an active precompile
const isActivePrecompile =
await precompileRegistry.methods.isActivePrecompile(proxyPrecompile).call();
// Should return 'Address is an active precompile: true'
console.log(`Address is a precompile: ${isActivePrecompile}`);
};
isActivePrecompile();
from web3 import Web3
abi = "INSERT_PRECOMPILE_REGISTRY_ABI" # Paste or import the Precompile Registry ABI
private_key = "INSERT_PRIVATE_KEY"
# Create provider
web3 = Web3(Web3.HTTPProvider("https://rpc.api.moonbase.moonbeam.network"))
# Create interface for the Precompile Registry
precompile_registry = web3.eth.contract(
address="0x0000000000000000000000000000000000000815", abi=abi
)
# Interact with the Precompile Registry
def is_active_precompile():
proxy_precompile = "0x000000000000000000000000000000000000080b"
# Check if the Proxy Precompile is a precompile
is_precompile = precompile_registry.functions.isPrecompile(proxy_precompile).call()
# Should return 'Address is a precompile: true'
print("Address is a precompile: ", is_precompile)
# Check if the Proxy Precompile is an active precompile
is_active_precompile = precompile_registry.functions.isActivePrecompile(
proxy_precompile
).call()
# Should return 'Address is an active precompile: true'
print("Address is an active precompile: ", is_active_precompile)
is_active_precompile()
| Created: May 3, 2023