Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Starkbiter Engine

starkbiter-engine provides high-level abstractions for building complex, multi-agent simulations on Starknet. It sits on top of starkbiter-core and offers ergonomic interfaces for agent-based modeling.

Overview

The engine crate enables you to:

  • Create Agents - Autonomous entities with custom behaviors
  • Define Behaviors - Reusable action patterns for agents
  • Build Worlds - Shared simulation environments
  • Orchestrate Universes - Multiple parallel simulations
  • Enable Messaging - Inter-agent communication

Installation

Add to your Cargo.toml:

[dependencies]
starkbiter-engine = "0.1"
starkbiter-core = "0.1"
tokio = { version = "1.0", features = ["full"] }

Quick Start

use starkbiter_core::environment::Environment;
use starkbiter_engine::{Agent, Behavior, World};
use anyhow::Result;

// Define a behavior
struct TradingBehavior;

impl Behavior for TradingBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        // Agent logic here
        println!("Executing trading strategy");
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    // Create environment
    let env = Environment::builder().build().await?;
    
    // Create world
    let world = World::new(env);
    
    // Create and add agents
    let trader = Agent::new("trader", TradingBehavior);
    world.add_agent(trader);
    
    // Run simulation
    world.run().await?;
    
    Ok(())
}

Core Concepts

Agents

Agents are autonomous entities that execute behaviors. They can:

  • React to blockchain events
  • Maintain internal state
  • Communicate with other agents
  • Execute transactions

Learn more about Agents →

Behaviors

Behaviors define what agents do. They are:

  • Reusable action patterns
  • Composable and modular
  • Event-driven or scheduled
  • Stateful or stateless

Learn more about Behaviors →

Worlds

Worlds provide the simulation environment:

  • Shared blockchain state
  • Agent coordination
  • Event distribution
  • Execution scheduling

Learn more about Worlds →

Universes

Universes manage multiple worlds:

  • Parallel simulations
  • Cross-world analytics
  • Resource management
  • Coordinated execution

Learn more about Universes →

Architecture

Universe
  ├─ World 1
  │   ├─ Agent A (Behavior 1)
  │   ├─ Agent B (Behavior 2)
  │   └─ Environment
  └─ World 2
      ├─ Agent C (Behavior 3)
      └─ Environment

Key Features

🤖 Agent-Based Modeling

Build sophisticated simulations with multiple autonomous agents:

#![allow(unused)]
fn main() {
// Create different agent types
let liquidator = Agent::new("liquidator", LiquidatorBehavior);
let borrower = Agent::new("borrower", BorrowerBehavior);
let lender = Agent::new("lender", LenderBehavior);

// Add to world
world.add_agent(liquidator);
world.add_agent(borrower);
world.add_agent(lender);

// Agents interact autonomously
world.run().await?;
}

📡 Event-Driven Architecture

Agents react to blockchain events:

#![allow(unused)]
fn main() {
impl Behavior for ArbitrageBehavior {
    async fn on_event(&mut self, event: Event) -> Result<()> {
        if event.name == "Swap" {
            // Check for arbitrage opportunity
            if let Some(profit) = self.check_arbitrage(&event).await? {
                self.execute_arbitrage(profit).await?;
            }
        }
        Ok(())
    }
}
}

💬 Inter-Agent Communication

Agents can message each other:

#![allow(unused)]
fn main() {
// Agent A sends message
world.send_message("agent-b", Message::RequestPrice).await?;

// Agent B receives and responds
impl Behavior for PriceOracleBehavior {
    async fn on_message(&mut self, msg: Message) -> Result<()> {
        match msg {
            Message::RequestPrice => {
                let price = self.get_current_price().await?;
                self.respond(Message::PriceUpdate(price)).await?;
            }
            _ => {}
        }
        Ok(())
    }
}
}

⚙️ Configuration-Driven

Define simulations in TOML:

# config.toml
[environment]
chain_id = "0x534e5f5345504f4c4941"
block_time = 10

