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

Behaviors

Behaviors define what agents do in a simulation. They are the core logic that determines how agents interact with the blockchain, respond to events, and communicate with other agents.

Overview

A Behavior is a trait that defines:

  • How an agent initializes
  • How it responds to events
  • How it executes periodic actions
  • How it processes messages

The Behavior Trait

#![allow(unused)]
fn main() {
pub trait Behavior: Send + Sync {
    /// Initialize the behavior
    async fn init(&mut self, world: &World) -> Result<()> {
        Ok(())
    }
    
    /// Main execution logic
    async fn execute(&mut self, world: &World) -> Result<()>;
    
    /// Handle blockchain events
    async fn on_event(&mut self, event: Event) -> Result<()> {
        Ok(())
    }
    
    /// Handle messages from other agents
    async fn on_message(&mut self, message: Message) -> Result<()> {
        Ok(())
    }
    
    /// Define which events to subscribe to
    fn events(&self) -> Vec<EventFilter> {
        vec![]
    }
}
}

Creating Behaviors

Simple Behavior

#![allow(unused)]
fn main() {
struct SimpleBehavior {
    counter: u64,
}

impl Behavior for SimpleBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        self.counter += 1;
        println!("Executed {} times", self.counter);
        Ok(())
    }
}
}

Event-Driven Behavior

#![allow(unused)]
fn main() {
struct EventDrivenBehavior {
    contract_address: Felt,
}

impl Behavior for EventDrivenBehavior {
    fn events(&self) -> Vec<EventFilter> {
        vec![
            EventFilter::contract_event(self.contract_address, "Transfer"),
        ]
    }
    
    async fn on_event(&mut self, event: Event) -> Result<()> {
        match event.name.as_str() {
            "Transfer" => self.handle_transfer(event).await?,
            _ => {}
        }
        Ok(())
    }
}
}

Stateful Behavior

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

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

Behavior Patterns

The Trading Bot

#![allow(unused)]
fn main() {
struct TradingBotBehavior {
    strategy: Box<dyn TradingStrategy>,
    portfolio: Portfolio,
}

impl Behavior for TradingBotBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        // Get market data
        let prices = self.fetch_prices(world).await?;
        
        // Generate signal
        let signal = self.strategy.analyze(&prices, &self.portfolio).await?;
        
        // Execute if signal is strong enough
        if signal.strength > 0.8 {
            self.execute_trade(world, signal.trade).await?;
        }
        
        Ok(())
    }
}
}

The Liquidator

#![allow(unused)]
fn main() {
struct LiquidatorBehavior {
    lending_protocol: ContractAddress,
    min_profit: u64,
}

impl Behavior for LiquidatorBehavior {
    fn events(&self) -> Vec<EventFilter> {
        vec![
            EventFilter::contract_event(self.lending_protocol, "Borrow"),
            EventFilter::contract_event(self.lending_protocol, "PriceUpdate"),
        ]
    }
    
    async fn on_event(&mut self, event: Event) -> Result<()> {
        // Check for liquidation opportunities
        let positions = self.get_unhealthy_positions(event).await?;
        
        for position in positions {
            if let Some(profit) = self.calculate_profit(&position).await? {
                if profit > self.min_profit {
                    self.liquidate(position).await?;
                }
            }
        }
        
        Ok(())
    }
}
}

The Market Maker

#![allow(unused)]
fn main() {
struct MarketMakerBehavior {
    pool: ContractAddress,
    spread: f64,
    inventory: Inventory,
}

impl Behavior for MarketMakerBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        // Update quotes
        let mid_price = self.get_mid_price(world).await?;
        let bid = mid_price * (1.0 - self.spread);
        let ask = mid_price * (1.0 + self.spread);
        
        // Place orders
        self.place_limit_order(world, Side::Buy, bid).await?;
        self.place_limit_order(world, Side::Sell, ask).await?;
        
        // Rebalance inventory
        self.rebalance_if_needed(world).await?;
        
        Ok(())
    }
}
}

The Oracle

#![allow(unused)]
fn main() {
struct OracleBehavior {
    price_feeds: Vec<PriceFeed>,
}

impl Behavior for OracleBehavior {
    async fn on_message(&mut self, msg: Message) -> Result<()> {
        match msg {
            Message::PriceRequest { asset } => {
                let price = self.fetch_price(&asset).await?;
                self.respond(Message::PriceResponse { asset, price }).await?;
            }
            _ => {}
        }
        Ok(())
    }
    
    async fn execute(&mut self, world: &World) -> Result<()> {
        // Periodically update prices
        for feed in &self.price_feeds {
            let price = feed.fetch_latest().await?;
            world.broadcast(Message::PriceUpdate {
                asset: feed.asset.clone(),
                price,
            }).await?;
        }
        Ok(())
    }
}
}

Composing Behaviors

Behavior Composition

Combine multiple behaviors:

#![allow(unused)]
fn main() {
struct ComposedBehavior {
    monitor: MonitoringBehavior,
    executor: ExecutionBehavior,
    reporter: ReportingBehavior,
}

impl Behavior for ComposedBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        // Execute in sequence
        self.monitor.execute(world).await?;
        self.executor.execute(world).await?;
        self.reporter.execute(world).await?;
        Ok(())
    }
}
}

Conditional Behavior

Execute behaviors conditionally:

#![allow(unused)]
fn main() {
impl Behavior for ConditionalBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        if self.should_trade(world).await? {
            self.trading_behavior.execute(world).await?;
        } else {
            self.monitoring_behavior.execute(world).await?;
        }
        Ok(())
    }
}
}

Testing Behaviors

Unit Tests

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

Mock World

#![allow(unused)]
fn main() {
struct MockWorld {
    // Minimal world for testing
}

impl MockWorld {
    fn new() -> Self {
        // Create test environment
        Self {}
    }
}
}

Best Practices

1. Keep Behaviors Focused

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

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

2. Make Behaviors Reusable

#![allow(unused)]
fn main() {
struct ReusableBehavior<S: Strategy> {
    strategy: S,
}

// Can be used with different strategies
let behavior1 = ReusableBehavior { strategy: ConservativeStrategy };
let behavior2 = ReusableBehavior { strategy: AggressiveStrategy };
}

3. Handle Errors Gracefully

#![allow(unused)]
fn main() {
impl Behavior for RobustBehavior {
    async fn execute(&mut self, world: &World) -> Result<()> {
        self.try_execute(world).await.or_else(|e| {
            log::error!("Execution failed: {}", e);
            self.fallback(world)
        })
    }
}
}

4. Add Logging

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

Examples

See the examples for complete behavior implementations.

Next Steps