Pallets
Orbinum extends Substrate with custom pallets for privacy and EVM compatibility.
Table of Contents
Overview
| Pallet | Purpose | Status |
|---|---|---|
pallet-shielded-pool | UTXO privacy pool | Implemented |
pallet-zk-verifier | ZK proof verification | Implemented |
pallet-evm | EVM execution | Implemented |
pallet-ethereum | Ethereum transactions | Implemented |
pallet-base-fee | EIP-1559 fees | Implemented |
pallet-dynamic-fee | Fee adjustment | Implemented |
pallet-evm-chain-id | Chain ID config | Implemented |
Privacy Pallets
pallet-shielded-pool
The core privacy pallet implementing a UTXO-based shielded pool.
Location: frame/shielded-pool
Extrinsics
| Extrinsic | Description | Weight |
|---|---|---|
shield | Deposit to shielded pool | ~50,000 |
private_transfer | Private transfer | ~150,000 |
unshield | Withdraw from pool | ~100,000 |
Storage
#[pallet::storage]
pub type MerkleRoot<T> = StorageValue<_, [u8; 32], ValueQuery>;
#[pallet::storage]
pub type MerkleLeaves<T> = StorageValue<_, BoundedVec<[u8; 32], MaxLeaves>, ValueQuery>;
#[pallet::storage]
pub type NullifierSet<T> = StorageMap<_, Blake2_128Concat, [u8; 32], bool, ValueQuery>;
#[pallet::storage]
pub type PoolBalance<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
#[pallet::storage]
pub type HistoricRoots<T> = StorageMap<_, Blake2_128Concat, [u8; 32], (), OptionQuery>;
Events
#[pallet::event]
pub enum Event<T: Config> {
/// Tokens shielded into pool
Shielded {
sender: T::AccountId,
commitment: [u8; 32],
value: BalanceOf<T>,
},
/// Private transfer executed
PrivateTransfer {
nullifiers: Vec<[u8; 32]>,
commitments: Vec<[u8; 32]>,
},
/// Tokens unshielded from pool
Unshielded {
recipient: T::AccountId,
nullifier: [u8; 32],
value: BalanceOf<T>,
},
}
Errors
#[pallet::error]
pub enum Error<T> {
/// Insufficient balance
InsufficientBalance,
/// Invalid proof
InvalidProof,
/// Nullifier already spent
NullifierAlreadySpent,
/// Invalid Merkle root
InvalidMerkleRoot,
/// Pool overflow
PoolOverflow,
}
pallet-zk-verifier
On-chain verification of Zero-Knowledge proofs.
Location: frame/zk-verifier
Extrinsics
| Extrinsic | Description | Weight |
|---|---|---|
verify_proof | Verify a Groth16 proof | ~200,000 |
register_verification_key | Register circuit VK | Governance |
update_verification_key | Update circuit VK | Governance |
Storage
#[pallet::storage]
pub type VerificationKeys<T> = StorageMap<
_,
Blake2_128Concat,
CircuitId,
VerificationKey,
OptionQuery,
>;
#[pallet::storage]
pub type CircuitInfo<T> = StorageMap<
_,
Blake2_128Concat,
CircuitId,
CircuitMetadata,
OptionQuery,
>;
#[pallet::storage]
pub type VerificationStats<T> = StorageValue<_, VerificationStatistics, ValueQuery>;
Circuit IDs
| ID | Circuit | Inputs |
|---|---|---|
| 0 | Shield | 1 commitment |
| 1 | Transfer (2-in-2-out) | 2 nullifiers, 2 commitments |
| 2 | Unshield | 1 nullifier, 1 recipient |
EVM Pallets
pallet-evm
Full Ethereum Virtual Machine implementation.
Location: frame/evm
Configuration
parameter_types! {
pub BlockGasLimit: U256 = U256::from(15_000_000);
pub WeightPerGas: Weight = Weight::from_parts(20_000, 0);
}
impl pallet_evm::Config for Runtime {
type FeeCalculator = BaseFee;
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type BlockGasLimit = BlockGasLimit;
type CallOrigin = EnsureAddressTruncated;
type WithdrawOrigin = EnsureAddressTruncated;
type AddressMapping = HashedAddressMapping<BlakeTwo256>;
type Currency = Balances;
// ...
}
Precompiles
Standard Ethereum precompiles plus custom:
pub struct OrhinumPrecompiles<R>(PhantomData<R>);
impl<R> PrecompileSet for OrhinumPrecompiles<R> {
fn execute(...) -> Option<PrecompileResult> {
match address {
a if a == hash(1) => Some(ECRecover::execute(...)),
a if a == hash(2) => Some(Sha256::execute(...)),
// ... standard precompiles
a if a == hash(0x800) => Some(Poseidon::execute(...)),
a if a == hash(0x801) => Some(ShieldedPool::execute(...)),
_ => None,
}
}
}
pallet-ethereum
Ethereum transaction format support.
Location: frame/ethereum
Transaction Types
pub enum TransactionV2 {
/// Legacy transaction (pre-EIP-2718)
Legacy(LegacyTransaction),
/// EIP-2930 access list transaction
EIP2930(EIP2930Transaction),
/// EIP-1559 dynamic fee transaction
EIP1559(EIP1559Transaction),
}
Events
#[pallet::event]
pub enum Event {
/// Ethereum transaction executed
Executed {
from: H160,
to: H160,
transaction_hash: H256,
exit_reason: ExitReason,
},
}
pallet-base-fee
EIP-1559 base fee calculation.
Location: frame/base-fee
Configuration
parameter_types! {
pub DefaultBaseFeePerGas: U256 = U256::from(1_000_000_000); // 1 Gwei
pub MinBaseFeePerGas: U256 = U256::from(1_000_000); // 0.001 Gwei
pub MaxBaseFeePerGas: U256 = U256::from(100_000_000_000); // 100 Gwei
}
Algorithm
Base fee adjusts based on block utilization:
fn compute_next_base_fee(
current: U256,
gas_used: U256,
gas_target: U256,
) -> U256 {
if gas_used > gas_target {
// Block fuller than target: increase fee
let delta = current * (gas_used - gas_target) / gas_target / 8;
current.saturating_add(delta)
} else {
// Block emptier than target: decrease fee
let delta = current * (gas_target - gas_used) / gas_target / 8;
current.saturating_sub(delta)
}
}
Pallet Dependencies
Coupling
| Pallet | Tight Coupling | Loose Coupling |
|---|---|---|
| shielded-pool | zk-verifier | balances |
| zk-verifier | - | - |
| evm | base-fee | balances |
| ethereum | evm | - |
Adding New Pallets
Template
#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
#[pallet::storage]
pub type MyStorage<T> = StorageValue<_, u32, ValueQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
SomethingHappened { value: u32 },
}
#[pallet::error]
pub enum Error<T> {
SomethingWrong,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(10_000)]
pub fn do_something(origin: OriginFor<T>, value: u32) -> DispatchResult {
let _who = ensure_signed(origin)?;
MyStorage::<T>::put(value);
Self::deposit_event(Event::SomethingHappened { value });
Ok(())
}
}
}
Related Documentation
- Parameters - Runtime configuration
- Shielded Pool - Privacy architecture
- EVM Compatibility - Ethereum integration