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

Waylon/bin docs #417

Merged
merged 6 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@ Arbiter can be used for:
- investigating risk, capital efficiency, rebalancing strategies, and portfolio replication (or performance). (LPs, funds, quants, traders)
- Engineering and testing new financial products built on top of more primitive financial products (DeFi firms and academics)

## Installation

### Build from source

```bash
git clone https://github.com/primitivefinance/arbiter.git
cd arbiter
cargo install --path .
```

## CLI

```bash
arbiter init your-project-name
cd your-project-name
arbiter bind
cargo run
```


## Generating Docs

To see the documentation for Arbiter, after cloning the repo, you can run:
Expand Down
20 changes: 16 additions & 4 deletions bin/bind.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
#![warn(missing_docs, unsafe_code)]

#![warn(missing_docs)]
use std::process::Command;

pub(crate) fn bind_forge() -> std::io::Result<()> {
/// Runs the `forge` command-line tool to generate bindings.
///
/// This function attempts to execute the external command `forge` with the
/// provided arguments to generate necessary bindings. The bindings are stored
/// in the `arbiter/src/bindings/` directory, and existing bindings will be overwritten.
/// The function wraps the forge command to generate bindings as a module to a specific destination.
///
/// # Returns
///
/// * `Ok(())` if the `forge` command successfully generates the bindings.
/// * `Err(std::io::Error)` if the command execution fails or if there's an error
/// in generating the bindings. This can also include if the `forge` tool is not installed.

pub(crate) fn forge_bind() -> std::io::Result<()> {
let output = Command::new("forge")
.arg("bind")
.arg("--revert-strings")
.arg("debug")
.arg("-b")
.arg("arbiter/src/bindings/")
.arg("src/bindings/")
.arg("--module")
.arg("--overwrite")
.output()?;
Expand Down
197 changes: 54 additions & 143 deletions bin/init.rs
Original file line number Diff line number Diff line change
@@ -1,149 +1,60 @@
#![warn(missing_docs, unsafe_code)]

use std::{fs, io::Write, path::Path};

use quote::quote;

pub(crate) fn create_simulation(simulation_name: &str) -> std::io::Result<()> {
let main = quote! {
mod simulations;

fn main() {
let _ = simulations::testsim::run();
}
use std::{env, io, process::Command};

/// Initializes a new Arbiter project from a template.
///
/// This function does the following:
/// 1. Clones the `arbiter-template` from GitHub into a new directory named after the provided project name.
/// Template link is here https://github.com/primitivefinance/arbiter-template
/// 2. Changes the current directory to the cloned project.
/// 3. Executes the `forge install` command.
///
/// If any of the steps fail, an error is logged and an `io::Error` is returned to the caller.
///
/// # Arguments
///
/// * `name` - The name of the new project. This will also be the name of the directory
/// where the project is initialized.
///
/// # Returns
///
/// Returns an `io::Result<()>` indicating the success or failure of the initialization.
/// Failure can be due to reasons like:
/// - Network issues or repository being unavailable leading to git clone failure.
/// - The `forge install` command failing.

pub(crate) fn init_project(name: &str) -> io::Result<()> {
let status = Command::new("git")
.arg("clone")
.arg("https://github.com/primitivefinance/arbiter-template.git")
.arg(name)
.status()?;

if !status.success() {
println!("Failed to clone the repository.");
return Err(io::Error::new(
io::ErrorKind::Other,
"Failed to clone the repository.",
));
}
.to_string();

let toml = quote! {
[package]
name = "arbitersim"
version = "0.1.0"
edition = "2021"

[[bin]]
name = {simulation_name}
path = "arbiter/src/main.rs"

// See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
revm_middleware = {{ git = "https://github.com/primitivefinance/arbiter", package = "revm-middleware" }}
env::set_current_dir(name)?;
let install_output = Command::new("forge").arg("install").output()?;

if install_output.status.success() {
let output_str = String::from_utf8_lossy(&install_output.stdout);
println!("Command output: {}", output_str);
} else {
let err_str = String::from_utf8_lossy(&install_output.stderr);
println!("Command failed, error: {}, is forge installed?", err_str);
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Command failed",
));
}
.to_string();

let mod_rs = quote! {
use std::error::Error;

pub fn run() -> Result<(), Box<dyn Error>> {
todo!()
}
}
.to_string();

let startup = quote! {
pub(crate) fn run(manager: &mut SimulationManager) -> Result<(), Box<dyn Error>> {
let weth_address = manager.deployed_contracts.get("weth").unwrap().address;
deploy_contracts(manager, weth_address)?;
let liquid_exchange_xy = manager
.deployed_contracts
.get("liquid_exchange_xy")
.unwrap();
let address = B160::from_low_u64_be(2);
let event_filters = vec![SimulationEventFilter::new(
liquid_exchange_xy,
"PriceChange",
)];
let arbitrageur = SimpleArbitrageur::new(
"arbitrageur",
event_filters,
U256::from(997_000_000_000_000_000u128).into(),
);
manager
.activate_agent(AgentType::SimpleArbitrageur(arbitrageur), address)
.unwrap();

mint(
&manager.deployed_contracts,
manager.agents.get("admin").unwrap(),
manager.agents.get("arbitrageur").unwrap(),
)?;
approve(
manager.agents.get("admin").unwrap(),
manager.agents.get("arbitrageur").unwrap(),
&manager.deployed_contracts,
)?;

allocate(
manager.agents.get("admin").unwrap(),
&manager.deployed_contracts,
)?;

Ok(())
}
pub fn deploy() {
todo!()
}

pub fn mint() {
todo!()
}

pub fn approve() {
todo!()
}

pub fn allocate() {
todo!()
}
}
.to_string();

// Create a directory
fs::create_dir_all("arbiter")?;

// Create a subdirectory

let src_path = Path::new("arbiter").join("src");
fs::create_dir_all(&src_path)?;

let bindings_path = src_path.join("bindings");
fs::create_dir_all(bindings_path)?;

let simulations_path = src_path.join("simulations");
fs::create_dir_all(&simulations_path)?;

let sim = simulations_path.join(simulation_name);
fs::create_dir_all(&sim)?;

// Create a file in the subdirectory
let file_path = Path::new(".").join("Cargo.toml");
let mut file = fs::File::create(file_path)?;
let toml_token = quote! {#toml};
write!(file, "{}", toml_token)?;

let file_path = simulations_path.join("mod.rs");
let mut file = fs::File::create(file_path)?;
let mod_token = quote! {
pub mod #simulation_name;
};
write!(file, "{}", mod_token)?;

let file_path = sim.join("mod.rs");
let mut file = fs::File::create(file_path)?;
let mod_rs_token = quote! {#mod_rs};
write!(file, "{}", mod_rs_token)?;

let file_path = sim.join("startup.rs");
let mut file = fs::File::create(file_path)?;
let startup_token = quote! {#startup};
write!(file, "{}", startup_token)?;

let file_path = sim.join("arbitrage.rs");
fs::File::create(file_path)?;

let file_path = src_path.join("main.rs");
let mut file = fs::File::create(file_path)?;
let main_token = quote! {#main};
write!(file, "{}", main_token)?;

println!(
"Your Arbiter project '{}' has been successfully initialized!",
name
);
Ok(())
}
69 changes: 48 additions & 21 deletions bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
#![warn(missing_docs, unsafe_code)]
#![warn(missing_docs)]

// TODO: Replace prints with better logging?
// TODO: Reduce any clutter here.
// TODO: Change some of the output messages to be more descriptive.
//! `Arbiter` CLI Tool
//!
//! The Arbiter command-line interface provides minimum utilities for the
//! utilization of the arbiter-core crate. It is designed to be a simple and versitile.
//!
//!
//! Key Features:
//! - Simulation Initialization: Allow users to kickstart new data analysis simulations.
//! - Contract Bindings: Generate necessary bindings for interfacing with different contracts.
//!
//!
//! This CLI leverages the power of Rust's type system to
//! offer fast and reliable operations, ensuring data integrity and ease of use.

use std::error::Error;

Expand All @@ -12,64 +22,81 @@ use thiserror::Error;
mod bind;
mod init;

/// Represents command-line arguments passed to the `Arbiter` tool.
#[derive(Parser)]
#[command(name = "Arbiter")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(about = "Data analysis tool for decentralized exchanges.", long_about = None)]
#[command(about = "Ethereum Virtual Machine Logic Simulator", long_about = None)]
0xJepsen marked this conversation as resolved.
Show resolved Hide resolved
#[command(author)]
struct Args {
/// Pass a subcommand in.
/// Defines the subcommand to execute.
#[command(subcommand)]
command: Option<Commands>,
}

/// `ConfigurationError` enumeration type for errors parsing a `.toml` configuration file.
/// Errors that can occur while reading or parsing a configuration file.
#[derive(Error, Debug)]
pub enum ConfigurationError {
/// Error occured when attempting to read file from designated path.
#[error("config file path does not exist")]
/// Indicates that the configuration file could not be read from the given path.
#[error("configuration file path does not exist")]
FilepathError(#[from] std::io::Error),

/// Error occured when attempting to deserialize toml file.
/// Indicates an error occurred during the deserialization of the `.toml` file.
#[error("toml deserialization failed")]
DeserializationError(#[from] toml::de::Error),

/// Error occured with missing fields in the toml file.
/// Indicates that certain expected fields were missing from the `.toml` file.
#[error("missing fields in toml file")]
MissingFieldsError(String),
}

/// `Configurable` trait for parsing a `.toml` configuration file.
/// Provides functionality for classes that need to be configured using a `.toml` file.
pub trait Configurable: Sized {
/// Used to parse a `.toml` configuration file.
/// Parses the given `.toml` file to configure the object.
///
/// # Arguments
///
/// * `command_path` - A string slice that holds the path to the `.toml` configuration file.
///
/// # Returns
///
/// * A `Result` which is either a configured object of type `Self` or a `ConfigurationError`.
fn configure(command_path: &str) -> Result<Self, ConfigurationError>;
}

/// Defines available subcommands for the `Arbiter` tool.
#[derive(Subcommand)]
enum Commands {
/// Represents the `Bind` subcommand.
Bind,

/// Represents the `Init` subcommand to initialize a simulation.
Init {
/// Name of the simulation to initialize
/// The name of the simulation to be initialized.
#[clap(index = 1)]
simulation_name: String,
},
}

/// The main entry point for the `Arbiter` tool.
///
/// This function parses command line arguments, and based on the provided subcommand,
/// either initializes a new simulation or generates bindings.
///
/// # Returns
///
/// * A `Result` which is either an empty tuple for successful execution or a dynamic error.
fn main() -> Result<(), Box<dyn Error>> {
let args = Args::parse();

if args.command.is_some() {
println!("Starting Arbiter...");
}

match &args.command {
Some(Commands::Init { simulation_name }) => {
println!("Initializing simulation...");
init::create_simulation(simulation_name)?;
println!("Initializing Arbiter project...");
init::init_project(simulation_name)?;
}
Some(Commands::Bind) => {
println!("Generating bindings...");
bind::bind_forge()?;
bind::forge_bind()?;
}
None => {
Args::command()
Expand Down