EVM Compatibility
Orbinum provides full EVM compatibility through Frontier, enabling Ethereum dApps and tooling to work seamlessly.
Table of Contents
Overview
Frontier is a set of Substrate pallets that implement:
- EVM execution - Full Ethereum Virtual Machine
- Ethereum RPC - Web3-compatible JSON-RPC
- Transaction format - Ethereum transaction types
- Account model - Unified Substrate/Ethereum accounts
Architecture
Pallets
| Pallet | Purpose |
|---|---|
pallet-evm | EVM execution environment |
pallet-ethereum | Ethereum transaction handling |
pallet-base-fee | EIP-1559 base fee calculation |
pallet-dynamic-fee | Dynamic fee adjustment |
pallet-evm-chain-id | Chain ID configuration |
Account Mapping
Substrate and Ethereum accounts are unified:
Ethereum Address (20 bytes) <-> Substrate AccountId (32 bytes)
Mapping strategy:
// Ethereum address from Substrate account
fn to_ethereum_address(account: AccountId32) -> H160 {
H160::from_slice(&account.as_ref()[0..20])
}
// Substrate account from Ethereum address
fn to_substrate_account(address: H160) -> AccountId32 {
let mut data = [0u8; 32];
data[0..20].copy_from_slice(address.as_bytes());
AccountId32::from(data)
}
Supported Features
EIPs Implemented
| EIP | Name | Status |
|---|---|---|
| EIP-155 | Replay protection | Implemented |
| EIP-1559 | Fee market | Implemented |
| EIP-2930 | Access lists | Implemented |
| EIP-2718 | Typed transactions | Implemented |
| EIP-3855 | PUSH0 opcode | Implemented |
Transaction Types
| Type | EIP | Support |
|---|---|---|
| Legacy | Pre-EIP-2718 | Yes |
| EIP-2930 | Access list | Yes |
| EIP-1559 | Dynamic fee | Yes |
Opcodes
Full EVM opcode support including:
- All arithmetic operations
- Memory/storage operations
- Control flow
- Environmental information
- Block information
- Logging (LOG0-LOG4)
- System operations (CREATE, CALL, etc.)
Gas and Fees
EIP-1559 Implementation
Base Fee Adjustment
// From pallet-base-fee
fn next_base_fee(
current: U256,
gas_used: U256,
gas_target: U256,
) -> U256 {
if gas_used > gas_target {
// Increase base fee
current + (current * (gas_used - gas_target)) / gas_target / 8
} else {
// Decrease base fee
current - (current * (gas_target - gas_used)) / gas_target / 8
}
}
Fee Parameters
| Parameter | Value | Description |
|---|---|---|
| Gas limit | 15M | Per-block gas limit |
| Gas target | 7.5M | Target utilization |
| Min base fee | 1 Gwei | Minimum base fee |
| Max fee change | 12.5% | Per-block change limit |
Precompiles
Standard Precompiles
| Address | Name | Gas |
|---|---|---|
0x01 | ecRecover | 3000 |
0x02 | SHA256 | 60 + 12/word |
0x03 | RIPEMD160 | 600 + 120/word |
0x04 | Identity | 15 + 3/word |
0x05 | Modexp | Variable |
0x06 | BN254 Add | 150 |
0x07 | BN254 Mul | 6000 |
0x08 | BN254 Pairing | 34000 + 45000/pair |
0x09 | Blake2 | Variable |
Custom Precompiles
Orbinum extends precompiles for privacy features:
| Address | Name | Purpose |
|---|---|---|
0x800 | Poseidon | Poseidon hash |
0x801 | ShieldedPool | Privacy operations |
0x802 | ZKVerifier | Proof verification |
WIP
Custom precompiles are under development. Interface may change.
Using ZK Precompile
interface IZKVerifier {
function verifyProof(
bytes calldata proof,
bytes32[] calldata publicInputs
) external view returns (bool);
}
contract PrivateVault {
IZKVerifier constant verifier = IZKVerifier(0x802);
function withdraw(
bytes calldata proof,
bytes32[] calldata inputs
) external {
require(verifier.verifyProof(proof, inputs), "Invalid proof");
// Process withdrawal
}
}
Developer Integration
Network Configuration
// MetaMask / ethers.js configuration
const network = {
chainId: '0x1234', // Orbinum chain ID
chainName: 'Orbinum Testnet',
nativeCurrency: {
name: 'ORB',
symbol: 'ORB',
decimals: 18
},
rpcUrls: ['https://rpc.orbinum.network'],
blockExplorerUrls: ['https://explorer.orbinum.network']
};
Hardhat Configuration
// hardhat.config.js
module.exports = {
networks: {
orbinum: {
url: 'https://rpc.orbinum.network',
chainId: 0x1234,
accounts: [process.env.PRIVATE_KEY],
gas: 15000000,
gasPrice: 'auto'
}
},
solidity: {
version: '0.8.20',
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
}
};
Deployment Example
const { ethers } = require('hardhat');
async function main() {
const Token = await ethers.getContractFactory('MyToken');
const token = await Token.deploy('My Token', 'MTK');
await token.waitForDeployment();
console.log('Token deployed to:', await token.getAddress());
}
main().catch(console.error);
Limitations
| Limitation | Description | Workaround |
|---|---|---|
| Block time | ~6s vs Ethereum ~12s | Account for in dApps |
| Gas metering | Substrate weight-based | Use gas estimates |
| State size | Trie differences | Optimize storage |
Related Documentation
- System Overview - Full architecture
- Shielded Pool - Privacy integration