Architecture
Kabu is built on a modern component-based architecture that emphasizes modularity, concurrency, and type safety. The system efficiently processes blockchain data and identifies arbitrage opportunities through a unified node design with pluggable components.
Core Design Principles
- Node-Based Architecture: Centralized node pattern with pluggable components
- Component-Based Concurrency: Each component runs independently with message-passing communication
- Type Safety: Extensive use of Rust's type system for correctness
- Modular Design: Clear separation between data types, business logic, and infrastructure
- Performance First: Optimized for low-latency arbitrage detection and execution
Node System
The new node system provides a unified way to build and launch Kabu instances:
KabuNode Trait
The KabuNode
trait defines the interface for different node implementations:
pub trait KabuNode<P, DB>: Send + Sync + 'static {
async fn build_components(
&self,
context: &KabuBuildContext<P, DB>,
) -> Result<Vec<BoxedComponent>>;
}
KabuBuilder
The KabuBuilder
provides a fluent API for constructing and launching nodes:
let handle = KabuBuilder::new(kabu_context)
.node(KabuEthereumNode::<P, DB>::default())
.build()
.launch(task_executor)
.await?;
KabuBuildContext
The build context centralizes all configuration and shared resources:
let kabu_context = KabuBuildContext::builder(
provider,
blockchain,
blockchain_state,
topology_config,
backrun_config,
multicaller_address,
db_pool,
is_exex,
)
.with_pools_config(pools_config)
.with_swap_encoder(swap_encoder)
.with_channels(mev_channels)
.build();
Component System
Components are independent processing units that:
- Implement the
Component
trait - Run as tokio tasks via
TaskExecutor
- Communicate through
MevComponentChannels
- Use builder pattern for configuration
- Support graceful shutdown
Key Components
- StateChangeArbSearcherComponent: Detects arbitrage in state changes
- SwapRouterComponent: Routes and encodes swap transactions
- SignersComponent: Manages transaction signing with nonce tracking
- FlashbotsBroadcastComponent: Submits bundles to Flashbots
- Market Components: Pool discovery and state management
- PoolHealthMonitorComponent: Tracks pool reliability
Type System Organization
Kabu's type system is split into three crates for modularity:
types/entities
Core blockchain and configuration types:
Block
,Transaction
,Account
StrategyConfig
trait and implementations- Pool loading configurations
types/market
Market structure and routing:
Token
: ERC20 with decimals, price, categoriesPool
trait: Unified pool interfaceMarket
: Registry of tokens and poolsSwapDirection
: Token pair direction in poolSwapPath
: Route through multiple pools
types/swap
Execution and profit tracking:
Swap
: Final transaction ready for executionSwapLine
: Path with amounts and gas costsSwapStep
: Single pool interaction- Calculation utilities for profit estimation
Communication Architecture
MevComponentChannels
All component communication is centralized through MevComponentChannels
:
pub struct MevComponentChannels<DB> {
// Market events
pub market_events: broadcast::Sender<MarketEvents>,
pub mempool_events: broadcast::Sender<MempoolEvents>,
// Swap processing
pub swap_compose: broadcast::Sender<SwapComposeMessage>,
pub estimated_swaps: broadcast::Sender<SwapComposeMessage>,
// Health monitoring
pub health_events: broadcast::Sender<HealthEvent>,
// Shared state
pub signers: Arc<RwLock<TxSigners>>,
pub account_state: Arc<RwLock<AccountNonceAndBalanceState>>,
}
Message Flow
The typical arbitrage detection and execution flow:
Block/Mempool Event
↓
StateChangeArbSearcher
↓
SwapCompose (prepare)
↓
Merger Components
↓
EvmEstimator
↓
SwapRouter
↓
SignersComponent
↓
FlashbotsBroadcast
Building a Kabu Instance
Using KabuEthereumNode
The standard way to create a Kabu instance:
// Create provider
let provider = ProviderBuilder::new()
.on_http(node_url)
.build()?;
// Initialize blockchain and state
let blockchain = Blockchain::new(chain_id);
let blockchain_state = BlockchainState::<KabuDB, KabuDataTypesEthereum>::new();
// Build context
let kabu_context = KabuBuildContext::builder(
provider,
blockchain,
blockchain_state,
topology_config,
backrun_config,
multicaller_address,
db_pool,
false, // is_exex
)
.build();
// Launch with KabuBuilder
let handle = KabuBuilder::new(kabu_context)
.node(KabuEthereumNode::default())
.build()
.launch(task_executor)
.await?;
// Wait for shutdown
handle.wait_for_shutdown().await?;
Custom Node Implementation
You can create custom nodes by implementing the KabuNode
trait:
pub struct CustomNode;
impl<P, DB> KabuNode<P, DB> for CustomNode
where
P: Provider + Send + Sync + 'static,
DB: Database + Send + Sync + 'static,
{
async fn build_components(
&self,
context: &KabuBuildContext<P, DB>,
) -> Result<Vec<BoxedComponent>> {
let mut components = vec![];
// Add your custom components
components.push(Box::new(
MyCustomComponent::new(context.config.clone())
.with_channels(&context.channels)
));
Ok(components)
}
}
This architecture provides:
- Unified node construction
- Centralized configuration
- Easy component composition
- Clean shutdown handling
- Flexible customization