Skip to content

Commit

Permalink
refactor: all u256 calcs to be safest using scalar
Browse files Browse the repository at this point in the history
Merge pull request #20 from willyrgf/refactor/u256_calcs
  • Loading branch information
willyrgf authored Nov 12, 2022
2 parents 8619533 + 81e0577 commit 0c7e5c7
Show file tree
Hide file tree
Showing 20 changed files with 368 additions and 167 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mfm"
version = "0.1.19"
version = "0.1.20"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
20 changes: 16 additions & 4 deletions src/asset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use web3::{
types::{Address, Bytes, H160, U256},
};

use crate::utils;
use crate::utils::{self, math};

use crate::config::{network::Network, wallet::Wallet, withdraw_wallet::WithdrawWallet, Config};
use crate::utils::resources::{exists_resource_file_fs_or_res, get_resource_file_fs_or_res};
Expand All @@ -33,6 +33,16 @@ pub struct Asset {
impl Asset {
pub fn new(asset_config: &AssetConfig, network: &Network) -> Result<Self, anyhow::Error> {
let asset_network = asset_config.networks.get(network.get_name())?;

// TODO: add a validator for the builders

if !(asset_network.slippage > 0.0 && asset_network.slippage <= 100.0) {
return Err(anyhow::anyhow!(
"Asset::new(): asset_name: {}; slippage needs to be between 0 and 100",
asset_network.name
));
}

Ok(Asset {
name: asset_network.name.clone(),
kind: asset_config.kind.clone(),
Expand All @@ -44,10 +54,12 @@ impl Asset {
})
}

pub fn slippage(&self) -> f64 {
self.slippage
}

pub fn slippage_u256(&self, asset_decimals: u8) -> U256 {
//TODO: review u128
let qe = ((&self.slippage / 100.0) * 10_f64.powf(asset_decimals.into())) as u128;
U256::from(qe)
math::percent_to_u256(self.slippage, asset_decimals)
}

pub fn name(&self) -> &str {
Expand Down
6 changes: 5 additions & 1 deletion src/cmd/approve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ pub fn generate_cmd() -> Command {
)
.arg(clap::arg!(-w --"wallet" <WALLET_NAME> "Wallet id from config file").required(true))
.arg(clap::arg!(-a --"asset" <ASSET> "Asset to approve spender").required(true))
.arg(clap::arg!(-v --"amount" <VALUE> "Amount to allow spending").required(true))
.arg(
clap::arg!(-v --"amount" <VALUE> "Amount to allow spending")
.required(true)
.value_parser(clap::value_parser!(f64)),
)
}

pub async fn call_sub_commands(args: &ArgMatches) -> Result<(), anyhow::Error> {
Expand Down
42 changes: 21 additions & 21 deletions src/cmd/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
Config,
},
rebalancer::config::RebalancerConfig,
utils::math,
};
use anyhow::Context;
use clap::ArgMatches;
Expand Down Expand Up @@ -89,30 +90,29 @@ pub fn get_txn_id(args: &ArgMatches) -> &str {

#[tracing::instrument(name = "get amount from command args")]
pub fn get_amount(args: &ArgMatches, asset_decimals: u8) -> Result<U256, anyhow::Error> {
//TODO: need to review usage from i128
match args.get_one::<String>("amount") {
Some(a) => {
//TODO: move it to a helper function
let q = a
.parse::<f64>()
.map_err(|e| anyhow::anyhow!("cant parse the amount value to f64, got {:?}", e))?;
let qe = (q * 10_f64.powf(asset_decimals.into())) as i128;
Ok(U256::from(qe))
}
None => Err(anyhow::anyhow!("--amount is required")),
match args.get_one::<f64>("amount") {
Some(amount) => Ok(math::f64_to_u256(*amount, asset_decimals)),
None => Err(anyhow::anyhow!("--amount is not a number")),
}
}

#[tracing::instrument(name = "get slippage from command args")]
pub fn get_slippage(args: &ArgMatches, asset_decimals: u8) -> Result<U256, anyhow::Error> {
//TODO: review u128
match args.get_one::<String>("slippage") {
Some(a) => {
let q = a.parse::<f64>().unwrap();
let qe = ((q / 100.0) * 10_f64.powf(asset_decimals.into())) as u128;
Ok(U256::from(qe))
}
None => Err(anyhow::anyhow!("--slippage is required")),
#[tracing::instrument(name = "get amount in f64 from command args")]
pub fn get_amount_f64(args: &ArgMatches) -> Result<f64, anyhow::Error> {
match args.get_one::<f64>("amount") {
Some(amount) => Ok(*amount),
None => Err(anyhow::anyhow!("--amount is not a number")),
}
}

#[tracing::instrument(name = "get slippage in f64 from command args")]
pub fn get_slippage(args: &ArgMatches) -> Result<f64, anyhow::Error> {
match args.get_one::<f64>("slippage") {
Some(f) if *f > 0.0 && *f <= 100.0 => Ok(*f),
Some(f) => Err(anyhow::anyhow!(
"--slippage needs to be between 0 and 100. f: {}",
f
)),
None => Err(anyhow::anyhow!("--slippage is not a number")),
}
}

Expand Down
27 changes: 15 additions & 12 deletions src/cmd/swap.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{cmd::helpers, utils};
use crate::{
cmd::helpers,
utils::{self, math},
};
use clap::{ArgMatches, Command};
use prettytable::{row, Table};
use web3::types::U256;
Expand All @@ -13,14 +16,16 @@ pub fn generate_cmd() -> Command {
.arg(clap::arg!(-w --"wallet" <WALLET_NAME> "Wallet id from config file").required(true))
.arg(
clap::arg!(-a --"amount" <AMMOUNT> "Amount of TokenA to swap to TokenB")
.required(false),
.required(false)
.value_parser(clap::value_parser!(f64)),
)
.arg(clap::arg!(-i --"token_input" <TOKEN_INPUT> "Asset of input token").required(false))
.arg(clap::arg!(-o --"token_output" <TOKEN_OUTPUT> "Asset of output token").required(false))
.arg(
clap::arg!(-s --"slippage" <SLIPPAGE> "Slippage (default 0.5)")
.required(false)
.default_value("0.5"),
.default_value("0.5")
.value_parser(clap::value_parser!(f64)),
)
}

Expand All @@ -37,10 +42,7 @@ pub async fn call_sub_commands(args: &ArgMatches) -> Result<(), anyhow::Error> {
let input_asset_decimals = input_asset.decimals().await.unwrap();
let output_asset_decimals = output_asset.decimals().await.unwrap();

let amount_in = helpers::get_amount(args, input_asset_decimals).unwrap_or_else(|e| {
tracing::error!(error = %e);
panic!()
});
let amount_in = helpers::get_amount(args, input_asset_decimals)?;

let exchange = match helpers::get_exchange(args) {
Ok(e) => e,
Expand All @@ -54,12 +56,12 @@ pub async fn call_sub_commands(args: &ArgMatches) -> Result<(), anyhow::Error> {
}
};

let wallet = helpers::get_wallet(args).unwrap_or_else(|e| {
let from_wallet = helpers::get_wallet(args).unwrap_or_else(|e| {
tracing::error!(error = %e);
panic!()
});

let slippage = helpers::get_slippage(args, output_asset_decimals).unwrap();
let slippage = helpers::get_slippage(args)?;

let asset_path_in = exchange.build_route_for(&input_asset, &output_asset).await;

Expand All @@ -71,17 +73,18 @@ pub async fn call_sub_commands(args: &ArgMatches) -> Result<(), anyhow::Error> {
.into();
tracing::debug!("amount_mint_out: {:?}", amount_min_out);

let slippage_amount = (amount_min_out * slippage) / U256::exp10(output_asset_decimals.into());
let slippage_amount =
math::get_slippage_amount(amount_min_out, slippage, output_asset_decimals);
let amount_out_slippage = amount_min_out - slippage_amount;

exchange
.swap_tokens_for_tokens(
wallet,
from_wallet,
amount_in,
amount_out_slippage,
input_asset.clone(),
output_asset.clone(),
Some(slippage),
Some(math::f64_to_u256(slippage, output_asset_decimals)),
)
.await;

Expand Down
1 change: 1 addition & 0 deletions src/cmd/withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub fn generate_cmd() -> Command {
.arg(
clap::arg!(-v --"amount" <VALUE> "Amount to withdraw")
.required(true)
.value_parser(clap::value_parser!(f64))
)
}

Expand Down
47 changes: 17 additions & 30 deletions src/config/exchange.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::asset::Asset;
use crate::utils;
use crate::utils::resources::{exists_resource_file_fs_or_res, get_resource_file_fs_or_res};
use crate::utils::scalar::BigDecimal;
use crate::utils::{self, math};

use std::ops::Div;
use std::str::FromStr;
use std::time::UNIX_EPOCH;
use std::{collections::HashMap, time::SystemTime};
Expand Down Expand Up @@ -214,8 +216,6 @@ impl Exchange {
}

let contract = self.router_contract();
// let quantity = 1;
// let amount: U256 = (quantity * 10_i32.pow(decimals.into())).into();
let result = contract.query(
"getAmountsOut",
(amount, assets_path),
Expand Down Expand Up @@ -273,34 +273,22 @@ impl Exchange {
.collect::<Vec<_>>(),
);

// let asset_path_out = self.build_route_for(&output_asset, &input_asset).await;
// let asset_path_in = self.build_route_for(&input_asset, &output_asset).await;

//let input_asset_decimals = input_asset.decimals().await;
let output_asset_decimals = output_asset.decimals().await.unwrap();
let amount_in = input_asset.balance_of(from_wallet.address()).await;

//TODO: review this model of use slippage
// review another usage to change to use always the output asset decimals
let slippage = {
let ais = input_asset.slippage_u256(output_asset_decimals);
let aos = output_asset.slippage_u256(output_asset_decimals);

ais + aos
};

let amount_out: U256 = self
.get_amounts_out(amount_in, asset_path.clone())
.await
.last()
.unwrap()
.into();

//TODO: move this kind of logic to the U256 module
//FIXME: fix the arithmetic operation overflow
let slippage_amount = (amount_out * slippage) / U256::exp10(output_asset_decimals.into());
// Sum the slippage of the both assets the input and output.
let slippage = input_asset.slippage() + output_asset.slippage();

let slippage_amount =
math::get_slippage_amount(amount_out, slippage, output_asset_decimals);
let amount_min_out_slippage = amount_out - slippage_amount;
//let amount_min_out_slippage = amount_out;

match swap_tokens_for_tokens::estimate_gas(
self,
Expand Down Expand Up @@ -349,14 +337,11 @@ impl Exchange {
let input_asset_decimals = input_asset.decimals().await.unwrap();
let output_asset_decimals = output_asset.decimals().await.unwrap();

//TODO: review this model of use slippage
let slippage = match slippage_opt {
Some(s) => s,
None => {
let ais = input_asset.slippage_u256(output_asset_decimals);
let aos = output_asset.slippage_u256(output_asset_decimals);

ais + aos
input_asset.slippage_u256(output_asset_decimals)
+ output_asset.slippage_u256(output_asset_decimals)
}
};

Expand Down Expand Up @@ -428,12 +413,13 @@ impl Exchange {

match limit_max_input {
Some(limit) if amount_in > limit => {
// TODO: resolv this calc with U256 exp10 or numbigint
let mut total = amount_in;
let amount_in_plus_two_decimals = amount_in * U256::exp10(2);
let number_hops = (((amount_in_plus_two_decimals / limit).as_u128() as f64)
/ 100_f64)
.ceil() as u64;

let amount_in_bd =
BigDecimal::from_unsigned_u256(&amount_in, input_asset_decimals.into());
let limit_bd = BigDecimal::from_unsigned_u256(&limit, input_asset_decimals.into());

let number_hops = amount_in_bd.div(limit_bd).to_f64().unwrap().ceil() as u64;

for _ in 0..number_hops {
let ai: U256;
Expand Down Expand Up @@ -471,6 +457,7 @@ impl Exchange {

let slippage_amount =
(ao * slippage) / U256::exp10(output_asset_decimals.into());

let amount_min_out_slippage = ao - slippage_amount;
tracing::debug!("slippage_amount {:?}", slippage_amount);

Expand Down
11 changes: 7 additions & 4 deletions src/config/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::HashMap;
use web3::{transports::Http, types::U256, Web3};

use super::{exchange::Exchange, Config};
use crate::asset::Asset;
use crate::{asset::Asset, utils::scalar::BigDecimal};

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Network {
Expand Down Expand Up @@ -43,9 +43,13 @@ impl Network {
.assets
.find_by_name_and_network(self.wrapped_asset.as_str(), self.name.as_str())
}

//TODO: validate min_balance_coin in the build of the type
pub fn get_min_balance_coin(&self, decimals: u8) -> U256 {
let qe = (self.min_balance_coin * 10_f64.powf(decimals.into())) as i64;
U256::from(qe)
BigDecimal::try_from(self.min_balance_coin)
.unwrap()
.with_scale(decimals.into())
.to_unsigned_u256()
}

pub fn get_web3_client_http(&self) -> Web3<Http> {
Expand All @@ -61,7 +65,6 @@ impl Network {
.collect()
}

// TODO: add test to avoid the bugs of like in the choice of exchange
pub async fn get_exchange_by_liquidity(
&self,
input_asset: &Asset,
Expand Down
8 changes: 5 additions & 3 deletions src/quote/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ pub fn generate() -> Command {
.arg(clap::arg!(-n --"network" <bsc> "Network to use, ex (bsc, polygon)").required(true))
.arg(clap::arg!(-e --"exchange" <pancake_swap_v2> "Exchange to use router").required(false))
.arg(
clap::arg!(-a --"amount" <AMMOUNT> "Amount of TokenA to swap to TokenB")
.required(false),
clap::arg!(-a --"amount" <AMOUNT> "Amount of TokenA to swap to TokenB")
.required(false)
.value_parser(clap::value_parser!(f64)),
)
.arg(clap::arg!(-i --"token_input" <TOKEN_INPUT> "Asset of input token").required(false))
.arg(clap::arg!(-o --"token_output" <TOKEN_OUTPUT> "Asset of output token").required(false))
.arg(
clap::arg!(-s --"slippage" <SLIPPAGE> "Slippage (default 0.5)")
.required(false)
.default_value("0.5"),
.default_value("0.5")
.value_parser(clap::value_parser!(f64)),
)
}

Expand Down
Loading

0 comments on commit 0c7e5c7

Please sign in to comment.