A framework for building Verifiable, Opinionated, Interoperable, and Decentralized (VOID) applications that run sovereignly across multiple blockchain ecosystems.
The VOID Toolkit provides developers with the tools needed to build sovereign, multi-chain applications that offer the benefits of app-specific chains while maintaining access to users and liquidity across multiple blockchain ecosystems.
VOID solves the fundamental trade-off that apps face today: choosing between deploying on general-purpose chains (losing sovereignty but gaining access to users) or launching app-specific chains (gaining sovereignty but losing distribution).
- Accept transactions from multiple blockchains simultaneously (Ethereum, Solana, Base, etc.).
- Users interact with your app using their existing wallets on their preferred chains.
- No onboarding friction.
- Define your own fee models and transaction ordering logic.
- Choose your execution environment or virtual machine.
- Implement specialized logic for gaming, DeFi, social apps, and more.
- Control and optimize your app’s transaction ordering.
- Capture, internalize or minimize all MEV generated by your application.
- Full control over your application's infrastructure.
- No dependence on general-purpose chain roadmaps or politics.
- Credible neutrality without ecosystem lock-in.
- Support for low latency, high throughput use cases.
- Efficient state management and proof generation.
- Optimized for real-time applications.
VOID applications consist of three main components:
- VOID Oracle - Aggregates events from interface contracts across multiple blockchains
- VOID Node - Executes your app's state transition function with custom ordering logic
- VOID Prove - Generates cryptographic proofs of state correctness
graph BT
subgraph BC ["Blockchains"]
D[Ethereum]
E[Optimism]
F[Base]
G[...]
end
subgraph "VOID Application"
A[VOID Oracle<br/>
• Multi-chain event aggregation<br/>
• Proof of events]
B[VOID Node<br/>
• Custom state transitions<br/>
• Transaction ordering<br/>
• Execution environment]
C[VOID Prove<br/>
• ZK proofs<br/>
• TEE attestations<br/>
• Optimistic proofs<br/>
• Configurable models]
end
BC --> A
A --> B
B --> C
classDef default text-align:center;
Add the VOID Toolkit to your Cargo.toml:
[dependencies]
void-toolkit = { version = "0.1", features = ["run", "oracle", "load-config"] }use std::convert::Infallible;
use futures::TryStreamExt;
use void_toolkit::{
app::UpdateLatestBlock,
load_config::load_config,
oracle::{oracle, Db},
oracle_types::config::Config,
run::run_node,
types::{Block, Lock},
};
pub struct State(pub u64);
impl UpdateLatestBlock for State {
type Error = Infallible;
fn update_latest_block(&mut self, _height: u64, _hash: [u8; 32]) -> Result<(), Self::Error> {
todo!()
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load configuration
let config: Config = load_config("config.yaml")?;
// Start oracle to watch for cross-chain events
let blocks = oracle(config, Db::memory(), 0).map_err(|e| anyhow::anyhow!(e));
// Define your app's state and transition logic
let state_transition = |block: &Block, _state: &mut State| -> Result<(), Infallible> {
// Process block and update state
for _event in &block.events {
// Handle cross-chain events
println!(
"Processing {} events in block {}",
block.events.len(),
block.height
);
}
Ok(())
};
let state = Lock::new(State(0));
// Run your VOID node
run_node(blocks, state, state_transition).await?;
Ok(())
}VOID provides different oracle modes for various deployment scenarios:
use void_toolkit::{
load_config::load_config,
oracle::{observer_oracle, oracle, publisher_oracle, relay_oracle, Db},
oracle_types::config::{Config, OracleBlocksConfig},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config: Config = load_config("config.yaml")?;
// Basic oracle - watches events and produces blocks locally
let _blocks = oracle(config.clone(), Db::memory(), 0);
// Publisher oracle - produces blocks and serves them over HTTP/SSE
let _blocks = publisher_oracle(
config.clone(),
Db::memory(),
0,
"127.0.0.1:8080".parse().unwrap(),
100,
None,
);
// Observer oracle - connects to existing producer, consumes blocks
let _blocks = observer_oracle(
OracleBlocksConfig {
endpoint: "http://127.0.0.1:8080".to_string(),
height: 0,
public_key: None,
},
Db::memory(),
);
// Relay oracle - connects to producer and also serves blocks to others
let _blocks = relay_oracle(
OracleBlocksConfig {
endpoint: "http://127.0.0.1:8080".to_string(),
height: 0,
public_key: None,
},
Db::memory(),
"127.0.0.1:8081".parse().unwrap(),
100,
);
Ok(())
}Create a config.yaml file to specify which chains and events to monitor:
# Example configuration for VOID Toolkit
# Uses public testnet endpoints - replace with your own for production
query:
stream_config:
- !ChainContractLogs
rpc_url: !Ws "wss://ethereum-sepolia.publicnode.com"
contract_addresses: ["0xA0b86a33E6417d7C1b1E4C2e75b9b0e2D6b2d8a0"] # Example contract
event_signatures: ["Transfer(address,address,uint256)"]
- !ChainContractLogs
rpc_url: !Ws "wss://base-sepolia.publicnode.com"
contract_addresses: ["0xB1c86a33E6417d7C1b1E4C2e75b9b0e2D6b2d8a1"] # Example contract
event_signatures: ["TokenTransfer(address,address,uint256)"]
block:
max_block_size: 1000000
block_duration_ms: 5000The VOID Toolkit consists of several specialized crates:
Core toolkit with runtime utilities, oracle integration, and configuration loading. Re-exports the other crates when relevant features are enabled.
Sparse Merkle tree implementations for efficient data verification with cryptographic proofs.
Zero-knowledge proof generation using RISC Zero for state transition verification.
ECDSA signature utilities compatible with Ethereum's cryptographic standards.
Network communication primitives for distributed replication and synchronization.
Macro library for generating Ethereum event decoders for cross-chain event processing.
VOID provides cryptographic proof generation using RISC Zero:
use void_toolkit::proof::host::prove;
use void_toolkit::types::Block;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Example block data (in practice this would come from your oracle)
let block = Block {
height: 0,
parent_hash: [0; 32],
events: vec![],
};
// Example witness data (application-specific state transition data)
let witness_data = vec![]; // Placeholder - would contain actual witness data
// Example circuit ELF (compiled RISC Zero program)
let circuit_elf = &[]; // Placeholder - would contain actual circuit binary
// Generate a ZK proof for a block with witness data
let proof_bytes = prove(&block, witness_data, circuit_elf)?;
println!("Generated proof of {} bytes", proof_bytes.len());
// The proof can be verified independently and transmitted to other parties
println!("Proof generation complete");
Ok(())
}Use the event decoder to handle events from different chains:
use alloy::sol;
use futures::TryStreamExt;
use void_toolkit::{
load_config::load_config, oracle::oracle, oracle::Db, oracle_decoder::decode_events,
oracle_types::config::Config,
};
// Define the events we want to decode
sol! {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Deposit(address indexed user, uint256 amount);
}
// Generate the decoder for these events
decode_events! {
Transfer,
Deposit,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load configuration
let config: Config = load_config("config.yaml")?;
// Start oracle to watch for cross-chain events
let blocks = oracle(config, Db::memory(), 0);
// Process decoded events in your state transition function
blocks
.try_for_each(|block| async move {
println!("Processing block with {} events", block.events.len());
for event in &block.events {
// Try to decode each event
if let Ok(decoded_event) = decode_input(event) {
match decoded_event {
AppEvent::Transfer(transfer) => {
println!(
"Transfer: {} -> {} (amount: {})",
transfer.from, transfer.to, transfer.amount
);
}
AppEvent::Deposit(deposit) => {
println!("Deposit: {} deposited {}", deposit.user, deposit.amount);
}
}
} else {
println!("Unknown event: {} bytes", event.len());
}
}
Ok(())
})
.await?;
Ok(())
}VOID is ideal for applications that need high performance, custom logic execution, and seamless cross-chain integration, such as:
- DeFi Protocols: DEXs, lending platforms, derivatives with cross-chain liquidity
- Gaming Applications: Real-time games requiring low latency and custom logic
- Social Applications: Decentralized social networks with specialized features
- Enterprise: Private or consortium applications with specific compliance needs