Reference
Using Precompiles
Practical guide to using precompiles in EVM contracts
Overview
Precompiles allow EVM contracts to interact directly with native chain modules. Precompiles are special contract addresses that execute native Go code instead of EVM bytecode.
All Standard EVM Precompiles
MANTRA Chain supports all standard EVM precompiles that are available on Ethereum and other EVM-compatible chains. These precompiles work identically to how they work on Ethereum, Base, Polygon, Arbitrum, and other EVM chains.
Standard Ethereum Precompiles
All of the following standard EVM precompiles are available and fully functional:
- ECRecover (0x01) - Elliptic curve signature recovery
- SHA256 (0x02) - SHA-256 hash function
- RIPEMD160 (0x03) - RIPEMD-160 hash function
- Identity (0x04) - Data copying function
- ModExp (0x05) - Modular exponentiation
- BN256Add (0x06) - Elliptic curve addition on the BN256 curve
- BN256Mul (0x07) - Elliptic curve scalar multiplication on the BN256 curve
- BN256Pairing (0x08) - Bilinear pairing on the BN256 curve
- Bls12-381 (0x09-0x0F) - BLS12-381 curve operations
These precompiles behave exactly as they do on Ethereum and other EVM chains, ensuring full compatibility with existing EVM contracts and libraries.
Additional Cosmos SDK Precompiles
In addition to all standard EVM precompiles, NVNM Chain exposes additional stateful precompiles that let developers interact with selected Cosmos SDK modules and NVNM-specific functionality from Solidity.
The EVM communicates with the Cosmos side of the chain through these precompiles - native Go code that directly interacts with module state (for example, the Bank module).
Active NVNM Precompiles
The following 5 stateful precompiles are currently active on NVNM Chain:
| Precompile | Address | What it does |
|---|---|---|
| ICS20 (IBC Transfer) | 0x0000000000000000000000000000000000000802 | Initiate IBC token transfers from EVM contracts |
| Bank | 0x0000000000000000000000000000000000000804 | Send native tokens and query balances |
| Governance | 0x0000000000000000000000000000000000000805 | Submit and vote on governance proposals |
| Slashing | 0x0000000000000000000000000000000000000806 | Query slashing-related state from the EVM |
| Anchoring | 0x0000000000000000000000000000000000000A00 | Create and manage on-chain anchoring records |
Note
Staking (0x800) and distribution (0x801) precompiles are not available on NVNM Chain. Those operations remain on the MANTRA L1 provider chain.
ERC20 Precompiles
Each TokenFactory token has its own ERC20 precompile address, allowing:
- Control token balance: Manage token balances from EVM
- Transfer tokens: Transfer TokenFactory tokens
- Use tokens in EVM contracts: Seamless integration
This enables TokenFactory tokens (created via Cosmos SDK) to be used directly in EVM contracts without wrapping.
Precompile Addresses
The following precompile addresses are fixed on NVNM Chain and can be used directly in your Solidity contracts:
// NVNM Stateful Precompile Addresses
address constant ICS20_PRECOMPILE = 0x0000000000000000000000000000000000000802;
address constant BANK_PRECOMPILE = 0x0000000000000000000000000000000000000804;
address constant GOVERNANCE_PRECOMPILE = 0x0000000000000000000000000000000000000805;
address constant SLASHING_PRECOMPILE = 0x0000000000000000000000000000000000000806;
address constant ANCHORING_PRECOMPILE = 0x0000000000000000000000000000000000000A00;Note
TokenFactory ERC20 precompile addresses are deterministic based on the token denom. See the TokenFactory Denoms page for available tokens.
Using Precompiles
Basic Pattern
Precompiles are called like regular contract calls:
// Call a precompile
IPrecompileInterface(precompileAddress).functionName(params);Note
If your application needs staking or reward-distribution flows, call those on the MANTRA L1 provider chain rather than through an NVNM precompile.
Example: Bank Precompile
interface IBankPrecompile {
function send(
address to,
string memory denom,
uint256 amount
) external returns (bool);
function balanceOf(
address account,
string memory denom
) external view returns (uint256);
}
contract PaymentContract {
address constant BANK_PRECOMPILE = 0x0000000000000000000000000000000000000804;
function sendNativeToken(
address to,
string memory denom,
uint256 amount
) external {
IBankPrecompile(BANK_PRECOMPILE).send(to, denom, amount);
}
function getBalance(
address account,
string memory denom
) external view returns (uint256) {
return IBankPrecompile(BANK_PRECOMPILE).balanceOf(account, denom);
}
}Example: Governance Precompile
interface IGovernancePrecompile {
function vote(
uint256 proposalId,
uint8 option
) external returns (bool);
function createProposal(
string memory title,
string memory description,
// ... other proposal parameters
) external returns (uint256);
}
contract GovernanceContract {
address constant GOVERNANCE_PRECOMPILE = 0x0000000000000000000000000000000000000805;
function voteOnProposal(uint256 proposalId, uint8 option) external {
IGovernancePrecompile(GOVERNANCE_PRECOMPILE).vote(proposalId, option);
}
}TokenFactory ERC20 Precompiles
TokenFactory tokens automatically get ERC20 precompile addresses:
interface ITokenFactoryERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
}
contract TokenContract {
// Precompile address for a specific TokenFactory token.
// This address is deterministic based on the token denom.
address immutable tokenPrecompile;
constructor(address tokenPrecompile_) {
tokenPrecompile = tokenPrecompile_;
}
function transferToken(address to, uint256 amount) external {
ITokenFactoryERC20(tokenPrecompile).transfer(to, amount);
}
}Best Practices
Error Handling
Always check return values from precompiles:
function safeDelegate(address validator, uint256 amount) external {
bool success = IStakingPrecompile(STAKING_PRECOMPILE).delegate(validator, amount);
require(success, "Delegation failed");
}Gas Considerations
Precompiles execute native Go code, which is generally more gas-efficient than contract calls, but still consider:
- Gas costs for precompile calls
- Batch operations when possible
- Optimize for frequently called functions
Security
- Validate inputs before calling precompiles
- Use access controls for precompile calls
- Test precompile interactions thoroughly
Next Steps
- Learn more about Cosmos precompiles architecture
- Understand transaction sequencing
- Explore EVM development guides