[agents.liquidator]
behavior = "LiquidatorBehavior"
threshold = 0.8

[agents.borrower]
behavior = "BorrowerBehavior"
risk_profile = "aggressive"

Learn more about Configuration →

Use Cases

DeFi Protocol Testing

Simulate complex DeFi scenarios:

#![allow(unused)]
fn main() {
// Setup lending protocol
let world = create_lending_world().await?;

// Add diverse agents
world.add_agent(Agent::new("whale-lender", WhaleLender));
world.add_agent(Agent::new("retail-borrower", RetailBorrower));
world.add_agent(Agent::new("liquidator-bot", LiquidatorBot));
world.add_agent(Agent::new("oracle", PriceOracle));

// Simulate market conditions
world.run_for_blocks(1000).await?;

// Analyze results
let metrics = world.get_metrics();
assert!(metrics.protocol_health > 0.95);
}

Economic Modeling

Model economic systems:

#![allow(unused)]
fn main() {
// AMM simulation
struct LiquidityProvider;
struct Arbitrageur;
struct RetailTrader;

let world = World::new(env);
world.add_agent(Agent::new("lp-1", LiquidityProvider));
world.add_agent(Agent::new("arb-1", Arbitrageur));
world.add_agent(Agent::new("trader-1", RetailTrader));

// Simulate trading activity
world.run().await?;

// Analyze pool dynamics
let pool_metrics = analyze_pool_behavior(&world);
}

Stress Testing

Test protocol under extreme conditions:

#![allow(unused)]
fn main() {
// Create stress test scenario
let world = setup_stress_test().await?;

// Add malicious agents
world.add_agent(Agent::new("attacker", FlashLoanAttacker));
world.add_agent(Agent::new("price-manipulator", PriceManipulator));

// Run attack scenarios
world.run_until(conditions_met).await?;

// Verify protocol safety
assert!(protocol_remains_solvent(&world));
}

Strategy Development

Develop and test trading strategies:

#![allow(unused)]
fn main() {
struct BacktestBehavior {
    strategy: TradingStrategy,
    performance: PerformanceTracker,
}

impl Behavior for BacktestBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        let signal = self.strategy.generate_signal(world).await?;
        
        if let Some(trade) = signal {
            let result = execute_trade(world, trade).await?;
            self.performance.record(result);
        }
        
        Ok(())
    }
}
}

Agent Lifecycle

Create → Initialize → Register Events → Execute → Cleanup

Creation

#![allow(unused)]
fn main() {
let agent = Agent::new("my-agent", MyBehavior::new());
}

Initialization

#![allow(unused)]
fn main() {
impl Behavior for MyBehavior {
    async fn init(&mut self, world: &World) -> Result<()> {
        // Setup state, deploy contracts, etc.
        self.contract = deploy_contract(world).await?;
        Ok(())
    }
}
}

Event Registration

#![allow(unused)]
fn main() {
impl Behavior for MyBehavior {
    fn events(&self) -> Vec<EventFilter> {
        vec![
            EventFilter::contract_event(self.contract, "Transfer"),
            EventFilter::contract_event(self.contract, "Approval"),
        ]
    }
}
}

Execution

#![allow(unused)]
fn main() {
impl Behavior for MyBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        // Main agent logic
        self.process_events(world).await?;
        self.update_state(world).await?;
        Ok(())
    }
}
}

Behavior Patterns

Reactive Behavior

Respond to events:

#![allow(unused)]
fn main() {
impl Behavior for ReactiveBehavior {
    async fn on_event(&mut self, event: Event) -> Result<()> {
        match event.name.as_str() {
            "Swap" => self.handle_swap(event).await?,
            "Mint" => self.handle_mint(event).await?,
            _ => {}
        }
        Ok(())
    }
}
}

Scheduled Behavior

Execute on schedule:

