Skip to content

Commit

Permalink
add analysis dir and modifications to caller
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexangelj committed Aug 3, 2023
1 parent 83a07fe commit 3716a9b
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 36 deletions.
1 change: 1 addition & 0 deletions src/analysis/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod trading_function_error;
157 changes: 157 additions & 0 deletions src/analysis/trading_function_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/// Analyzes the trading function solidity against the rust implementation.
use crate::calls::{Caller, DecodedReturns};
use crate::math::NormalCurve as RustInput;

use anyhow::anyhow;
use arbiter::{
manager::SimulationManager,
utils::{float_to_wad, wad_to_float},
};
use bindings::external_normal_strategy_lib::{
ApproximateYGivenXCall, NormalCurve as SolidityInput,
};
use ethers::abi::{Tokenizable, Tokenize};

/// Input for the data.
#[derive(Clone, Debug)]
struct Input(SolidityInput);

/// Output format of the data.
#[derive(Clone, Debug)]
struct Output {
pub output_sol: f64,
pub output_rs: f64,
}

/// Each data point.
#[derive(Clone, Debug)]
struct DataPoint {
pub input: Input,
pub output: Output,
}

/// Collection of data.
#[derive(Clone, Debug)]
struct Results {
pub data: Vec<DataPoint>,
}

static STEP: f64 = 0.001;

