EVM Precompiles
Precompiles are special contracts accessible at fixed addresses that execute native runtime logic instead of EVM bytecode. They allow EVM clients — including MetaMask and other Ethereum wallets — to interact directly with Substrate pallets and cryptographic primitives without needing a custom JSON-RPC endpoint.
Orbinum exposes three groups of precompiles: Ethereum standard precompiles, Frontier non-standard precompiles, and Orbinum-specific precompiles.
Address Convention
Precompile addresses follow the Frontier convention:
H160::from_low_u64_be(n)
where n is a small integer. This produces an address of the form
0x0000000000000000000000000000000000000NNN.
Ethereum Standard Precompiles
These are the precompiles defined in the Ethereum Yellow Paper and its EIPs. They are available at the same addresses as Ethereum mainnet.
| Address | Name | Description |
|---|---|---|
0x0000000000000000000000000000000000000001 | ECRecover | ECDSA signature recovery |
0x0000000000000000000000000000000000000002 | SHA-256 | SHA-2 256-bit hash |
0x0000000000000000000000000000000000000003 | RIPEMD-160 | RIPEMD 160-bit hash |
0x0000000000000000000000000000000000000004 | Identity | Data copy (identity function) |
0x0000000000000000000000000000000000000005 | Modexp | Modular exponentiation (EIP-198) |
0x06–0x09The BN128 precompiles (0x06 add, 0x07 mul, 0x08 pairing) and BLAKE2F (0x09) are not
currently registered on Orbinum.
This matters if you are porting a contract that verifies ZK proofs on-chain — a snarkjs-generated
Groth16Verifier.sol, Semaphore, or any Tornado-style mixer. Those contracts call 0x08 for the
pairing check.
A call to an unregistered precompile address is not a revert. The EVM treats it as a call to an
empty account: it succeeds and returns empty data. A verifier that decodes that as false will
silently reject every valid proof, which is difficult to diagnose. Test any pairing-dependent
contract before relying on it.
Frontier Non-Standard Precompiles
These precompiles are provided by Frontier for Substrate-based chains. They are not part of the Ethereum standard and are only available on Frontier-compatible networks.
| Address | Name | Description |
|---|---|---|
0x0000000000000000000000000000000000000400 | SHA3-FIPS-256 | FIPS 202-compliant Keccak-256 |
0x0000000000000000000000000000000000000401 | ECRecoverPublicKey | ECDSA public key recovery |
0x0000000000000000000000000000000000000402 | Curve25519Add | Curve25519 point addition |
0x0000000000000000000000000000000000000403 | Curve25519ScalarMul | Curve25519 scalar multiplication |
Orbinum Precompiles
These precompiles expose Orbinum-specific runtime pallets to EVM clients. They are allocated from
the 0x0800–0x08FF range to avoid collisions with standard and Frontier precompiles.
| Address | Name | Description |
|---|---|---|
0x0000000000000000000000000000000000000801 | ShieldedPool | Shield, transfer, and unshield tokens using ZK proofs |
0x0000000000000000000000000000000000000802 | Balances | Transfer native tokens to any AccountId32, including Sr25519 and Ed25519 accounts |
Address Derivation
Orbinum precompile indices map as follows:
| Index | Hex Address |
|---|---|
2049 (0x801) | 0x0000000000000000000000000000000000000801 |
2050 (0x802) | 0x0000000000000000000000000000000000000802 |
Calling a Precompile
The Balances precompile is the simplest one to start with — no ZK proof, two parameters:
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;
interface IBalances {
function transfer(bytes32 dest, uint256 value) external;
}
IBalances balances = IBalances(0x0000000000000000000000000000000000000802);
balances.transfer(recipientAccountId32, 1 ether);
From ethers.js:
import { ethers } from 'ethers';
const abi = ['function transfer(bytes32 dest, uint256 value)'];
const balances = new ethers.Contract(
'0x0000000000000000000000000000000000000802',
abi,
signer
);
await balances.transfer(recipientAccountId32, ethers.parseEther('1'));
Each precompile page documents its full ABI. Note that ShieldedPool.shield is payable — the
deposited amount travels in msg.value rather than as a parameter.
Security Notes
- Origin derivation varies by precompile.
Balancesderives the caller'sAccountId32from the EVMH160sender viaAddressMapping.ShieldedPooldoes not:shielddispatches under the precompile's own address, andprivateTransfer/unshielddispatch unsigned, authenticated by their ZK proof. See ShieldedPool → Origin model. - No signature verification is repeated inside a precompile — the EVM transaction already provides authentication for whichever address is used as origin.
- Precompiles do not return values beyond a success indicator. Use RPC or events to confirm state changes.
- Gas costs are estimated conservatively. They may be adjusted before mainnet.