#![allow(unused)]
fn main() {
impl Behavior for ScheduledBehavior {
    fn schedule(&self) -> Schedule {
        Schedule::Every(Duration::from_secs(60)) // Every minute
    }
    
    async fn execute(&mut self, world: &World) -> Result<()> {
        // Periodic action
        self.rebalance_portfolio(world).await?;
        Ok(())
    }
}
}

Stateful Behavior

Maintain state across executions:

#![allow(unused)]
fn main() {
struct StatefulBehavior {
    state: AgentState,
    history: Vec<Action>,
}

impl Behavior for StatefulBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        // Use and update state
        let action = self.state.decide_action(world).await?;
        self.history.push(action.clone());
        self.execute_action(action, world).await?;
        Ok(())
    }
}
}

Messaging System

High-Level Messaging

#![allow(unused)]
fn main() {
use starkbiter_engine::messager::{Messager, Message};

// Create messager
let messager = Messager::new();

// Agent A subscribes
messager.subscribe("agent-a", callback).await?;

// Agent B publishes
messager.publish("agent-a", Message::Data(value)).await?;
}

Custom Messages

#![allow(unused)]
fn main() {
#[derive(Debug, Clone)]
enum CustomMessage {
    PriceUpdate(u64),
    TradeSignal { asset: String, action: Action },
    Alert(String),
}

// Send custom message
messager.publish("trader", CustomMessage::PriceUpdate(1000)).await?;
}

Error Handling

Behavior Errors

#![allow(unused)]
fn main() {
impl Behavior for MyBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        self.risky_operation(world)
            .await
            .map_err(|e| anyhow!("Agent failed: {}", e))?;
        Ok(())
    }
}
}

World-Level Error Handling

#![allow(unused)]
fn main() {
match world.run().await {
    Ok(_) => println!("Simulation completed successfully"),
    Err(e) => {
        eprintln!("Simulation failed: {}", e);
        world.dump_state().await?;
    }
}
}

Testing Agents

Unit Tests

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_agent_behavior() {
    let mut behavior = MyBehavior::new();
    let world = create_test_world().await;
    
    behavior.init(&world).await.unwrap();
    behavior.execute(&world).await.unwrap();
    
    assert_eq!(behavior.get_state(), expected_state);
}
}

Integration Tests

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_multi_agent_interaction() {
    let world = World::new(env);
    world.add_agent(Agent::new("agent-1", Behavior1));
    world.add_agent(Agent::new("agent-2", Behavior2));
    
    world.run_for_blocks(100).await.unwrap();
    
    let results = world.get_results();
    assert!(results.agents_interacted_correctly());
}
}

Performance Optimization

Parallel Agent Execution

#![allow(unused)]
fn main() {
// Agents execute in parallel when possible
world.set_execution_mode(ExecutionMode::Parallel);
world.run().await?;
}

Batch Operations

#![allow(unused)]
fn main() {
impl Behavior for BatchBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        // Batch multiple operations
        let operations = self.prepare_batch();
        world.execute_batch(operations).await?;
        Ok(())
    }
}
}

Best Practices

1. Keep Behaviors Focused

#![allow(unused)]
fn main() {
// Good: Single responsibility
struct LiquidatorBehavior;

// Avoid: Too many responsibilities
struct GodBehavior;  // Don't do this!
}

2. Use Composition

#![allow(unused)]
fn main() {
struct ComposedBehavior {
    price_oracle: PriceOracle,
    risk_manager: RiskManager,
    executor: TradeExecutor,
}
}

3. Handle Errors Gracefully

#![allow(unused)]
fn main() {
impl Behavior for RobustBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        match self.try_execute(world).await {
            Ok(_) => Ok(()),
            Err(e) => {
                log::error!("Execution failed: {}", e);
                self.recover().await?;
                Ok(())
            }
        }
    }
}
}

4. Log Extensively

#![allow(unused)]
fn main() {
impl Behavior for LoggingBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        log::info!("Starting execution");
        let result = self.do_work(world).await?;
        log::info!("Completed with result: {:?}", result);
        Ok(())
    }
}
}

Next Steps