Skip to main content

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.

AddressNameDescription
0x0000000000000000000000000000000000000001ECRecoverECDSA signature recovery
0x0000000000000000000000000000000000000002SHA-256SHA-2 256-bit hash
0x0000000000000000000000000000000000000003RIPEMD-160RIPEMD 160-bit hash
0x0000000000000000000000000000000000000004IdentityData copy (identity function)
0x0000000000000000000000000000000000000005ModexpModular exponentiation (EIP-198)
Not yet registered: 0x060x09

The 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.

AddressNameDescription
0x0000000000000000000000000000000000000400SHA3-FIPS-256FIPS 202-compliant Keccak-256
0x0000000000000000000000000000000000000401ECRecoverPublicKeyECDSA public key recovery
0x0000000000000000000000000000000000000402Curve25519AddCurve25519 point addition
0x0000000000000000000000000000000000000403Curve25519ScalarMulCurve25519 scalar multiplication

Orbinum Precompiles

These precompiles expose Orbinum-specific runtime pallets to EVM clients. They are allocated from the 0x08000x08FF range to avoid collisions with standard and Frontier precompiles.

AddressNameDescription
0x0000000000000000000000000000000000000801ShieldedPoolShield, transfer, and unshield tokens using ZK proofs
0x0000000000000000000000000000000000000802BalancesTransfer native tokens to any AccountId32, including Sr25519 and Ed25519 accounts

Address Derivation

Orbinum precompile indices map as follows:

IndexHex 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. Balances derives the caller's AccountId32 from the EVM H160 sender via AddressMapping. ShieldedPool does not: shield dispatches under the precompile's own address, and privateTransfer / unshield dispatch 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.