diff --git a/Cargo.toml b/Cargo.toml index d4e0f47..10eea5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,17 @@ [package] -name = "arbiter_template" +name = "arbiter-template" version = "0.1.0" edition = "2021" [dependencies] -arbiter-core = { version = "0.8.0" } -arbiter-bindings = { version = "0.1.0" } -RustQuant = { version = "0.0.38", features = ["seedable"] } +tracing = "0.1.40" +tracing-subscriber = "0.3.18" +arbiter-core = { version = "0.10.1" } +arbiter-macros = { version = "0.1.1" } +arbiter-engine = { version = "0.2.0" } tokio = { version = "1.34.0", features = ["full"] } ethers = { version = "2.0.10" } -serde_json = { version = "1.0.107" } serde = { version = "1.0.189", features = ["derive"] } anyhow = { version = "1.0.75" } async-trait = "0.1.74" -config = "0.13.3" clap = { version = "4.4.8", features = ["derive"] } \ No newline at end of file diff --git a/analysis/prices/output.json b/analysis/prices/output.json deleted file mode 100644 index e69de29..0000000 diff --git a/configs/example.toml b/configs/example.toml new file mode 100644 index 0000000..d1b5df6 --- /dev/null +++ b/configs/example.toml @@ -0,0 +1,2 @@ +[[my_agent]] +Incrementer = { max_number_of_times = 5 } diff --git a/contracts/Counter.sol b/contracts/ModifiedCounter.sol similarity index 65% rename from contracts/Counter.sol rename to contracts/ModifiedCounter.sol index f18f8af..253059c 100644 --- a/contracts/Counter.sol +++ b/contracts/ModifiedCounter.sol @@ -1,13 +1,15 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -contract Counter { +contract ModifiedCounter { uint256 public number; - event Incremented(uint256 number); + event NumberSet(uint256 newNumber); + event Incremented(uint256 newNumber); function setNumber(uint256 newNumber) public { number = newNumber; + emit NumberSet(newNumber); } function increment() public { diff --git a/src/agents/block_admin.rs b/src/agents/block_admin.rs deleted file mode 100644 index 5c56fd1..0000000 --- a/src/agents/block_admin.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::sync::Arc; - -use arbiter_core::environment::Environment; -use arbiter_core::middleware::RevmMiddleware; -use ethers::providers::Middleware; -use settings::SimulationConfig; - -use super::*; - -/// A structure representing a block admin agent. -/// This agent is responsible for updating the block number and timestamp. -#[derive(Clone)] -pub struct BlockAdmin { - /// A client to interface with arbiter's revm middleware. - /// You can think of this as the agents wallet or EOA. - pub client: Arc, - - /// The size of each timestep in the simulation, representing block time passage. - pub timestep_size: u64, - - /// The current simulated block timestamp. - pub block_timestamp: u64, - - /// The current simulated block number. - pub block_number: u64, -} - -impl BlockAdmin { - /// Creates a new BlockAdmin using the provided environment and simulation configuration. - /// - /// # Arguments - /// * [`Environment`] - The environment containing blockchain node information. - /// * [`SimulationConfig`] - The simulation configuration providing block timestep size. - /// - /// # Returns - /// * [`Result`] - A result containing the new BlockAdmin or an error. - pub async fn new(environment: &Environment, config: &SimulationConfig) -> Result { - let client = RevmMiddleware::new(environment, "block_admin".into())?; - let timestep_size = config.block.timestep_size; - let block_number = client.get_block_number().await?.as_u64(); - let block_timestamp = client.get_block_timestamp().await?.as_u64(); - - Ok(Self { - client, - timestep_size, - block_timestamp, - block_number, - }) - } - /// Updates the simulated block information. - /// - /// Increments the block number and calculates the new block timestamp based on the timestep size. - /// - /// # Returns - /// * [`Result<()>`] - A result indicating the success or failure of the operation. - pub fn update_block(&mut self) -> Result<()> { - self.block_number += 1; - self.block_timestamp = self.block_number * self.timestep_size; - self.client - .update_block(self.block_number, self.block_timestamp)?; - Ok(()) - } -} - -#[async_trait::async_trait] -impl Agent for BlockAdmin { - async fn step(&mut self) -> Result<()> { - self.update_block()?; - Ok(()) - } - async fn startup(&mut self) -> Result<()> { - self.update_block()?; - Ok(()) - } -} diff --git a/src/agents/counter_agent.rs b/src/agents/counter_agent.rs deleted file mode 100644 index a1b23c8..0000000 --- a/src/agents/counter_agent.rs +++ /dev/null @@ -1,49 +0,0 @@ -use arbiter_core::{environment::Environment, middleware::RevmMiddleware}; -use bindings::counter::Counter; -use std::sync::Arc; - -use super::*; - -/// A structure representing a counter agent. -/// This agent is responsible for incrementing the count on a counter contract. -#[derive(Clone)] -pub struct CounterAgent { - // A client to interface with arbiter's revm middleware - pub client: Arc, - - // An instance of a deployed counter contract - pub counter: Counter, -} - -impl CounterAgent { - /// Creates a new instance of a [`CounterAgent`]. - /// - /// # Arguments - /// * [`Environment`] - A reference to the environment that holds blockchain configuration. - /// - /// # Returns - /// * [`Result`] - Result of CounterAgent creation, containing the agent or an error. - pub async fn new(environment: &Environment) -> Result { - let client = RevmMiddleware::new(environment, "counter_agent".into())?; - let counter = Counter::deploy(client.clone(), ())?.send().await?; - - Ok(Self { client, counter }) - } - - /// Increments the counter in the smart contract. - /// - /// # Returns - /// * [`Result<()>`] - Result of the increment operation, indicating success or error. - pub async fn increment(&self) -> Result<()> { - self.counter.increment().send().await?.await?; - Ok(()) - } -} - -#[async_trait::async_trait] -impl Agent for CounterAgent { - async fn step(&mut self) -> Result<()> { - self.increment().await?; - Ok(()) - } -} diff --git a/src/agents/mod.rs b/src/agents/mod.rs deleted file mode 100644 index 909cd24..0000000 --- a/src/agents/mod.rs +++ /dev/null @@ -1,74 +0,0 @@ -use super::*; - -pub mod block_admin; -pub mod counter_agent; -pub mod price_changer; -pub mod token_admin; - -use std::marker::{Send, Sync}; - -/// Universal agent methods for interacting with the simulation environment or -/// loop. -/// Agents are expected to be both [`Send`] and [`Sync`]. -#[async_trait::async_trait] -pub trait Agent: Sync + Send { - /// Executed outside the main simulation loop. - async fn startup(&mut self) -> Result<()> { - Ok(()) - } - - /// Executed by each agent inside the main simulation loop. - /// Ordering is determined by placement in the simulation loop. - async fn step(&mut self) -> Result<()> { - Ok(()) - } - - /// Executed by each agent in a separate loop before the main loop. - async fn priority_step(&mut self) -> Result<()> { - Ok(()) - } -} -/// A collection of agents that can be operated on collectively. -pub struct Agents(pub Vec>); - -impl Agents { - /// Returns a mutable iterator over the agents. - /// This can be used to invoke methods on each agent individually. - pub fn iter_mut(&mut self) -> impl Iterator> { - self.0.iter_mut() - } -} - -impl Agents { - /// Constructs a new [`Agents`] collection. - /// This static method provides a way to create a new collection of agents. - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - Self(vec![]) - } - - /// Adds a new agent to the collection. - /// This method takes ownership of the agent and adds it to the collection. - #[allow(clippy::should_implement_trait)] - pub fn add(mut self, agent: impl Agent + 'static) -> Self { - self.0.push(Box::new(agent)); - self - } -} - -/// [`Agent`] trait implementation for a collection of agents. -/// This allows collective operations on the group of agents. -#[async_trait::async_trait] -impl Agent for Agents { - /// Implementation of the `step` method for the collection. - /// This allows the collection to forward the step action to each agent. - async fn step(&mut self) -> Result<()> { - Ok(()) - } - - /// Implementation of the `priority_step` method for the collection. - /// This allows the collection to forward the priority step action to each agent. - async fn priority_step(&mut self) -> Result<()> { - Ok(()) - } -} diff --git a/src/agents/price_changer.rs b/src/agents/price_changer.rs deleted file mode 100644 index 94abd13..0000000 --- a/src/agents/price_changer.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::agents::*; -use crate::settings::{GBMParameters, SimulationConfig}; -use arbiter_bindings::bindings::liquid_exchange::LiquidExchange; -use arbiter_core::environment::Environment; -use arbiter_core::{math::float_to_wad, middleware::RevmMiddleware}; -use ethers::utils::parse_ether; -use RustQuant::stochastics::{GeometricBrownianMotion, StochasticProcess, Trajectories}; - -/// The [`PriceChanger`] holds the data and has methods that allow it to update -/// the price of the [`LiquidExchange`]. -pub struct PriceChanger { - /// The path the price process takes. - pub trajectory: Trajectories, - - /// The [`LiquidExchange`] contract with the admin `Client`. - pub liquid_exchange: LiquidExchange, - - /// The index of the current price in the trajectory. - pub index: usize, -} - -impl PriceChanger { - /// Create a new [`PriceChanger`] with the given [`LiquidExchange`] contract - /// bound to the admin `Client`. The [`PriceChanger`] will use the - /// `OrnsteinUhlenbeck` process to generate a price trajectory with the - /// constants defined in `config.rs`. - /// Ornstein-Uhlenbeck processes are useful for modeling the price of stable - /// tokens. - pub async fn new( - environment: &Environment, - token_admin: &token_admin::TokenAdmin, - config: &SimulationConfig, - ) -> Result { - let client = RevmMiddleware::new(environment, "price_changer".into())?; - let liquid_exchange = LiquidExchange::deploy( - client, - ( - token_admin.arbx.address(), - token_admin.arby.address(), - float_to_wad(config.trajectory.initial_price), - ), - )? - .send() - .await?; - - token_admin - .mint( - liquid_exchange.address(), - parse_ether(100_000_000_000_u64).unwrap(), - parse_ether(100_000_000_000_u64).unwrap(), - ) - .await?; - - let trajectory_params = &config.trajectory; - let trajectory = match trajectory_params.process.as_str() { - "gbm" => { - let GBMParameters { drift, volatility } = config.gbm; - GeometricBrownianMotion::new(drift, volatility).seedable_euler_maruyama( - trajectory_params.initial_price, - trajectory_params.t_0, - trajectory_params.t_n, - trajectory_params.num_steps, - 1, - false, - trajectory_params.seed, - ) - } - _ => panic!("Invalid process type"), - }; - - Ok(Self { - trajectory, - liquid_exchange, - index: 1, /* start after the initial price since it is already set on contract - * deployment */ - }) - } - - /// Update the price of the [`LiquidExchange`] contract to the next price in - /// the trajectory and increment the index. - pub async fn update_price(&mut self) -> Result<()> { - let price = self.trajectory.paths[0][self.index]; - self.liquid_exchange - .set_price(arbiter_core::math::float_to_wad(price)) - .send() - .await? - .await?; - self.index += 1; - Ok(()) - } -} - -#[async_trait::async_trait] -impl Agent for PriceChanger { - async fn step(&mut self) -> Result<()> { - self.update_price().await?; - Ok(()) - } -} diff --git a/src/agents/token_admin.rs b/src/agents/token_admin.rs deleted file mode 100644 index 8534659..0000000 --- a/src/agents/token_admin.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::sync::Arc; - -use arbiter_bindings::bindings::arbiter_token::ArbiterToken; -use arbiter_core::{environment::Environment, middleware::RevmMiddleware}; -use ethers::types::{Address, U256}; - -use super::*; - -/// Manages the administrative operations for two types of tokens within the simulation environment. -/// The token admin is responsible for minting tokens to agents and other contracts. -#[derive(Clone)] -pub struct TokenAdmin { - /// The client interface for interacting with the [RevmMiddleware]. - pub client: Arc, - - /// The arbiter token X contract. - pub arbx: ArbiterToken, - - /// The arbiter token Y contract. - pub arby: ArbiterToken, -} - -impl TokenAdmin { - /// Creates a new [`TokenAdmin`] instance, deploying two ArbiterToken contracts. - /// - /// # Arguments - /// * [`Environment`] - The simulation environment containing blockchain network configurations. - /// - /// # Returns - /// * [`Result`] - The result of the operation, yielding a new [`TokenAdmin`] if successful. - pub async fn new(environment: &Environment) -> Result { - let client = RevmMiddleware::new(environment, "token_admin".into())?; - let decimals = 18_u8; - - let arbx = ArbiterToken::deploy( - client.clone(), - ("Arbiter Token X".to_string(), "arbx".to_string(), decimals), - )? - .send() - .await?; - let arby = ArbiterToken::deploy( - client.clone(), - ("Arbiter Token Y".to_string(), "arby".to_string(), decimals), - )? - .send() - .await?; - - Ok(Self { client, arbx, arby }) - } - - pub async fn mint(&self, to: Address, amount_x: U256, amount_y: U256) -> Result<()> { - self.arbx.mint(to, amount_x).send().await?.await?; - self.arby.mint(to, amount_y).send().await?.await?; - Ok(()) - } -} - -#[async_trait::async_trait] -impl Agent for TokenAdmin {} diff --git a/src/behaviors/incrementer.rs b/src/behaviors/incrementer.rs new file mode 100644 index 0000000..3fc567a --- /dev/null +++ b/src/behaviors/incrementer.rs @@ -0,0 +1,55 @@ +use std::sync::Arc; + +use anyhow::Result; +use arbiter_core::{events::stream_event, middleware::ArbiterMiddleware}; +use arbiter_engine::{ + machine::{Behavior, ControlFlow, EventStream}, + messager::Messager, +}; +use tracing::{debug, info}; + +use super::*; +use crate::bindings::modified_counter::{IncrementedFilter, ModifiedCounter}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Incrementer { + #[serde(default)] + curr_number_of_times: u64, + max_number_of_times: u64, + #[serde(skip)] + counter: Option>, +} + +#[async_trait::async_trait] +impl Behavior for Incrementer { + async fn startup( + &mut self, + client: Arc, + _messager: Messager, + ) -> Result> { + debug!("Incrementer starting up"); + let counter = ModifiedCounter::deploy(client.clone(), ())?.send().await?; + let stream = stream_event(counter.incremented_filter()); + counter.increment().send().await?.await?; + self.curr_number_of_times += 1; + let curr_number = counter.number().call().await?; + debug!("Incremented to: {}", curr_number); + self.counter = Some(counter); + Ok(stream) + } + + async fn process(&mut self, _event: IncrementedFilter) -> Result { + debug!("Incrementer processing event"); + let counter = self.counter.as_ref().unwrap(); + if self.curr_number_of_times < self.max_number_of_times { + counter.increment().send().await?.await?; + self.curr_number_of_times += 1; + let curr_number = counter.number().call().await?; + debug!("Incremented to: {}", curr_number); + Ok(ControlFlow::Continue) + } else { + info!("Incrementer done"); + return Ok(ControlFlow::Halt); + } + } +} diff --git a/src/behaviors/mod.rs b/src/behaviors/mod.rs new file mode 100644 index 0000000..09bbbd1 --- /dev/null +++ b/src/behaviors/mod.rs @@ -0,0 +1,12 @@ +use arbiter_engine::machine::{CreateStateMachine, Engine, StateMachine}; +use arbiter_macros::Behaviors; +use serde::{Deserialize, Serialize}; + +pub mod incrementer; + +use incrementer::Incrementer; + +#[derive(Debug, Serialize, Deserialize, Behaviors)] +pub enum Behaviors { + Incrementer(Incrementer), +} diff --git a/src/bindings/mod.rs b/src/bindings/mod.rs index 455a6a1..ddfa7a8 100644 --- a/src/bindings/mod.rs +++ b/src/bindings/mod.rs @@ -1,6 +1 @@ -#![allow(clippy::all)] -//! This module contains abigen! generated bindings for solidity contracts. -//! This is autogenerated code. -//! Do not manually edit these files. -//! These files may be overwritten by the codegen system at any time. -pub mod counter; +pub mod modified_counter; diff --git a/src/bindings/counter.rs b/src/bindings/modified_counter.rs similarity index 60% rename from src/bindings/counter.rs rename to src/bindings/modified_counter.rs index 936824b..f829a9f 100644 --- a/src/bindings/counter.rs +++ b/src/bindings/modified_counter.rs @@ -1,4 +1,4 @@ -pub use counter::*; +pub use modified_counter::*; /// This module was auto-generated with ethers-rs Abigen. /// More information at: #[allow( @@ -9,7 +9,7 @@ pub use counter::*; dead_code, non_camel_case_types )] -pub mod counter { +pub mod modified_counter { #[allow(deprecated)] fn __abi() -> ::ethers::core::abi::Abi { ::ethers::core::abi::ethabi::Contract { @@ -58,87 +58,107 @@ pub mod counter { },], ), ]), - events: ::core::convert::From::from([( - ::std::borrow::ToOwned::to_owned("Incremented"), - ::std::vec![::ethers::core::abi::ethabi::Event { - name: ::std::borrow::ToOwned::to_owned("Incremented"), - inputs: ::std::vec![::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned("number"), - kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), - indexed: false, + events: ::core::convert::From::from([ + ( + ::std::borrow::ToOwned::to_owned("Incremented"), + ::std::vec![::ethers::core::abi::ethabi::Event { + name: ::std::borrow::ToOwned::to_owned("Incremented"), + inputs: ::std::vec![::ethers::core::abi::ethabi::EventParam { + name: ::std::borrow::ToOwned::to_owned("newNumber"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + indexed: false, + },], + anonymous: false, + },], + ), + ( + ::std::borrow::ToOwned::to_owned("NumberSet"), + ::std::vec![::ethers::core::abi::ethabi::Event { + name: ::std::borrow::ToOwned::to_owned("NumberSet"), + inputs: ::std::vec![::ethers::core::abi::ethabi::EventParam { + name: ::std::borrow::ToOwned::to_owned("newNumber"), + kind: ::ethers::core::abi::ethabi::ParamType::Uint(256usize,), + indexed: false, + },], + anonymous: false, },], - anonymous: false, - },], - )]), + ), + ]), errors: ::std::collections::BTreeMap::new(), receive: false, fallback: false, } } - ///The parsed JSON ABI of the contract. - pub static COUNTER_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = + /// The parsed JSON ABI of the contract. + pub static MODIFIEDCOUNTER_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0]W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FEther sent to non-payable functi`D\x82\x01\x90\x81Ra7\xB7`\xF1\x1B`d\x83\x01R`\x84\x82\xFD[Pa\x028\x80a\0m`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0]W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FEther sent to non-payable functi`D\x82\x01\x90\x81Ra7\xB7`\xF1\x1B`d\x83\x01R`\x84\x82\xFD[P`\x046\x10a\0\x8EW`\x005`\xE0\x1C\x80c?\xB5\xC1\xCB\x14a\0\xF3W\x80c\x83\x81\xF5\x8A\x14a\x01\x08W\x80c\xD0\x9D\xE0\x8A\x14a\x01#W[`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`5`$\x82\x01R\x7FContract does not have fallback `D\x82\x01\x90\x81Rtnor receive functions`X\x1B`d\x83\x01R`\x84\x82\xFD[a\x01\x06a\x01\x016`\x04a\x01wV[`\0UV[\0[a\x01\x11`\0T\x81V[`@Q\x90\x81R` \x01`@Q\x80\x91\x03\x90\xF3[a\x01\x06`\0\x80T\x90\x80a\x015\x83a\x01\xDBV[\x91\x90PUP\x7F \xD8\xA6\xF5\xA6\x93\xF9\xD1\xD6'\xA5\x98\xE8\x82\x0FzU\xEEt\xC1\x83\xAA\x8F\x1A0\xE8\xD4\xE8\xDD\x9A\x8D\x84`\0T`@Qa\x01m\x91\x81R` \x01\x90V[`@Q\x80\x91\x03\x90\xA1V[`\0` \x82\x84\x03\x12\x15a\x01\xD4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FABI decoding: tuple data too sho`D\x82\x01Ra\x1C\x9D`\xF2\x1B`d\x82\x01R`\x84\x81\xFD[P5\x91\x90PV[`\0`\x01\x82\x01a\x01\xFBWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[P`\x01\x01\x90V\xFE\xA2dipfsX\"\x12 \xDB\xAA\xB4\x86\xE5\x17I\xF1\x17\x15\x88\x9D\xFE\x8A\x18<\xD6\x1E\xB3\xEB\x1E\xC2\x1AB\xF5\x93\xF7\xCE\xC8\x8F}qdsolcC\0\x08\x13\x003"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0]W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FEther sent to non-payable functi`D\x82\x01\x90\x81Ra7\xB7`\xF1\x1B`d\x83\x01R`\x84\x82\xFD[Pa\x02B\x80a\0m`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0]W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FEther sent to non-payable functi`D\x82\x01\x90\x81Ra7\xB7`\xF1\x1B`d\x83\x01R`\x84\x82\xFD[P`\x046\x10a\0\x8EW`\x005`\xE0\x1C\x80c?\xB5\xC1\xCB\x14a\0\xF3W\x80c\x83\x81\xF5\x8A\x14a\x01\x08W\x80c\xD0\x9D\xE0\x8A\x14a\x01#W[`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`5`$\x82\x01R\x7FContract does not have fallback `D\x82\x01\x90\x81Rtnor receive functions`X\x1B`d\x83\x01R`\x84\x82\xFD[a\x01\x06a\x01\x016`\x04a\x01\xB7V[a\x01+V[\0[a\x01\x11`\0T\x81V[`@Q\x90\x81R` \x01`@Q\x80\x91\x03\x90\xF3[a\x01\x06a\x01fV[`\0\x81\x90U`@Q\x81\x81R\x7F\x9E\xC8%Ii\xD1\x97N\xAC\x8Ct\xAF\xB0\xC05\x95\xB4\xFF\xE0\xA1\xD7\xAD\x8A\x7F\x82\xED1\xB9\xC8T%\x91\x90` \x01`@Q\x80\x91\x03\x90\xA1PV[`\0\x80T\x90\x80a\x01u\x83a\x02\x1BV[\x91\x90PUP\x7F \xD8\xA6\xF5\xA6\x93\xF9\xD1\xD6'\xA5\x98\xE8\x82\x0FzU\xEEt\xC1\x83\xAA\x8F\x1A0\xE8\xD4\xE8\xDD\x9A\x8D\x84`\0T`@Qa\x01\xAD\x91\x81R` \x01\x90V[`@Q\x80\x91\x03\x90\xA1V[`\0` \x82\x84\x03\x12\x15a\x02\x14W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FABI decoding: tuple data too sho`D\x82\x01Ra\x1C\x9D`\xF2\x1B`d\x82\x01R`\x84\x81\xFD[P5\x91\x90PV[`\0`\x01\x82\x01a\x02;WcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[P`\x01\x01\x90V"; /// The bytecode of the contract. - pub static COUNTER_BYTECODE: ::ethers::core::types::Bytes = + pub static MODIFIEDCOUNTER_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static(__BYTECODE); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0]W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FEther sent to non-payable functi`D\x82\x01\x90\x81Ra7\xB7`\xF1\x1B`d\x83\x01R`\x84\x82\xFD[P`\x046\x10a\0\x8EW`\x005`\xE0\x1C\x80c?\xB5\xC1\xCB\x14a\0\xF3W\x80c\x83\x81\xF5\x8A\x14a\x01\x08W\x80c\xD0\x9D\xE0\x8A\x14a\x01#W[`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`5`$\x82\x01R\x7FContract does not have fallback `D\x82\x01\x90\x81Rtnor receive functions`X\x1B`d\x83\x01R`\x84\x82\xFD[a\x01\x06a\x01\x016`\x04a\x01wV[`\0UV[\0[a\x01\x11`\0T\x81V[`@Q\x90\x81R` \x01`@Q\x80\x91\x03\x90\xF3[a\x01\x06`\0\x80T\x90\x80a\x015\x83a\x01\xDBV[\x91\x90PUP\x7F \xD8\xA6\xF5\xA6\x93\xF9\xD1\xD6'\xA5\x98\xE8\x82\x0FzU\xEEt\xC1\x83\xAA\x8F\x1A0\xE8\xD4\xE8\xDD\x9A\x8D\x84`\0T`@Qa\x01m\x91\x81R` \x01\x90V[`@Q\x80\x91\x03\x90\xA1V[`\0` \x82\x84\x03\x12\x15a\x01\xD4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FABI decoding: tuple data too sho`D\x82\x01Ra\x1C\x9D`\xF2\x1B`d\x82\x01R`\x84\x81\xFD[P5\x91\x90PV[`\0`\x01\x82\x01a\x01\xFBWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[P`\x01\x01\x90V\xFE\xA2dipfsX\"\x12 \xDB\xAA\xB4\x86\xE5\x17I\xF1\x17\x15\x88\x9D\xFE\x8A\x18<\xD6\x1E\xB3\xEB\x1E\xC2\x1AB\xF5\x93\xF7\xCE\xC8\x8F}qdsolcC\0\x08\x13\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0]W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FEther sent to non-payable functi`D\x82\x01\x90\x81Ra7\xB7`\xF1\x1B`d\x83\x01R`\x84\x82\xFD[P`\x046\x10a\0\x8EW`\x005`\xE0\x1C\x80c?\xB5\xC1\xCB\x14a\0\xF3W\x80c\x83\x81\xF5\x8A\x14a\x01\x08W\x80c\xD0\x9D\xE0\x8A\x14a\x01#W[`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`5`$\x82\x01R\x7FContract does not have fallback `D\x82\x01\x90\x81Rtnor receive functions`X\x1B`d\x83\x01R`\x84\x82\xFD[a\x01\x06a\x01\x016`\x04a\x01\xB7V[a\x01+V[\0[a\x01\x11`\0T\x81V[`@Q\x90\x81R` \x01`@Q\x80\x91\x03\x90\xF3[a\x01\x06a\x01fV[`\0\x81\x90U`@Q\x81\x81R\x7F\x9E\xC8%Ii\xD1\x97N\xAC\x8Ct\xAF\xB0\xC05\x95\xB4\xFF\xE0\xA1\xD7\xAD\x8A\x7F\x82\xED1\xB9\xC8T%\x91\x90` \x01`@Q\x80\x91\x03\x90\xA1PV[`\0\x80T\x90\x80a\x01u\x83a\x02\x1BV[\x91\x90PUP\x7F \xD8\xA6\xF5\xA6\x93\xF9\xD1\xD6'\xA5\x98\xE8\x82\x0FzU\xEEt\xC1\x83\xAA\x8F\x1A0\xE8\xD4\xE8\xDD\x9A\x8D\x84`\0T`@Qa\x01\xAD\x91\x81R` \x01\x90V[`@Q\x80\x91\x03\x90\xA1V[`\0` \x82\x84\x03\x12\x15a\x02\x14W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\"`$\x82\x01R\x7FABI decoding: tuple data too sho`D\x82\x01Ra\x1C\x9D`\xF2\x1B`d\x82\x01R`\x84\x81\xFD[P5\x91\x90PV[`\0`\x01\x82\x01a\x02;WcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[P`\x01\x01\x90V"; /// The deployed bytecode of the contract. - pub static COUNTER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = + pub static MODIFIEDCOUNTER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static(__DEPLOYED_BYTECODE); - pub struct Counter(::ethers::contract::Contract); - impl ::core::clone::Clone for Counter { + pub struct ModifiedCounter(::ethers::contract::Contract); + impl ::core::clone::Clone for ModifiedCounter { fn clone(&self) -> Self { Self(::core::clone::Clone::clone(&self.0)) } } - impl ::core::ops::Deref for Counter { + impl ::core::ops::Deref for ModifiedCounter { type Target = ::ethers::contract::Contract; fn deref(&self) -> &Self::Target { &self.0 } } - impl ::core::ops::DerefMut for Counter { + impl ::core::ops::DerefMut for ModifiedCounter { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } - impl ::core::fmt::Debug for Counter { + impl ::core::fmt::Debug for ModifiedCounter { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - f.debug_tuple(::core::stringify!(Counter)) + f.debug_tuple(::core::stringify!(ModifiedCounter)) .field(&self.address()) .finish() } } - impl Counter { - /// Creates a new contract instance with the specified `ethers` client at - /// `address`. The contract derefs to a `ethers::Contract` object. + impl ModifiedCounter { + /// Creates a new contract instance with the specified `ethers` client + /// at `address`. The contract derefs to a `ethers::Contract` + /// object. pub fn new>( address: T, client: ::std::sync::Arc, ) -> Self { Self(::ethers::contract::Contract::new( address.into(), - COUNTER_ABI.clone(), + MODIFIEDCOUNTER_ABI.clone(), client, )) } - /// Constructs the general purpose `Deployer` instance based on the provided constructor arguments and sends it. - /// Returns a new instance of a deployer that returns an instance of this contract after sending the transaction + /// Constructs the general purpose `Deployer` instance based on the + /// provided constructor arguments and sends it. Returns a new + /// instance of a deployer that returns an instance of this contract + /// after sending the transaction /// /// Notes: - /// - If there are no constructor arguments, you should pass `()` as the argument. + /// - If there are no constructor arguments, you should pass `()` as the + /// argument. /// - The default poll duration is 7 seconds. /// - The default number of confirmations is 1 block. /// /// /// # Example /// - /// Generate contract bindings with `abigen!` and deploy a new contract instance. + /// Generate contract bindings with `abigen!` and deploy a new contract + /// instance. /// - /// *Note*: this requires a `bytecode` and `abi` object in the `greeter.json` artifact. + /// *Note*: this requires a `bytecode` and `abi` object in the + /// `greeter.json` artifact. /// /// ```ignore /// # async fn deploy(client: ::std::sync::Arc) { @@ -156,21 +176,21 @@ pub mod counter { ::ethers::contract::ContractError, > { let factory = ::ethers::contract::ContractFactory::new( - COUNTER_ABI.clone(), - COUNTER_BYTECODE.clone().into(), + MODIFIEDCOUNTER_ABI.clone(), + MODIFIEDCOUNTER_BYTECODE.clone().into(), client, ); let deployer = factory.deploy(constructor_args)?; let deployer = ::ethers::contract::ContractDeployer::new(deployer); Ok(deployer) } - ///Calls the contract's `increment` (0xd09de08a) function + /// Calls the contract's `increment` (0xd09de08a) function pub fn increment(&self) -> ::ethers::contract::builders::ContractCall { self.0 .method_hash([208, 157, 224, 138], ()) .expect("method not found (this should never happen)") } - ///Calls the contract's `number` (0x8381f58a) function + /// Calls the contract's `number` (0x8381f58a) function pub fn number( &self, ) -> ::ethers::contract::builders::ContractCall { @@ -178,7 +198,7 @@ pub mod counter { .method_hash([131, 129, 245, 138], ()) .expect("method not found (this should never happen)") } - ///Calls the contract's `setNumber` (0x3fb5c1cb) function + /// Calls the contract's `setNumber` (0x3fb5c1cb) function pub fn set_number( &self, new_number: ::ethers::core::types::U256, @@ -187,23 +207,31 @@ pub mod counter { .method_hash([63, 181, 193, 203], new_number) .expect("method not found (this should never happen)") } - ///Gets the contract's `Incremented` event + /// Gets the contract's `Incremented` event pub fn incremented_filter( &self, ) -> ::ethers::contract::builders::Event<::std::sync::Arc, M, IncrementedFilter> { self.0.event() } + /// Gets the contract's `NumberSet` event + pub fn number_set_filter( + &self, + ) -> ::ethers::contract::builders::Event<::std::sync::Arc, M, NumberSetFilter> { + self.0.event() + } /// Returns an `Event` builder for all the events of this contract. pub fn events( &self, - ) -> ::ethers::contract::builders::Event<::std::sync::Arc, M, IncrementedFilter> + ) -> ::ethers::contract::builders::Event<::std::sync::Arc, M, ModifiedCounterEvents> { self.0 .event_with_filter(::core::default::Default::default()) } } - impl From<::ethers::contract::Contract> for Counter { + impl From<::ethers::contract::Contract> + for ModifiedCounter + { fn from(contract: ::ethers::contract::Contract) -> Self { Self::new(contract.address(), contract.client()) } @@ -222,9 +250,72 @@ pub mod counter { )] #[ethevent(name = "Incremented", abi = "Incremented(uint256)")] pub struct IncrementedFilter { - pub number: ::ethers::core::types::U256, + pub new_number: ::ethers::core::types::U256, + } + #[derive( + Clone, + ::ethers::contract::EthEvent, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash, + )] + #[ethevent(name = "NumberSet", abi = "NumberSet(uint256)")] + pub struct NumberSetFilter { + pub new_number: ::ethers::core::types::U256, + } + /// Container type for all of the contract's events + #[derive( + Clone, + ::ethers::contract::EthAbiType, + serde::Serialize, + serde::Deserialize, + Debug, + PartialEq, + Eq, + Hash, + )] + pub enum ModifiedCounterEvents { + IncrementedFilter(IncrementedFilter), + NumberSetFilter(NumberSetFilter), + } + impl ::ethers::contract::EthLogDecode for ModifiedCounterEvents { + fn decode_log( + log: &::ethers::core::abi::RawLog, + ) -> ::core::result::Result { + if let Ok(decoded) = IncrementedFilter::decode_log(log) { + return Ok(ModifiedCounterEvents::IncrementedFilter(decoded)); + } + if let Ok(decoded) = NumberSetFilter::decode_log(log) { + return Ok(ModifiedCounterEvents::NumberSetFilter(decoded)); + } + Err(::ethers::core::abi::Error::InvalidData) + } + } + impl ::core::fmt::Display for ModifiedCounterEvents { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + match self { + Self::IncrementedFilter(element) => ::core::fmt::Display::fmt(element, f), + Self::NumberSetFilter(element) => ::core::fmt::Display::fmt(element, f), + } + } + } + impl ::core::convert::From for ModifiedCounterEvents { + fn from(value: IncrementedFilter) -> Self { + Self::IncrementedFilter(value) + } + } + impl ::core::convert::From for ModifiedCounterEvents { + fn from(value: NumberSetFilter) -> Self { + Self::NumberSetFilter(value) + } } - ///Container type for all input parameters for the `increment` function with signature `increment()` and selector `0xd09de08a` + /// Container type for all input parameters for the `increment` function + /// with signature `increment()` and selector `0xd09de08a` #[derive( Clone, ::ethers::contract::EthCall, @@ -239,7 +330,8 @@ pub mod counter { )] #[ethcall(name = "increment", abi = "increment()")] pub struct IncrementCall; - ///Container type for all input parameters for the `number` function with signature `number()` and selector `0x8381f58a` + /// Container type for all input parameters for the `number` function with + /// signature `number()` and selector `0x8381f58a` #[derive( Clone, ::ethers::contract::EthCall, @@ -254,7 +346,8 @@ pub mod counter { )] #[ethcall(name = "number", abi = "number()")] pub struct NumberCall; - ///Container type for all input parameters for the `setNumber` function with signature `setNumber(uint256)` and selector `0x3fb5c1cb` + /// Container type for all input parameters for the `setNumber` function + /// with signature `setNumber(uint256)` and selector `0x3fb5c1cb` #[derive( Clone, ::ethers::contract::EthCall, @@ -271,7 +364,7 @@ pub mod counter { pub struct SetNumberCall { pub new_number: ::ethers::core::types::U256, } - ///Container type for all of the contract's call + /// Container type for all of the contract's call #[derive( Clone, ::ethers::contract::EthAbiType, @@ -282,12 +375,12 @@ pub mod counter { Eq, Hash, )] - pub enum CounterCalls { + pub enum ModifiedCounterCalls { Increment(IncrementCall), Number(NumberCall), SetNumber(SetNumberCall), } - impl ::ethers::core::abi::AbiDecode for CounterCalls { + impl ::ethers::core::abi::AbiDecode for ModifiedCounterCalls { fn decode( data: impl AsRef<[u8]>, ) -> ::core::result::Result { @@ -304,7 +397,7 @@ pub mod counter { Err(::ethers::core::abi::Error::InvalidData.into()) } } - impl ::ethers::core::abi::AbiEncode for CounterCalls { + impl ::ethers::core::abi::AbiEncode for ModifiedCounterCalls { fn encode(self) -> Vec { match self { Self::Increment(element) => ::ethers::core::abi::AbiEncode::encode(element), @@ -313,7 +406,7 @@ pub mod counter { } } } - impl ::core::fmt::Display for CounterCalls { + impl ::core::fmt::Display for ModifiedCounterCalls { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { Self::Increment(element) => ::core::fmt::Display::fmt(element, f), @@ -322,22 +415,23 @@ pub mod counter { } } } - impl ::core::convert::From for CounterCalls { + impl ::core::convert::From for ModifiedCounterCalls { fn from(value: IncrementCall) -> Self { Self::Increment(value) } } - impl ::core::convert::From for CounterCalls { + impl ::core::convert::From for ModifiedCounterCalls { fn from(value: NumberCall) -> Self { Self::Number(value) } } - impl ::core::convert::From for CounterCalls { + impl ::core::convert::From for ModifiedCounterCalls { fn from(value: SetNumberCall) -> Self { Self::SetNumber(value) } } - ///Container type for all return fields from the `number` function with signature `number()` and selector `0x8381f58a` + /// Container type for all return fields from the `number` function with + /// signature `number()` and selector `0x8381f58a` #[derive( Clone, ::ethers::contract::EthAbiType, diff --git a/src/config/counter.toml b/src/config/counter.toml deleted file mode 100644 index 0647caa..0000000 --- a/src/config/counter.toml +++ /dev/null @@ -1,28 +0,0 @@ -simulation = "Counter" -output_directory = "analysis/counter" -output_file_name = "output" - -[trajectory] -# The type of price process to use. -process = "gbm" -# The number of steps in the process. -num_steps = 36500 -# Starting seed for the price processes -seed = 10 -# The number of distinct paths to use -num_paths = 10 -# The initial price of the asset. -initial_price = 1.0 -# The start time of the process. -t_0 = 0.0 -# The end time of the process. -t_n = 10.0 - -[gbm] -# The drift of the process. -drift = 0.0 -# The volatility of the process. -volatility = 0.5 - -[block] -timestep_size = 15 diff --git a/src/config/gbm.toml b/src/config/gbm.toml deleted file mode 100644 index b104276..0000000 --- a/src/config/gbm.toml +++ /dev/null @@ -1,28 +0,0 @@ -simulation = "SimulatedPricePath" -output_directory = "analysis/prices" -output_file_name = "output" - -[trajectory] -# The type of price process to use. -process = "gbm" -# The number of steps in the process. -num_steps = 36500 -# Starting seed for the price processes -seed = 10 -# The number of distinct paths to use -num_paths = 10 -# The initial price of the asset. -initial_price = 1.0 -# The start time of the process. -t_0 = 0.0 -# The end time of the process. -t_n = 10.0 - -[gbm] -# The drift of the process. -drift = 0.0 -# The volatility of the process. -volatility = 0.5 - -[block] -timestep_size = 15 diff --git a/src/main.rs b/src/main.rs index 2300015..ed16fb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,92 +1,26 @@ -use std::time::Instant; - -use anyhow::Result; -use clap::{ArgAction, CommandFactory, Parser, Subcommand}; - -pub mod agents; +pub mod behaviors; pub mod bindings; -pub mod settings; -pub mod simulations; - -/// Represents command-line arguments passed to this binary. -#[derive(Parser)] -#[clap(name = "Template")] -#[clap(version = env!("CARGO_PKG_VERSION"))] -#[clap(about = "Simulation driven development.", long_about = None)] -#[clap(author)] -struct Args { - /// Defines the subcommand to execute. - #[command(subcommand)] - command: Option, - #[clap(short, long, global = true, required = false, action = ArgAction::Count, value_parser( - clap::value_parser!(u8)))] - verbose: Option, -} +use behaviors::Behaviors; -/// Defines available subcommands for the `Arbiter` tool. -#[derive(Subcommand)] -enum Commands { - /// Represents the `Bind` subcommand. - Simulate { - #[clap(index = 1, default_value = "src/config/")] - config_path: String, - }, -} - -/// The entry point for the simulation tool. -/// -/// This binary provides a command-line interface for the simulation-driven development. -/// It allows users to run simulations by specifying configuration paths, with detailed command-line -/// feedback provided through the `clap` crate. -/// -/// # Usage -/// Run the binary without arguments to see available commands and options. -/// Example usage for running simulations: +/// To run this example, you can do the following from the `arbiter/` directory: +/// ```sh +/// cargo run --example project simulate examples/project/configs/example.toml /// ``` -/// $ cargo run simulate [path_to_config] +/// If you would like to see more detailed logs, you can run the following: +/// ```sh +/// cargo run --example project simulate examples/project/configs/example.toml -vvv /// ``` +/// to get `debug` level logs. /// -/// By default, if no configuration path is provided, it will read from "src/config/". -/// -/// These simulations are performed in Arbiter's in memory revm instance and with the exposed RevmMiddleware. -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - - match &args.command { - Some(Commands::Simulate { config_path }) => { - println!("Reading from config path: {}", config_path); - let start = Instant::now(); - // This is the entry point for the simulation - let files = read_toml_file(config_path)?; - println!("files: {:?}", files); - let simulation = settings::SimulationConfig::new(files.clone())?; - println!("simulation: {:?}", simulation); - simulations::SimulationType::run(simulation).await.unwrap(); - let duration = start.elapsed(); - println!("Total duration of simulations: {:?}", duration); - } - None => Args::command().print_long_help()?, - } - Ok(()) -} - -// Function to read a .toml file and return its path -fn read_toml_file(file: &str) -> Result { - if is_toml_file(file) { - Ok(file.to_string()) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "Not a TOML file", - )) - } -} - -// Helper function to check if a file is a .toml file -fn is_toml_file(file: &str) -> bool { - std::path::Path::new(file) - .extension() - .map_or(false, |ext| ext == "toml") -} +/// By running +/// ```sh +/// cargo run --example project +/// ``` +/// you will get the `--help` message for the project. +#[arbiter_macros::main( + name = "ExampleArbiterProject", + about = "Our example to get you started.", + behaviors = Behaviors +)] +pub async fn main() {} diff --git a/src/settings/mod.rs b/src/settings/mod.rs deleted file mode 100644 index 33987d6..0000000 --- a/src/settings/mod.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::simulations::SimulationType; -use config::{Config, ConfigError}; -use serde::{Deserialize, Serialize}; - -/// Defines the configuration for a simulation. -/// -/// This struct holds all the necessary parameters and configurations needed to run a simulation. -/// It encompasses several sub-configurations such as `TrajectoryParameters` and `GBMParameters`. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SimulationConfig { - /// The type of simulation to run, defined by an enum `SimulationType`. - pub simulation: SimulationType, - - /// Directory where the simulation output will be stored. - pub output_directory: String, - - /// Name of the file where the simulation results will be written. - pub output_file_name: String, - - /// Parameters specific to the trajectory of the simulation. - pub trajectory: TrajectoryParameters, - - /// Parameters specific to the Geometric Brownian Motion (GBM) if applicable. - pub gbm: GBMParameters, - - /// Parameters related to block configurations. - pub block: BlockParameters, -} - -impl SimulationConfig { - /// Creates a new `SimulationConfig` instance from a configuration file. - /// - /// Reads the specified configuration file and deserializes it into a `SimulationConfig` object. - /// The `config_path` is the path to the configuration file in question. - pub fn new(config_path: String) -> Result { - let s = Config::builder() - .add_source(config::File::with_name(&config_path)) - .build()?; - s.try_deserialize() - } -} - -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] -pub struct BlockParameters { - pub timestep_size: u64, -} - -/// Defines parameters for a trajectory in the simulation. -/// -/// Contains information like initial price, start and end times, -/// and number of steps and paths in the simulation. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct TrajectoryParameters { - /// The name. - pub process: String, - - /// The initial price of the asset. - pub initial_price: f64, - - /// The start time of the process. - pub t_0: f64, - - /// The end time of the process. - pub t_n: f64, - - /// The number of steps in the process. - pub num_steps: usize, - - /// The number of paths in the process. - pub num_paths: usize, - - /// The seed for the process. - pub seed: u64, - - /// The tag for the output file. - pub output_tag: Option, -} - -/// Contains the parameters for the Geometric Brownian Motion (GBM) process. -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] -pub struct GBMParameters { - // The drift of the process. - pub drift: f64, - - // The volatility of the process. - pub volatility: f64, -} diff --git a/src/simulations/counter.rs b/src/simulations/counter.rs deleted file mode 100644 index 3885ace..0000000 --- a/src/simulations/counter.rs +++ /dev/null @@ -1,43 +0,0 @@ -use arbiter_core::{ - data_collection::EventLogger, - environment::builder::{BlockSettings, EnvironmentBuilder}, -}; - -use super::*; -use crate::{ - agents::{block_admin::BlockAdmin, counter_agent::CounterAgent, Agents}, - settings::SimulationConfig, -}; - -/// Asynchronously sets up a `Counter` simulation using the provided configuration. -/// -/// This function prepares the environment, initializes the `BlockAdmin` and `CounterAgent`, -/// logs events, and returns a `Simulation` with the configured agents, steps, and environment. -/// -/// # Arguments -/// -/// * `config` - The configuration for the simulation based on `SimulationConfig`. -/// -/// # Returns -/// -/// * A `Result` containing the fully initialized `Simulation` or an error if any step fails. -pub async fn setup(config: SimulationConfig) -> Result { - let environment = EnvironmentBuilder::new() - .block_settings(BlockSettings::UserControlled) - .build(); - - let block_admin = BlockAdmin::new(&environment, &config).await?; - let counter_agent = CounterAgent::new(&environment).await?; - - EventLogger::builder() - .directory(config.output_directory) - .file_name(config.output_file_name) - .add(counter_agent.counter.events(), "counter") - .run()?; - - Ok(Simulation { - agents: Agents::new().add(block_admin).add(counter_agent), - steps: config.trajectory.num_steps, - environment, - }) -} diff --git a/src/simulations/mod.rs b/src/simulations/mod.rs deleted file mode 100644 index cd01b33..0000000 --- a/src/simulations/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -/// Arbiter Simulation module for handling different types of simulations. -/// -/// This module provides structs and functions for executing and managing -/// various types of simulations, including counter simulations and price path simulations. -use arbiter_core::environment::Environment; -use serde::{Deserialize, Serialize}; -use std::sync::Arc; - -use crate::agents::Agents; - -pub mod counter; -pub mod price_path_simulation; - -use crate::settings::SimulationConfig; -use anyhow::Result; -use tokio::runtime::Builder; - -/// Represents the main Simulation structure. -/// -/// This struct encapsulates agents, steps, and the environment needed -/// for a simulation. -pub struct Simulation { - pub agents: Agents, - pub steps: usize, - environment: Environment, -} - -/// Defines the types of simulations available. -/// -/// The `SimulationType` enum provides an easy way to specify and differentiate -/// between different types of simulations, such as `SimulatedPricePath` and `Counter`. -/// If you wanted to add a simulation you would add it here to this enum -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub enum SimulationType { - SimulatedPricePath, - Counter, -} - -impl SimulationType { - /// Asynchronously runs the specified simulation type based on the provided configuration. - /// - /// This function matches on the `SimulationType` to determine which simulation setup to use, - /// then executes the chosen simulation. - pub async fn run(config: SimulationConfig) -> Result<()> { - let simulation = match config.simulation { - SimulationType::SimulatedPricePath => { - price_path_simulation::setup(config.clone()).await? - } - SimulationType::Counter => counter::setup(config.clone()).await?, - }; - match looper(simulation.agents, simulation.steps).await { - Result::Ok(_) => { - simulation.environment.stop()?; - Ok(()) - } - Err(e) => { - simulation.environment.stop()?; - Err(e) - } - } - } -} - -/// Executes a batch of simulations based on the provided configuration path. -/// -/// This function sets up multiple simulations to run in parallel, manages available resources using a semaphore, -/// and handles any errors that arise during execution. -pub fn batch(config_paths: Vec) -> Result<()> { - // Create a multi-threaded runtime - let rt = Builder::new_multi_thread().build()?; - - let mut configs = vec![]; - for path in config_paths { - configs.push(SimulationConfig::new(path)?); - } - - rt.block_on(async { - let mut handles = vec![]; - let errors = Arc::new(tokio::sync::Mutex::new(vec![])); - - for config in configs { - let errors_clone = errors.clone(); - handles.push(tokio::spawn(async move { - let result = SimulationType::run(config).await; - if let Err(e) = result { - let mut errors_clone_lock = errors_clone.lock().await; - errors_clone_lock.push(e); - } - })); - } - - for handle in handles { - handle.await?; - } - - Ok(()) - }) -} - -/// Asynchronously loops through agents and performs the steps for each agent. -/// -/// This function starts each agent, then performs priority steps and regular steps -/// for a given number of iterations. -pub async fn looper(mut agents: Agents, steps: usize) -> Result<()> { - for agent in agents.iter_mut() { - agent.startup().await?; - } - - for _ in 0..steps { - for agent in agents.iter_mut() { - agent.priority_step().await?; - } - - for agent in agents.iter_mut() { - agent.step().await?; - } - } - - Ok(()) -} diff --git a/src/simulations/price_path_simulation.rs b/src/simulations/price_path_simulation.rs deleted file mode 100644 index c9747e4..0000000 --- a/src/simulations/price_path_simulation.rs +++ /dev/null @@ -1,47 +0,0 @@ -use arbiter_core::{ - data_collection::EventLogger, - environment::builder::{BlockSettings, EnvironmentBuilder}, -}; - -use super::*; -use crate::{ - agents::{ - block_admin::BlockAdmin, price_changer::PriceChanger, token_admin::TokenAdmin, Agents, - }, - settings::SimulationConfig, -}; - -/// Asynchronously sets up a `SimulatedPricePath` simulation using the provided configuration. -/// -/// This function prepares the environment, initializes various agents including -/// `BlockAdmin`, `TokenAdmin`, and `PriceChanger`, logs events, and then returns a `Simulation` -/// object which houses the configured agents, steps, and the environment. -/// -/// # Arguments -/// -/// * `config` - The configuration for the simulation based on `SimulationConfig`. -/// -/// # Returns -/// -/// * A `Result` containing the fully initialized `Simulation` or an error if any step of the setup fails. -pub async fn setup(config: SimulationConfig) -> Result { - let environment = EnvironmentBuilder::new() - .block_settings(BlockSettings::UserControlled) - .build(); - - let block_admin = BlockAdmin::new(&environment, &config).await?; - let token_admin = TokenAdmin::new(&environment).await?; - let price_changer = PriceChanger::new(&environment, &token_admin, &config).await?; - - EventLogger::builder() - .directory(config.output_directory) - .file_name(config.output_file_name) - .add(price_changer.liquid_exchange.events(), "lex") - .run()?; - - Ok(Simulation { - agents: Agents::new().add(price_changer).add(block_admin), - steps: config.trajectory.num_steps, - environment, - }) -}