/// Plots the trading function error.
pub fn main(manager: &SimulationManager) -> anyhow::Result<(), anyhow::Error> {
let start_time = std::time::Instant::now();

let library = manager.deployed_contracts.get("library").unwrap();
let admin = manager.agents.get("admin").unwrap();
let mut caller = Caller::new(admin);

let mut input_rs = RustInput {
reserve_x_per_wad: 0.308537538726,
reserve_y_per_wad: 0.308537538726,
strike_price_f: 1.0,
std_dev_f: 1.0,
time_remaining_sec: 31556953.0,
invariant_f: 0.0,
};

let mut input_sol = Input(SolidityInput {
reserve_x_per_wad: float_to_wad(input_rs.reserve_x_per_wad),
reserve_y_per_wad: float_to_wad(input_rs.reserve_y_per_wad),
strike_price_wad: float_to_wad(input_rs.strike_price_f),
standard_deviation_wad: float_to_wad(input_rs.std_dev_f),
time_remaining_seconds: 31556953.into(),
invariant: 0.into(),
});

let mut inputs = Vec::<Input>::new();
let mut sol = Vec::<f64>::new();
let mut rs = Vec::<f64>::new();

let mut x = 0.0;
let mut y = 0.0;

// Collect y coordinates from sol & rust at x coordinates with a distance of STEP.
// Important that x != 1.0, as that is outside the domain of the functions.
while x <= 1.0 {
let _ = y; // does nothing. Just to silence the compiler warning.

// First step cannot be zero! Undefined input for the math functions.
x += STEP;

// Edit the rust input.
input_rs.reserve_x_per_wad = x;

// Compute the rust output.
y = input_rs.approximate_y_given_x_floating();

// Edit the rust output.
rs.push(y);

// Edit the rust input with the new y value.
input_rs.reserve_y_per_wad = y;

// Edit the solidity input.
input_sol.0.reserve_x_per_wad = float_to_wad(x);

// Compute the solidity output and edit the input.
input_sol.0.reserve_y_per_wad = caller
.call(
library,
"approximateYGivenX",
vec![input_sol.0.clone().into_token()],
)?
.decoded(library)?;

// Edit the solidity output.
sol.push(wad_to_float(input_sol.0.reserve_y_per_wad));

// Add the input to the inputs vector.
inputs.push(input_sol.clone());
}

// Assert both y coordinates are the same length
if sol.len() != rs.len() {
return Err(anyhow!("sol.len() != rs.len()"));
}

// Compute the error solidity - rust.
let error = sol
.clone()
.into_iter()
.zip(rs.clone().into_iter())
.map(|(x, y)| x - y)
.collect::<Vec<f64>>();

// Format the data into the Results struct.
let mut data = Vec::<DataPoint>::new();

for i in 0..sol.len() {
data.push(DataPoint {
input: inputs[i].clone(),
output: Output {
output_sol: sol[i],
output_rs: rs[i],
},
});
}

// Print the time to run.
let elapsed = start_time.elapsed();
println!(
"\nTrading Function Error Analysis took {} seconds to run.",
elapsed.as_secs_f64()
);

// Print the data.
println!(
"data.output: {:?}",
data.into_iter()
.map(|x| x.output.output_sol.clone())
.collect::<Vec<f64>>()
);

// Plot the data.

Ok(())
}
31 changes: 21 additions & 10 deletions src/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl<'a> Caller<'a> {
contract: &SimulationContract<IsDeployed>,
function_name: &str,
args: Vec<ethers::abi::Token>,
) -> &mut Self {
) -> Result<&mut Self, Error> {
self.set_last_call(Call {
from: recast_address(self.caller.address()),
function_name: function_name.to_string(),
Expand All @@ -92,8 +92,9 @@ impl<'a> Caller<'a> {
let result = self.caller.call(contract, function_name, args.clone());

// Wraps the dynamic error into the anyhow error with some context for the last call.
let _ = self.handle_error_gracefully(result);
self
// Return type of this function must be a result so we can propagate the error with `?`.
let _ = self.handle_error_gracefully(result)?;
Ok(self)
}

pub fn balance_of(&mut self, token: &SimulationContract<IsDeployed>) -> &mut Self {
Expand Down Expand Up @@ -217,7 +218,7 @@ impl<'a> Caller<'a> {
&mut self,
portfolio: &SimulationContract<IsDeployed>,
swap_order: Order,
) -> &mut Self {
) -> Result<&mut Self, Error> {
let args: SwapCall = SwapCall {
args: swap_order.clone(),
};
Expand All @@ -235,8 +236,8 @@ impl<'a> Caller<'a> {
.call(portfolio, "swap", args.clone().into_tokens());

// Wraps the dynamic error into the anyhow error with some context for the last call.
let _ = self.handle_error_gracefully(result);
self
let _ = self.handle_error_gracefully(result)?;
Ok(self)
}

/// Wraps the arbiter call with anyhow's error context, using the last call details.
Expand All @@ -249,13 +250,14 @@ impl<'a> Caller<'a> {
if res.is_success() {
let return_bytes = unpack_execution(res.clone()).unwrap();

if return_bytes.len() == 0 {
// todo: do we need this check?
/* if return_bytes.len() == 0 {
return Err(anyhow!(
"calls.rs: {:?} call returned empty bytes: {:?}",
self.last_call,
res
));
}
} */

// Sets the result of the last call.
self.set_last_call_result(res.clone());
Expand All @@ -272,7 +274,7 @@ impl<'a> Caller<'a> {
Err(e) => {
let msg = e.to_string();
return Err(anyhow!(
"calls.rs: failed to call {:?}: {:?}",
"calls.rs: failed to call {:?}: msg: {:?}",
self.last_call,
msg
));
Expand All @@ -295,7 +297,16 @@ impl DecodedReturns for Caller<'_> {
&self,
contract: &SimulationContract<IsDeployed>,
) -> Result<T, Error> {
let result = self.last_call.result.clone().unwrap();
let result = self.last_call.result.clone();
let result = match result {
Some(result) => result,
None => {
return Err(anyhow!(
"calls.rs: {:?} call result is None when attempting to decode, was there a result?",
self.last_call
))
}
};
let return_bytes = unpack_execution(result.clone())?;

if return_bytes.len() == 0 {
Expand Down
14 changes: 7 additions & 7 deletions src/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ use bindings::{external_normal_strategy_lib, i_portfolio::*};
pub static OUTPUT_DIRECTORY: &str = "out_data";
pub static OUTPUT_FILE_NAME: &str = "results";

/// Defines the output file directory and name for the plots and csv data.
#[derive(Clone, Parser, Serialize, Deserialize, Debug)]
pub struct OutputStorage {
pub output_path: String,
pub output_file_names: String,
}

/// # Log::Run
/// Fetches the raw simulation data and records
/// it to the raw_data container.
Expand Down Expand Up @@ -210,13 +217,6 @@ fn get_reference_price(
Ok(reference_price)
}

/// Defines the output file directory and name for the plots and csv data.
#[derive(Clone, Parser, Serialize, Deserialize, Debug)]
pub struct OutputStorage {
pub output_path: String,
pub output_file_names: String,
}

pub fn plot_trading_curve(display: Display, curves: Vec<Curve>) {
// plot the trading curve coordinates using transparent_plot
let title: String = String::from("Trading Curve");
Expand Down
13 changes: 8 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ use clap::Parser;
use ethers::abi::Tokenize;
use serde::{Deserialize, Serialize};
use visualize::{design::*, plot::*};
//use log::plot_trading_curve;

// dynamic imports... generate with build.sh

pub static OUTPUT_DIRECTORY: &str = "out_data";
pub static OUTPUT_FILE_NAME: &str = "results";

mod analysis;
mod bisection;
mod calls;
mod common;
Expand Down Expand Up @@ -75,12 +75,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let token1 = manager.deployed_contracts.get("token1").unwrap();
let portfolio = manager.deployed_contracts.get("portfolio").unwrap();
let mut arb_caller = calls::Caller::new(arbitrageur);
arb_caller
let _ = arb_caller
.approve(&token0, recast_address(portfolio.address), 0.0)
.res();
arb_caller
.res()?;
let _ = arb_caller
.approve(&token1, recast_address(portfolio.address), 0.0)
.res();
.res()?;

// Simulation loop

Expand Down Expand Up @@ -144,6 +144,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
plot.lp_pvf_plot();
plot.arbitrageur_pvf_plot();

// Do some analysis on the data.
analysis::trading_function_error::main(&manager)?;

// Simulation finish and log
manager.shutdown();
println!("Simulation finished.");
Expand Down
26 changes: 12 additions & 14 deletions src/step.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
use super::calls::Caller;
use arbiter::{
agent::Agent,
manager::SimulationManager,
utils::{float_to_wad, recast_address},
};
use ethers::abi::Tokenize;

/// Moves the simulation forward a step.
/// Moves the simulation forward a step by calling `setPrice` triggering the `PriceChange` event.
pub fn run(manager: &SimulationManager, price: f64) -> Result<(), Box<dyn std::error::Error>> {
let admin = manager.agents.get("admin").unwrap();
let exchange = manager.deployed_contracts.get("exchange").unwrap();
let token = manager.deployed_contracts.get("token0").unwrap();
let admin = manager.agents.get("admin").unwrap();
let mut caller = Caller::new(admin);

let wad_price = float_to_wad(price);

// Triggers the "PriceChange" event, which agents might be awaiting.
let new_price_call = admin.call(
exchange,
"setPrice",
(recast_address(token.address), wad_price).into_tokens(),
)?;

match new_price_call.is_success() {
true => {} // just be quiet... make noise if there's an error.,
false => println!("New price failed to set: {}", price),
}
// Calls the `res()` at the end with a `?` to propagate any errors.
let _ = caller
.call(
exchange,
"setPrice",
(recast_address(token.address), wad_price).into_tokens(),
)?
.res()?;

Ok(())
}

0 comments on commit 3716a9b

Please sign in to comment.