Skip to main content

Pallets

Orbinum extends Substrate with custom pallets for privacy and EVM compatibility.


Table of Contents


Overview

PalletPurposeStatus
pallet-shielded-poolUTXO privacy poolImplemented
pallet-zk-verifierZK proof verificationImplemented
pallet-evmEVM executionImplemented
pallet-ethereumEthereum transactionsImplemented
pallet-base-feeEIP-1559 feesImplemented
pallet-dynamic-feeFee adjustmentImplemented
pallet-evm-chain-idChain ID configImplemented

Privacy Pallets

pallet-shielded-pool

The core privacy pallet implementing a UTXO-based shielded pool.

Location: frame/shielded-pool

Extrinsics

ExtrinsicDescriptionWeight
shieldDeposit to shielded pool~50,000
private_transferPrivate transfer~150,000
unshieldWithdraw 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

ExtrinsicDescriptionWeight
verify_proofVerify a Groth16 proof~200,000
register_verification_keyRegister circuit VKGovernance
update_verification_keyUpdate circuit VKGovernance

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

IDCircuitInputs
0Shield1 commitment
1Transfer (2-in-2-out)2 nullifiers, 2 commitments
2Unshield1 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

PalletTight CouplingLoose Coupling
shielded-poolzk-verifierbalances
zk-verifier--
evmbase-feebalances
ethereumevm-

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(())
}
}
}