From 2455ab238949b3172914fb520a54c28cfd05782f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Willy=20Rom=C3=A3o?= Date: Wed, 5 Jul 2023 15:18:05 -0300 Subject: [PATCH] feat: add approve-all command to batch approvals (#51) * improvements on logging and code quality * feat: add approve-all command to batch approvals --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/approve_all/cmd.rs | 24 ++++++++++++++++++++++ src/approve_all/mod.rs | 45 ++++++++++++++++++++++++++++++++++++++++++ src/asset/mod.rs | 2 +- src/cmd/enc.rs | 25 ----------------------- src/cmd/helpers.rs | 24 +++++++++++----------- src/cmd/mod.rs | 12 +++++++---- src/lib.rs | 3 ++- src/rebalancer/cmd.rs | 1 + src/rebalancer/mod.rs | 1 + src/track/cmd.rs | 2 +- src/track/mod.rs | 2 +- 13 files changed, 98 insertions(+), 47 deletions(-) create mode 100644 src/approve_all/cmd.rs create mode 100644 src/approve_all/mod.rs diff --git a/Cargo.lock b/Cargo.lock index e90d8b4..3631f65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1204,7 +1204,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mfm" -version = "0.1.28" +version = "0.1.29" dependencies = [ "anyhow", "bigdecimal", diff --git a/Cargo.toml b/Cargo.toml index 870b81b..785fd44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mfm" -version = "0.1.28" +version = "0.1.29" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/approve_all/cmd.rs b/src/approve_all/cmd.rs new file mode 100644 index 0000000..94ca86f --- /dev/null +++ b/src/approve_all/cmd.rs @@ -0,0 +1,24 @@ +use clap::{ArgMatches, Command}; + +pub fn generate() -> Command { + Command::new("approve-all") + .about("Approve all configured token spending (needed to swap tokens)") + .arg( + clap::arg!(-w --"wallet" "Wallet id from config file") + .required(true), + ) + .arg( + clap::arg!(-n --"network" "Network to run all approvals") + .required(true), + ) + .arg( + clap::arg!(-a --"amount" "Amount to allow spending: default is the current balance") + .required(false) + .value_parser(clap::value_parser!(f64)), + ) +} + +#[tracing::instrument(name = "approve_all call command", level = "debug")] +pub async fn call_sub_commands(args: &ArgMatches) -> Result<(), anyhow::Error> { + super::run(args).await +} diff --git a/src/approve_all/mod.rs b/src/approve_all/mod.rs new file mode 100644 index 0000000..db4ce10 --- /dev/null +++ b/src/approve_all/mod.rs @@ -0,0 +1,45 @@ +use crate::{cmd::helpers, config::Config}; +use clap::ArgMatches; + +pub mod cmd; + +#[tracing::instrument(name = "run approve-all", skip(args))] +async fn run(args: &ArgMatches) -> Result<(), anyhow::Error> { + let wallet = helpers::get_wallet(args)?; + let network = helpers::get_network(args)?; + let config = Config::global(); + + let assets = config.assets.assets_by_network(network)?; + let exchanges = network.get_exchanges(); + + for asset in assets { + let asset_decimals = asset.decimals().await.unwrap(); + let amount = helpers::get_amount(args, asset_decimals).unwrap_or(asset.balance_of(wallet.address()).await?); + tracing::debug!("amount: {:?}", amount); + + for exchange in exchanges.iter() { + let allowance_amount = asset.allowance(wallet.address(), exchange.as_router_address().unwrap()).await; + if allowance_amount >= amount { + tracing::info!("current amount allowed is greater or equal --amount, to {} on {} for {} with spender {}", asset.name(), exchange.name(), amount, wallet.address()); + continue; + } + + tracing::info!("running approve_spender to {} on {} for {} with spender {}", asset.name(), exchange.name(), amount, wallet.address()); + asset + .approve_spender(wallet, exchange.as_router_address().unwrap(), amount) + .await + .unwrap(); + + let remaning = asset + .allowance(wallet.address(), exchange.as_router_address().unwrap()) + .await; + tracing::info!( + "approved_spender allowance remaning on {}, to spend: {:?}, asset_decimals: {}", + asset.name(), + remaning, + asset_decimals + ); + } + } + Ok(()) +} diff --git a/src/asset/mod.rs b/src/asset/mod.rs index 1ba2132..745ebb3 100644 --- a/src/asset/mod.rs +++ b/src/asset/mod.rs @@ -120,7 +120,7 @@ impl Asset { } // TODO: validate it in the initialization of Asset - #[tracing::instrument(name = "get decimals from asset contract")] + #[tracing::instrument(name = "get decimals from asset contract", level = "debug")] pub async fn decimals(&self) -> Result { self.contract() .query("decimals", (), None, Options::default(), None) diff --git a/src/cmd/enc.rs b/src/cmd/enc.rs index 074a4a1..e69de29 100644 --- a/src/cmd/enc.rs +++ b/src/cmd/enc.rs @@ -1,25 +0,0 @@ -use crate::utils::{self, password::encrypt_private_key_to_base64}; -use clap::{ArgMatches, Command}; - -pub const COMMAND: &str = "enc"; - -pub fn generate_cmd<'a>() -> Command { - Command::new(COMMAND).about("Encrypt data with a password") -} - -pub async fn call_sub_commands(_: &ArgMatches) { - let password = utils::password::prompt_password("Type a password: ").unwrap_or_else(|e| { - tracing::error!(error = %e); - panic!() - }); - - let private_key = utils::password::prompt_password("Paste the wallet private key: ") - .unwrap_or_else(|e| { - tracing::error!(error = %e); - panic!() - }); - - let base64 = encrypt_private_key_to_base64(password, private_key); - - println!("Encrypted key as base64: {}", base64); -} diff --git a/src/cmd/helpers.rs b/src/cmd/helpers.rs index 332a7d8..b9323f7 100644 --- a/src/cmd/helpers.rs +++ b/src/cmd/helpers.rs @@ -15,7 +15,7 @@ use web3::types::{Address, H160, U256}; //TODO: add constants to all keys in value_of -#[tracing::instrument(name = "get exchange from command args")] +#[tracing::instrument(name = "get exchange from command args", level = "debug", skip(args))] pub fn get_exchange(args: &ArgMatches) -> Result<&Exchange, anyhow::Error> { match args.get_one::("exchange") { Some(n) => { @@ -29,7 +29,7 @@ pub fn get_exchange(args: &ArgMatches) -> Result<&Exchange, anyhow::Error> { } } -#[tracing::instrument(name = "get network from command args")] +#[tracing::instrument(name = "get network from command args", level = "debug", skip(args))] pub fn get_network(args: &ArgMatches) -> Result<&Network, anyhow::Error> { match args.get_one::("network") { Some(n) => { @@ -43,7 +43,7 @@ pub fn get_network(args: &ArgMatches) -> Result<&Network, anyhow::Error> { } } -#[tracing::instrument(name = "get address from command args")] +#[tracing::instrument(name = "get address from command args", level = "debug", skip(args))] pub fn get_address(args: &ArgMatches) -> Result { match args.get_one::("address") { Some(a) => Address::from_str(a).map_err(|e| anyhow::anyhow!(e)), @@ -51,7 +51,7 @@ pub fn get_address(args: &ArgMatches) -> Result { } } -#[tracing::instrument(name = "get wallet from command args")] +#[tracing::instrument(name = "get wallet from command args", level = "debug", skip(args))] pub fn get_wallet(args: &ArgMatches) -> Result<&Wallet, anyhow::Error> { let config = Config::global(); match args.get_one::("wallet") { @@ -98,7 +98,7 @@ pub fn get_txn_id(args: &ArgMatches) -> &str { } } -#[tracing::instrument(name = "get amount from command args")] +#[tracing::instrument(name = "get amount from command args", skip(args), level = "debug")] pub fn get_amount(args: &ArgMatches, asset_decimals: u8) -> Result { match args.get_one::("amount") { Some(amount) => Ok(math::f64_to_u256(*amount, asset_decimals)), @@ -106,7 +106,7 @@ pub fn get_amount(args: &ArgMatches, asset_decimals: u8) -> Result Result { match args.get_one::("amount") { Some(amount) => Ok(*amount), @@ -114,7 +114,7 @@ pub fn get_amount_f64(args: &ArgMatches) -> Result { } } -#[tracing::instrument(name = "get slippage in f64 from command args")] +#[tracing::instrument(name = "get slippage in f64 from command args", level = "debug", skip(args))] pub fn get_slippage(args: &ArgMatches) -> Result { match args.get_one::("slippage") { Some(f) if *f > 0.0 && *f <= 100.0 => Ok(*f), @@ -126,7 +126,7 @@ pub fn get_slippage(args: &ArgMatches) -> Result { } } -#[tracing::instrument(name = "get input token in network from command args")] +#[tracing::instrument(name = "get input token in network from command args", level = "debug", skip(args))] pub fn get_token_input_in_network_from_args( args: &ArgMatches, network_id: &str, @@ -141,7 +141,7 @@ pub fn get_token_input_in_network_from_args( } } -#[tracing::instrument(name = "get output token in network from command args")] +#[tracing::instrument(name = "get output token in network from command args", level = "debug", skip(args))] pub fn get_token_output_in_network_from_args( args: &ArgMatches, network_id: &str, @@ -184,7 +184,7 @@ pub fn get_withdraw_wallet(args: &ArgMatches) -> WithdrawWallet { } } -#[tracing::instrument(name = "get hide zero from command args")] +#[tracing::instrument(name = "get hide zero from command args", level = "debug", skip(args))] pub fn get_hide_zero(args: &ArgMatches) -> bool { match args.get_one::("hide-zero") { Some(b) => b.parse().unwrap_or(false), @@ -192,12 +192,12 @@ pub fn get_hide_zero(args: &ArgMatches) -> bool { } } -#[tracing::instrument(name = "get run every from command args")] +#[tracing::instrument(name = "get run every from command args", level = "debug", skip(args))] pub fn get_run_every(args: &ArgMatches) -> Option<&u32> { args.get_one::("run-every") } -#[tracing::instrument(name = "get track from command args")] +#[tracing::instrument(name = "get track from command args", level = "debug", skip(args))] pub fn get_track(args: &ArgMatches) -> bool { match args.get_one::("track") { Some(b) => b.parse().unwrap_or(false), diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index bb8bb25..1e0f5c3 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,5 +1,5 @@ use crate::{ - allowance, approve, balances, encrypt, quote, rebalancer, swap, track, unwrap, withdraw, wrap, watcher, + allowance, approve, balances, encrypt, quote, rebalancer, swap, track, unwrap, withdraw, wrap, watcher, approve_all, }; use crate::{config::Config, APP_NAME}; use clap::{crate_version, ArgMatches, Command}; @@ -22,6 +22,8 @@ pub enum Commands { Track, Encrypt, Watcher, + #[serde(rename = "approve-all")] + ApproveAll, } impl Commands { @@ -39,6 +41,7 @@ impl Commands { Self::Quote => quote::cmd::call_sub_commands(args).await, Self::Track => track::cmd::call_sub_commands(args).await, Self::Watcher => watcher::cmd::call_sub_commands(args).await, + Self::ApproveAll => approve_all::cmd::call_sub_commands(args).await, } } } @@ -65,15 +68,16 @@ pub fn new() -> Command { .subcommand(quote::cmd::generate()) .subcommand(track::cmd::generate()) .subcommand(watcher::cmd::generate()) + .subcommand(approve_all::cmd::generate()) } -#[tracing::instrument(name = "lookup command from cli")] +#[tracing::instrument(name = "lookup command from cli", level = "debug")] pub fn lookup_command(cmd: &str) -> Result { let json_cmd = format!("\"{}\"", cmd); serde_json::from_str(json_cmd.as_str()).map_err(|e| anyhow::anyhow!(e)) } -#[tracing::instrument(name = "call commands")] +#[tracing::instrument(name = "call commands", level = "debug")] pub async fn call_sub_commands(matches: &ArgMatches) -> Result<(), anyhow::Error> { match matches.subcommand() { Some((cmd, sub_matches)) => lookup_command(cmd)?.run(sub_matches).await, @@ -81,7 +85,7 @@ pub async fn call_sub_commands(matches: &ArgMatches) -> Result<(), anyhow::Error } } -#[tracing::instrument(name = "cli run command", skip(cmd))] +#[tracing::instrument(name = "cli run command", level = "debug")] pub async fn run(cmd: Command) -> Result<(), anyhow::Error> { let cmd_matches = cmd.get_matches(); diff --git a/src/lib.rs b/src/lib.rs index 04eb682..c1c77a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,9 +16,10 @@ pub mod utils; pub mod watcher; pub mod withdraw; pub mod wrap; +pub mod approve_all; pub const APP_NAME: &str = "mfm"; -pub const DEFAULT_LOG_LEVEL: &str = "warn"; +pub const DEFAULT_LOG_LEVEL: &str = "info"; // Since the exit code names e.g. `SIGBUS` are most appropriate yet trigger a test error with the // clippy lint `upper_case_acronyms` we have disabled this lint for this enum. diff --git a/src/rebalancer/cmd.rs b/src/rebalancer/cmd.rs index b814b3b..a91bdfa 100644 --- a/src/rebalancer/cmd.rs +++ b/src/rebalancer/cmd.rs @@ -157,6 +157,7 @@ async fn cmd_run(args: &ArgMatches) -> Result<(), anyhow::Error> { } Strategy::DiffParking => { tracing::debug!("rebalancer::cmd::call_sub_commands() Strategy::DiffParking"); + tracing::info!("running run_diff_parking by Strategy::DiffParking"); rebalancer::run_diff_parking(&config).await } } diff --git a/src/rebalancer/mod.rs b/src/rebalancer/mod.rs index b2428ef..638ad3b 100644 --- a/src/rebalancer/mod.rs +++ b/src/rebalancer/mod.rs @@ -512,6 +512,7 @@ pub async fn run_diff_parking_per_kind( tracing::debug!("diff_parking: parking_to_asset: asset_in.name: {}, asset_out.name: {}, amount_in: {:?}, amount_out: {:?}", asset_in.name(), asset_out.name(), amount_in, amount_out); + tracing::info!("run_diff_parking_per_kind: move_asset_with_slippage: asset_in ({}), asset_out ({}), amount_in ({:?}), amount_out ({:?})", asset_in.name(), asset_out.name(), amount_in, amount_out); move_asset_with_slippage(config, asset_in, asset_out, amount_in, amount_out).await } } diff --git a/src/track/cmd.rs b/src/track/cmd.rs index a6f7fbc..aaa4f43 100644 --- a/src/track/cmd.rs +++ b/src/track/cmd.rs @@ -9,7 +9,7 @@ pub fn generate() -> Command { ) } -#[tracing::instrument(name = "track call command")] +#[tracing::instrument(name = "track call command", level = "debug")] pub async fn call_sub_commands(args: &ArgMatches) -> Result<(), anyhow::Error> { super::run(args).await } diff --git a/src/track/mod.rs b/src/track/mod.rs index f821978..9bace26 100644 --- a/src/track/mod.rs +++ b/src/track/mod.rs @@ -40,7 +40,7 @@ pub struct TrackPortfolioState { data: TrackPortfolioStateData, } -#[tracing::instrument(name = "wrapped run track")] +#[tracing::instrument(name = "wrapped run track", level = "debug")] pub(crate) async fn wrapped_run(args: &ArgMatches) -> Result<(), anyhow::Error> { let config = Config::global(); let (api_token, api_address) = match &config.server {