Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation #13

Merged
merged 3 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ docs/
.env
*.lock

analysis/counter/*
45 changes: 22 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
# arbiter-template
# Arbiter Template

Minimal template for simulating contracts with arbiter.
Minimal template for simulating contracts with arbiter. This template is used by the `arbiter init` command when starting new simulations. This template provides a framework for performantly simulating Agent Based Models (ABM) with evm parity. In this model you can think of any things that happens as an action of an agent. Agents can own keys and externally owned accounts, they can interact with each other and they can interact with smart contracts.

## Usage
This repository has some example agents including a [`TokenAdmin`](src/agents/token_admin.rs), [`BlockAdmin`](src/agents/block_admin.rs), and [`CounterAgent`](src/agents/counter_agent.rs) which their own functionality and responsibilities. We also give an example of [how to parametarize your simulations](src/settings/mod.rs) with a configuration file containing different price paths and price path parameters. These can be played with to see how the simulation changes. Furthermore we provide an [api to batch simulations](src/simulations/mod.rs) and run them in parallel. This is useful for running many simulations with different parameters.

1. Clone this repository
### Prerequisites

```
git clone https://github.com/primitivefinance/arbiter-template.git
cd arbiter-template
```
- Rust programming language and Cargo package manager (latest stable version recommended)
- [Foundry](https://book.getfoundry.sh/getting-started/installation) is used behind the scenes to generate rust contract bindings. Make sure you have forge installed and up to date.

2. Install foundry
## Usage
1. Install arbiter

```
curl -L https://foundry.paradigm.xyz | bash
foundryup
``` bash
cargo install arbiter
```

3. Install forge libraries
2. Create arbiter project from this template

```
forge install
``` bash
arbiter init <name_of_project>
```

4. Generate bindings

```
forge bind --revert-strings debug -b src/bindings/ --module --overwrite
3. Run the project
```bash
cargo run simulate src/config/counter.toml
```

5. Run the project
## Documentation

```
cargo run
```
The documentation for the repository is primarily inline with the code. Cargo automatically can compile these into a browsable format. To view the documentation run the following command:

```bash
cargo doc --open
```
26 changes: 24 additions & 2 deletions src/agents/block_admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,33 @@ 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<RevmMiddleware>,

/// 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<Fixed>`] - The simulation configuration providing block timestep size.
///
/// # Returns
/// * [`Result<Self>`] - A result containing the new BlockAdmin or an error.
pub async fn new(environment: &Environment, config: &SimulationConfig<Fixed>) -> Result<Self> {
let client = RevmMiddleware::new(environment, "block_admin".into())?;
let timestep_size = config.block.timestep_size;
Expand All @@ -29,7 +47,12 @@ impl BlockAdmin {
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;
Expand All @@ -45,7 +68,6 @@ impl Agent for BlockAdmin {
self.update_block()?;
Ok(())
}

async fn startup(&mut self) -> Result<()> {
self.update_block()?;
Ok(())
Expand Down
26 changes: 20 additions & 6 deletions src/agents/counter_agent.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
use std::sync::Arc;

use arbiter_core::{middleware::RevmMiddleware, environment::Environment, bindings::arbiter_token::ArbiterToken};
use ethers::types::{Address, U256};
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<RevmMiddleware>,

// An instance of a deployed counter contract
pub counter: Counter<RevmMiddleware>,
}

impl CounterAgent {
/// Creates a new instance of a [`CounterAgent`].
///
/// # Arguments
/// * [`Environment`] - A reference to the environment that holds blockchain configuration.
///
/// # Returns
/// * [`Result<Self>`] - Result of CounterAgent creation, containing the agent or an error.
pub async fn new(environment: &Environment) -> Result<Self> {
let client = RevmMiddleware::new(environment, "counter_agent".into())?;
let counter = Counter::deploy(client.clone(), ())?.send().await?;

Ok(Self { client, counter})
Ok(Self { client, counter })
}

pub async fn increment (&self) -> Result<()> {
/// 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(())
}
Expand Down
16 changes: 15 additions & 1 deletion src/agents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::settings::parameters::Fixed;

/// 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.
Expand All @@ -29,33 +30,46 @@ pub trait Agent: Sync + Send {
Ok(())
}
}

/// A collection of agents that can be operated on collectively.
pub struct Agents(pub Vec<Box<dyn Agent>>);

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<Item = &mut Box<dyn Agent>> {
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(())
}
Expand Down
18 changes: 9 additions & 9 deletions src/agents/price_changer.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
use arbiter_core::math::{float_to_wad, Trajectories, GeometricBrownianMotion, StochasticProcess};
use crate::agents::*;
use crate::settings::{parameters::GBMParameters, SimulationConfig};
use arbiter_core::bindings::liquid_exchange::LiquidExchange;
use arbiter_core::environment::Environment;
use arbiter_core::math::{float_to_wad, GeometricBrownianMotion, StochasticProcess, Trajectories};
use arbiter_core::middleware::RevmMiddleware;
use ethers::utils::parse_ether;
use crate::settings::{SimulationConfig, parameters::GBMParameters};
use crate::agents::*;

/// The `PriceChanger` holds the data and has methods that allow it to update
/// the price of the `LiquidExchange`.
/// 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`.
/// The [`LiquidExchange`] contract with the admin `Client`.
pub liquid_exchange: LiquidExchange<RevmMiddleware>,

/// 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
/// 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
Expand Down Expand Up @@ -76,7 +76,7 @@ impl PriceChanger {
})
}

/// Update the price of the `LiquidExchange` contract to the next price in
/// 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];
Expand Down
18 changes: 17 additions & 1 deletion src/agents/token_admin.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
use std::sync::Arc;

use arbiter_core::{middleware::RevmMiddleware, environment::Environment, bindings::arbiter_token::ArbiterToken};
use arbiter_core::{
bindings::arbiter_token::ArbiterToken, 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<RevmMiddleware>,

/// The arbiter token X contract.
pub arbx: ArbiterToken<RevmMiddleware>,

/// The arbiter token Y contract.
pub arby: ArbiterToken<RevmMiddleware>,
}

impl TokenAdmin {
/// Creates a new [`TokenAdmin`] instance, deploying two ArbiterToken contracts.
///
/// # Arguments
/// * [`Environment`] - The simulation environment containing blockchain network configurations.
///
/// # Returns
/// * [`Result<Self>`] - The result of the operation, yielding a new [`TokenAdmin`] if successful.
pub async fn new(environment: &Environment) -> Result<Self> {
let client = RevmMiddleware::new(environment, "token_admin".into())?;
let decimals = 18_u8;
Expand Down
Loading
Loading