From 3f523c4c7ed3e52c65f542f3ddf81693c172d7e2 Mon Sep 17 00:00:00 2001 From: AJ Taylor Date: Wed, 28 Aug 2024 16:12:57 -0600 Subject: [PATCH] add math --- Cargo.lock | 2 + Cargo.toml | 2 + src/jupiter.rs | 1 - src/main.rs | 1 + src/math.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ src/run.rs | 13 +++--- 6 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 src/math.rs diff --git a/Cargo.lock b/Cargo.lock index 3757c85..4776f2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1444,6 +1444,8 @@ dependencies = [ "colored", "futures", "jsonrpsee", + "num-derive 0.3.3", + "num-traits", "reqwest", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index dae581f..f6796d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,3 +40,5 @@ thiserror = "1.0.50" tokio = { version = "1.39.2", features = ["full"] } tokio-tungstenite = "0.16" url = "2.5" +num-derive = "^0.3" +num-traits = "^0.2" diff --git a/src/jupiter.rs b/src/jupiter.rs index 8ceb137..63401c6 100644 --- a/src/jupiter.rs +++ b/src/jupiter.rs @@ -68,7 +68,6 @@ impl Arber { pub async fn jupiter_swap_tx(&self, quote: Quote) -> Result { let url = format!("{}/swap", self.jupiter_quote_url.as_ref().unwrap()); - println!("Quote: {:?}", quote); let request = SwapRequest { user_public_key: self.signer().pubkey(), wrap_and_unwrap_SOL: Some(true), diff --git a/src/main.rs b/src/main.rs index 778e728..12cc2b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod etherfuse; mod field_as_string; mod jito; mod jupiter; +mod math; mod purchase; mod run; mod transaction; diff --git a/src/math.rs b/src/math.rs new file mode 100644 index 0000000..f4baa4d --- /dev/null +++ b/src/math.rs @@ -0,0 +1,106 @@ +#![allow(dead_code)] + +use anyhow::{anyhow, Result}; +use std::fmt::Display; + +pub fn checked_as_f64(arg: T) -> Result +where + T: Display + num_traits::ToPrimitive + Clone, +{ + let option: Option = num_traits::NumCast::from(arg.clone()); + if let Some(res) = option { + Ok(res) + } else { + Err(anyhow!("Math overflow")) + } +} + +pub fn checked_as_u64(arg: T) -> Result +where + T: Display + num_traits::ToPrimitive + Clone, +{ + let option: Option = num_traits::NumCast::from(arg.clone()); + if let Some(res) = option { + Ok(res) + } else { + return Err(anyhow!("Math overflow")); + } +} + +pub fn checked_div(arg1: T, arg2: T) -> Result +where + T: num_traits::PrimInt + Display, +{ + if let Some(res) = arg1.checked_div(&arg2) { + Ok(res) + } else { + return Err(anyhow!("Math overflow")); + } +} + +pub fn checked_float_div(arg1: T, arg2: T) -> Result +where + T: num_traits::Float + Display, +{ + if arg2 == T::zero() { + return Err(anyhow!("Math overflow")); + } + let res = arg1 / arg2; + if !res.is_finite() { + return Err(anyhow!("Math overflow")); + } else { + Ok(res) + } +} + +pub fn checked_mul(arg1: T, arg2: T) -> Result +where + T: num_traits::PrimInt + Display, +{ + if let Some(res) = arg1.checked_mul(&arg2) { + Ok(res) + } else { + return Err(anyhow!("Math overflow")); + } +} + +pub fn checked_float_mul(arg1: T, arg2: T) -> Result +where + T: num_traits::Float + Display, +{ + let res = arg1 * arg2; + if !res.is_finite() { + return Err(anyhow!("Math overflow")); + } else { + Ok(res) + } +} + +pub fn checked_powi(arg: f64, exp: i32) -> Result { + let res = if exp > 0 { + f64::powi(arg, exp) + } else { + // wrokaround due to f64::powi() not working properly on-chain with negative + // exponent + checked_float_div(1.0, f64::powi(arg, -exp))? + }; + if res.is_finite() { + Ok(res) + } else { + return Err(anyhow!("Math overflow")); + } +} + +pub fn to_ui_amount(amount: u64, decimals: u8) -> Result { + checked_float_div( + checked_as_f64(amount)?, + checked_powi(10.0, decimals as i32)?, + ) +} + +pub fn to_token_amount(ui_amount: f64, decimals: u8) -> Result { + checked_as_u64(checked_float_mul( + ui_amount, + checked_powi(10.0, decimals as i32)?, + )?) +} diff --git a/src/run.rs b/src/run.rs index 66812e2..b4b4b79 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,6 +1,7 @@ use crate::args::{JupiterQuoteArgs, RunArgs}; use crate::constants::{MIN_USDC_AMOUNT, STABLEBOND_DECIMALS, USDC_DECIMALS, USDC_MINT}; use crate::jupiter::Quote; +use crate::math; use crate::{Arber, PurchaseArgs}; use anyhow::Result; @@ -14,6 +15,7 @@ impl Arber { let mut interval = tokio::time::interval(std::time::Duration::from_secs(60)); loop { interval.tick().await; + println!("Checking for arb opportunity"); self.check_arb(args.clone()).await?; } } @@ -22,12 +24,13 @@ impl Arber { let usdc_balance = self.update_usdc_balance().await?; // get etherfuse price of token let stablebond_price_to_usd = self.get_etherfuse_price(args.etherfuse_token).await?; - let max_usdc_ui_amount_to_purchase = usdc_balance * 0.99; + let max_usdc_ui_amount_to_purchase = math::checked_float_mul(usdc_balance, 0.99)?; let mut usdc_token_amount = to_token_amount(max_usdc_ui_amount_to_purchase, USDC_DECIMALS); - let stablebond_ui_amount = max_usdc_ui_amount_to_purchase / stablebond_price_to_usd; + let stablebond_ui_amount = + math::checked_float_div(max_usdc_ui_amount_to_purchase, stablebond_price_to_usd)?; let mut stablebond_token_amount = - to_token_amount(stablebond_ui_amount, STABLEBOND_DECIMALS); + math::to_token_amount(stablebond_ui_amount, STABLEBOND_DECIMALS)?; // get jupiter price of token based on quoted amount of USDC in users wallet let (mut jup_price_token_to_usd, mut quote) = self .sell_quote(args.clone(), stablebond_token_amount) @@ -37,8 +40,8 @@ impl Arber { && (jup_price_token_to_usd < stablebond_price_to_usd) { // reduce the amount of tokens to purchase to see if arb exists on smaller trade - stablebond_token_amount /= 2; - usdc_token_amount /= 2; + usdc_token_amount = math::checked_div(usdc_token_amount, 2)?; + stablebond_token_amount = math::checked_div(stablebond_token_amount, 2)?; (jup_price_token_to_usd, quote) = self .sell_quote(args.clone(), stablebond_token_amount) .await?;