diff --git a/.github/workflows/adex-cli.yml b/.github/workflows/komodefi-cli.yml similarity index 89% rename from .github/workflows/adex-cli.yml rename to .github/workflows/komodefi-cli.yml index 1265d28d46..2051cac111 100644 --- a/.github/workflows/adex-cli.yml +++ b/.github/workflows/komodefi-cli.yml @@ -1,4 +1,4 @@ -name: adex-cli +name: komodefi-cli on: [push] concurrency: @@ -38,8 +38,8 @@ jobs: - name: Start checking code format and lint run: | - cargo fmt --manifest-path ./mm2src/adex_cli/Cargo.toml --all -- --check - cargo clippy --manifest-path ./mm2src/adex_cli/Cargo.toml --all-targets --all-features -- --D warnings + cargo fmt --manifest-path ./mm2src/komodefi_cli/Cargo.toml --all -- --check + cargo clippy --manifest-path ./mm2src/komodefi_cli/Cargo.toml --all-targets --all-features -- --D warnings test: timeout-minutes: 60 @@ -70,7 +70,7 @@ jobs: - name: Start testing run: | - cargo test --manifest-path ./mm2src/adex_cli/Cargo.toml --no-fail-fast + cargo test --manifest-path ./mm2src/komodefi_cli/Cargo.toml --no-fail-fast build: timeout-minutes: 60 @@ -101,4 +101,4 @@ jobs: - name: Start building run: | - cargo build --manifest-path ./mm2src/adex_cli/Cargo.toml + cargo build --manifest-path ./mm2src/komodefi_cli/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 475508ffe3..27b2f608d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4661,6 +4661,7 @@ dependencies = [ "ser_error_derive", "serde", "serde_json", + "skip_serializing_none", "uuid 1.2.2", ] @@ -6872,6 +6873,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68a406c1882ed7f29cd5e248c9848a80e7cb6ae0fea82346d2746f2f941c07e1" +[[package]] +name = "skip_serializing_none" +version = "0.1.0" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.95", +] + [[package]] name = "slab" version = "0.4.8" diff --git a/Cargo.toml b/Cargo.toml index 5868b5b314..fc6a5340f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,9 +42,9 @@ members = [ ] exclude = [ - "mm2src/adex_cli", "mm2src/floodsub", "mm2src/gossipsub", + "mm2src/komodefi_cli", "mm2src/mm2_libp2p", ] diff --git a/mm2src/adex_cli/src/adex_config.rs b/mm2src/adex_cli/src/adex_config.rs deleted file mode 100644 index d69e27353f..0000000000 --- a/mm2src/adex_cli/src/adex_config.rs +++ /dev/null @@ -1,181 +0,0 @@ -use anyhow::{anyhow, bail, Result}; -use directories::ProjectDirs; -use inquire::Password; -use log::{error, info, warn}; -use serde::{Deserialize, Serialize}; -use std::fmt::{Display, Formatter}; -use std::fs; -use std::path::{Path, PathBuf}; - -use crate::adex_proc::SmartFractPrecision; -use crate::helpers::rewrite_json_file; -use crate::logging::{error_anyhow, warn_bail}; - -const PROJECT_QUALIFIER: &str = "com"; -const PROJECT_COMPANY: &str = "komodoplatform"; -const PROJECT_APP: &str = "adex-cli"; -const ADEX_CFG: &str = "adex_cfg.json"; - -const PRICE_PRECISION_MIN: usize = 8; -const PRICE_PRECISION_MAX: usize = 8; -const VOLUME_PRECISION_MIN: usize = 2; -const VOLUME_PRECISION_MAX: usize = 5; -const VOLUME_PRECISION: SmartFractPrecision = (VOLUME_PRECISION_MIN, VOLUME_PRECISION_MAX); -const PRICE_PRECISION: SmartFractPrecision = (PRICE_PRECISION_MIN, PRICE_PRECISION_MAX); -#[cfg(unix)] -const CFG_FILE_PERM_MODE: u32 = 0o660; - -pub(super) fn get_config() { - let Ok(adex_cfg) = AdexConfigImpl::from_config_path() else { return; }; - info!("{}", adex_cfg) -} - -pub(super) fn set_config(set_password: bool, rpc_api_uri: Option) -> Result<()> { - assert!(set_password || rpc_api_uri.is_some()); - let mut adex_cfg = AdexConfigImpl::from_config_path().unwrap_or_else(|_| AdexConfigImpl::default()); - - if set_password { - let rpc_password = Password::new("Enter RPC API password:") - .prompt() - .map_err(|error| error_anyhow!("Failed to get rpc_api_password: {error}"))?; - adex_cfg.set_rpc_password(rpc_password); - } - - if let Some(rpc_api_uri) = rpc_api_uri { - adex_cfg.set_rpc_uri(rpc_api_uri); - } - - adex_cfg.write_to_config_path()?; - info!("Configuration has been set"); - - Ok(()) -} - -pub(super) trait AdexConfig { - fn rpc_password(&self) -> Option; - fn rpc_uri(&self) -> Option; - fn orderbook_price_precision(&self) -> &SmartFractPrecision; - fn orderbook_volume_precision(&self) -> &SmartFractPrecision; -} - -#[derive(Debug, Default, Deserialize, Serialize)] -pub(super) struct AdexConfigImpl { - #[serde(skip_serializing_if = "Option::is_none")] - rpc_password: Option, - #[serde(skip_serializing_if = "Option::is_none")] - rpc_uri: Option, -} - -impl AdexConfig for AdexConfigImpl { - fn rpc_password(&self) -> Option { self.rpc_password.clone() } - fn rpc_uri(&self) -> Option { self.rpc_uri.clone() } - fn orderbook_price_precision(&self) -> &SmartFractPrecision { &PRICE_PRECISION } - fn orderbook_volume_precision(&self) -> &SmartFractPrecision { &VOLUME_PRECISION } -} - -impl Display for AdexConfigImpl { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - if !self.is_set() { - return writeln!(f, "adex configuration is not set"); - } - writeln!( - f, - "mm2 RPC URL: {}", - self.rpc_uri.as_ref().expect("Expected rpc_uri is set") - )?; - writeln!(f, "mm2 RPC password: *************")?; - Ok(()) - } -} - -impl AdexConfigImpl { - #[cfg(test)] - pub(super) fn new(rpc_password: &str, rpc_uri: &str) -> Self { - Self { - rpc_password: Some(rpc_password.to_string()), - rpc_uri: Some(rpc_uri.to_string()), - } - } - - #[cfg(not(test))] - pub(super) fn read_config() -> Result { - let config = AdexConfigImpl::from_config_path()?; - if config.rpc_password.is_none() { - warn!("Configuration is not complete, no rpc_password in there"); - } - if config.rpc_uri.is_none() { - warn!("Configuration is not complete, no rpc_uri in there"); - } - Ok(config) - } - - fn is_set(&self) -> bool { self.rpc_uri.is_some() && self.rpc_password.is_some() } - - pub(super) fn get_config_dir() -> Result { - let project_dirs = ProjectDirs::from(PROJECT_QUALIFIER, PROJECT_COMPANY, PROJECT_APP) - .ok_or_else(|| error_anyhow!("Failed to get project_dirs"))?; - let config_path: PathBuf = project_dirs.config_dir().into(); - fs::create_dir_all(&config_path) - .map_err(|error| error_anyhow!("Failed to create config_dir: {config_path:?}, error: {error}"))?; - Ok(config_path) - } - - pub(crate) fn get_config_path() -> Result { - let mut config_path = Self::get_config_dir()?; - config_path.push(ADEX_CFG); - Ok(config_path) - } - - fn from_config_path() -> Result { - let config_path = Self::get_config_path()?; - - if !config_path.exists() { - warn_bail!("Config is not set") - } - Self::read_from(&config_path) - } - - fn write_to_config_path(&self) -> Result<()> { - let config_path = Self::get_config_path()?; - self.write_to(&config_path) - } - - fn read_from(cfg_path: &Path) -> Result { - let adex_path_str = cfg_path.to_str().unwrap_or("Undefined"); - let adex_cfg_file = fs::File::open(cfg_path) - .map_err(|error| error_anyhow!("Failed to open: {adex_path_str}, error: {error}"))?; - - serde_json::from_reader(adex_cfg_file) - .map_err(|error| error_anyhow!("Failed to read adex_cfg to read from: {adex_path_str}, error: {error}")) - } - - fn write_to(&self, cfg_path: &Path) -> Result<()> { - let komodefi_path_str = cfg_path - .to_str() - .ok_or_else(|| error_anyhow!("Failed to get cfg_path as str"))?; - rewrite_json_file(self, komodefi_path_str)?; - #[cfg(unix)] - { - Self::warn_on_insecure_mode(komodefi_path_str)?; - } - Ok(()) - } - - #[cfg(unix)] - fn warn_on_insecure_mode(file_path: &str) -> Result<()> { - use std::os::unix::fs::PermissionsExt; - let perms = fs::metadata(file_path)?.permissions(); - let mode = perms.mode() & 0o777; - if mode != CFG_FILE_PERM_MODE { - warn!( - "Configuration file: '{}' - does not comply to the expected mode: {:o}, the actual one is: {:o}", - file_path, CFG_FILE_PERM_MODE, mode - ); - }; - Ok(()) - } - - fn set_rpc_password(&mut self, rpc_password: String) { self.rpc_password.replace(rpc_password); } - - fn set_rpc_uri(&mut self, rpc_uri: String) { self.rpc_uri.replace(rpc_uri); } -} diff --git a/mm2src/adex_cli/src/adex_proc/adex_proc_impl.rs b/mm2src/adex_cli/src/adex_proc/adex_proc_impl.rs deleted file mode 100644 index 33d4fbfb62..0000000000 --- a/mm2src/adex_cli/src/adex_proc/adex_proc_impl.rs +++ /dev/null @@ -1,159 +0,0 @@ -use anyhow::{anyhow, bail, Result}; -use log::{error, info, warn}; -use mm2_rpc::data::legacy::{BalanceResponse, CoinInitResponse, GetEnabledResponse, Mm2RpcResult, MmVersionResponse, - OrderbookRequest, OrderbookResponse, SellBuyRequest, SellBuyResponse, Status}; -use serde_json::{json, Value as Json}; - -use super::command::{Command, Dummy, Method}; -use super::response_handler::ResponseHandler; -use super::OrderbookConfig; -use crate::activation_scheme_db::get_activation_scheme; -use crate::adex_config::AdexConfig; -use crate::transport::Transport; -use crate::{error_anyhow, error_bail, warn_anyhow}; - -pub(crate) struct AdexProc<'trp, 'hand, 'cfg, T: Transport, H: ResponseHandler, C: AdexConfig + ?Sized> { - pub(crate) transport: Option<&'trp T>, - pub(crate) response_handler: &'hand H, - pub(crate) config: &'cfg C, -} - -macro_rules! request_legacy { - ($request: ident, $response_ty: ty, $self: ident, $handle_method: ident$ (, $opt:expr)*) => {{ - let transport = $self.transport.ok_or_else(|| warn_anyhow!( concat!("Failed to send: `", stringify!($request), "`, transport is not available")))?; - match transport.send::<_, $response_ty, Json>($request).await { - Ok(Ok(ok)) => $self.response_handler.$handle_method(&ok, $($opt),*), - Ok(Err(error)) => $self.response_handler.print_response(error), - Err(error) => error_bail!( - concat!("Failed to send: `", stringify!($request), "`, error: {}"), - error - ), - } - }}; -} - -impl AdexProc<'_, '_, '_, T, P, C> { - pub(crate) async fn enable(&self, asset: &str) -> Result<()> { - info!("Enabling asset: {asset}"); - - let activation_scheme = get_activation_scheme()?; - let activation_method = activation_scheme.get_activation_method(asset)?; - - let enable = Command::builder() - .flatten_data(activation_method) - .userpass(self.get_rpc_password()?) - .build(); - - request_legacy!(enable, CoinInitResponse, self, on_enable_response) - } - - pub(crate) async fn get_balance(&self, asset: &str) -> Result<()> { - info!("Getting balance, coin: {asset} ..."); - let get_balance = Command::builder() - .method(Method::GetBalance) - .flatten_data(json!({ "coin": asset })) - .userpass(self.get_rpc_password()?) - .build(); - request_legacy!(get_balance, BalanceResponse, self, on_balance_response) - } - - pub(crate) async fn get_enabled(&self) -> Result<()> { - info!("Getting list of enabled coins ..."); - - let get_enabled = Command::::builder() - .method(Method::GetEnabledCoins) - .userpass(self.get_rpc_password()?) - .build(); - request_legacy!( - get_enabled, - Mm2RpcResult, - self, - on_get_enabled_response - ) - } - - pub(crate) async fn get_orderbook(&self, base: &str, rel: &str, orderbook_config: OrderbookConfig) -> Result<()> { - info!("Getting orderbook, base: {base}, rel: {rel} ..."); - - let get_orderbook = Command::builder() - .method(Method::GetOrderbook) - .flatten_data(OrderbookRequest { - base: base.to_string(), - rel: rel.to_string(), - }) - .build(); - - request_legacy!( - get_orderbook, - OrderbookResponse, - self, - on_orderbook_response, - self.config, - orderbook_config - ) - } - - pub(crate) async fn sell(&self, order: SellBuyRequest) -> Result<()> { - info!( - "Selling: {} {} for: {} {} at the price of {} {} per {}", - order.volume, - order.base, - order.volume.clone() * order.price.clone(), - order.rel, - order.price, - order.rel, - order.base, - ); - - let sell = Command::builder() - .userpass(self.get_rpc_password()?) - .method(Method::Sell) - .flatten_data(order) - .build(); - request_legacy!(sell, Mm2RpcResult, self, on_sell_response) - } - - pub(crate) async fn buy(&self, order: SellBuyRequest) -> Result<()> { - info!( - "Buying: {} {} with: {} {} at the price of {} {} per {}", - order.volume, - order.base, - order.volume.clone() * order.price.clone(), - order.rel, - order.price, - order.rel, - order.base, - ); - - let buy = Command::builder() - .userpass(self.get_rpc_password()?) - .method(Method::Buy) - .flatten_data(order) - .build(); - request_legacy!(buy, Mm2RpcResult, self, on_buy_response) - } - - pub(crate) async fn send_stop(&self) -> Result<()> { - info!("Sending stop command"); - let stop_command = Command::::builder() - .userpass(self.get_rpc_password()?) - .method(Method::Stop) - .build(); - request_legacy!(stop_command, Mm2RpcResult, self, on_stop_response) - } - - pub(crate) async fn get_version(self) -> Result<()> { - info!("Request for mm2 version"); - let get_version = Command::::builder() - .userpass(self.get_rpc_password()?) - .method(Method::Version) - .build(); - request_legacy!(get_version, MmVersionResponse, self, on_version_response) - } - - fn get_rpc_password(&self) -> Result { - self.config - .rpc_password() - .ok_or_else(|| error_anyhow!("Failed to get rpc_password, not set")) - } -} diff --git a/mm2src/adex_cli/src/adex_proc/command.rs b/mm2src/adex_cli/src/adex_proc/command.rs deleted file mode 100644 index 461798cf56..0000000000 --- a/mm2src/adex_cli/src/adex_proc/command.rs +++ /dev/null @@ -1,94 +0,0 @@ -use derive_more::Display; -use serde::Serialize; - -#[derive(Serialize, Clone)] -pub(super) struct Command -where - T: Serialize + Sized, -{ - #[serde(flatten, skip_serializing_if = "Option::is_none")] - flatten_data: Option, - #[serde(skip_serializing_if = "Option::is_none")] - method: Option, - #[serde(skip_serializing_if = "Option::is_none")] - userpass: Option, -} - -#[derive(Serialize, Clone, Display)] -#[serde(rename_all = "lowercase")] -pub(super) enum Method { - Stop, - Version, - #[serde(rename = "my_balance")] - GetBalance, - #[serde(rename = "get_enabled_coins")] - GetEnabledCoins, - #[serde(rename = "orderbook")] - GetOrderbook, - Sell, - Buy, -} - -#[derive(Serialize, Clone, Copy, Display)] -pub(super) struct Dummy {} - -impl Command -where - T: Serialize + Sized, -{ - pub(super) fn builder() -> CommandBuilder { CommandBuilder::new() } -} - -pub(super) struct CommandBuilder { - userpass: Option, - method: Option, - flatten_data: Option, -} - -impl CommandBuilder -where - T: Serialize, -{ - fn new() -> Self { - CommandBuilder { - userpass: None, - method: None, - flatten_data: None, - } - } - - pub(super) fn userpass(&mut self, userpass: String) -> &mut Self { - self.userpass = Some(userpass); - self - } - - pub(super) fn method(&mut self, method: Method) -> &mut Self { - self.method = Some(method); - self - } - - pub(super) fn flatten_data(&mut self, flatten_data: T) -> &mut Self { - self.flatten_data = Some(flatten_data); - self - } - - pub(super) fn build(&mut self) -> Command { - Command { - userpass: self.userpass.take(), - method: self.method.take(), - flatten_data: self.flatten_data.take(), - } - } -} - -impl std::fmt::Display for Command { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut cmd: Self = self.clone(); - cmd.userpass = self.userpass.as_ref().map(|_| "***********".to_string()); - writeln!( - f, - "{}", - serde_json::to_string(&cmd).unwrap_or_else(|_| "Unknown".to_string()) - ) - } -} diff --git a/mm2src/adex_cli/src/adex_proc/mod.rs b/mm2src/adex_cli/src/adex_proc/mod.rs deleted file mode 100644 index 0d6795a413..0000000000 --- a/mm2src/adex_cli/src/adex_proc/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod adex_proc_impl; -mod command; -mod response_handler; - -pub(super) use adex_proc_impl::AdexProc; -pub(super) use response_handler::{ResponseHandler, ResponseHandlerImpl, SmartFractPrecision}; - -#[derive(Clone)] -pub(super) struct OrderbookConfig { - pub(super) uuids: bool, - pub(super) min_volume: bool, - pub(super) max_volume: bool, - pub(super) publics: bool, - pub(super) address: bool, - pub(super) age: bool, - pub(super) conf_settings: bool, - pub(super) asks_limit: Option, - pub(super) bids_limit: Option, -} diff --git a/mm2src/adex_cli/src/adex_proc/response_handler.rs b/mm2src/adex_cli/src/adex_proc/response_handler.rs deleted file mode 100644 index 74a0838136..0000000000 --- a/mm2src/adex_cli/src/adex_proc/response_handler.rs +++ /dev/null @@ -1,207 +0,0 @@ -#[path = "response_handler/orderbook.rs"] mod orderbook; -#[path = "response_handler/smart_fraction_fmt.rs"] -mod smart_fraction_fmt; - -pub(crate) use smart_fraction_fmt::SmartFractPrecision; - -use anyhow::{anyhow, Result}; -use itertools::Itertools; -use log::{error, info}; -use mm2_rpc::data::legacy::{BalanceResponse, CoinInitResponse, GetEnabledResponse, Mm2RpcResult, MmVersionResponse, - OrderbookResponse, SellBuyResponse, Status}; -use serde_json::Value as Json; -use std::cell::RefCell; -use std::fmt::Debug; -use std::io::Write; - -use super::OrderbookConfig; -use crate::adex_config::AdexConfig; -use crate::error_anyhow; -use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; - -pub(crate) trait ResponseHandler { - fn print_response(&self, response: Json) -> Result<()>; - fn debug_response(&self, response: &T) -> Result<()>; - fn on_orderbook_response( - &self, - orderbook: &OrderbookResponse, - config: &Cfg, - orderbook_config: OrderbookConfig, - ) -> Result<()>; - fn on_get_enabled_response(&self, enabled: &Mm2RpcResult) -> Result<()>; - fn on_version_response(&self, response: &MmVersionResponse) -> Result<()>; - fn on_enable_response(&self, response: &CoinInitResponse) -> Result<()>; - fn on_balance_response(&self, response: &BalanceResponse) -> Result<()>; - fn on_sell_response(&self, response: &Mm2RpcResult) -> Result<()>; - fn on_buy_response(&self, response: &Mm2RpcResult) -> Result<()>; - fn on_stop_response(&self, response: &Mm2RpcResult) -> Result<()>; -} - -pub(crate) struct ResponseHandlerImpl<'a> { - pub(crate) writer: RefCell<&'a mut dyn Write>, -} - -impl ResponseHandler for ResponseHandlerImpl<'_> { - fn print_response(&self, result: Json) -> Result<()> { - let object = result - .as_object() - .ok_or_else(|| error_anyhow!("Failed to cast result as object"))?; - - object - .iter() - .map(SimpleCliTable::from_pair) - .for_each(|value| writeln_safe_io!(self.writer.borrow_mut(), "{}: {:?}", value.key, value.value)); - Ok(()) - } - - fn debug_response(&self, response: &T) -> Result<()> { - info!("{response:?}"); - Ok(()) - } - - fn on_orderbook_response( - &self, - orderbook: &OrderbookResponse, - config: &Cfg, - orderbook_config: OrderbookConfig, - ) -> Result<()> { - let mut writer = self.writer.borrow_mut(); - - let base_vol_head = format!("Volume: {}", orderbook.base); - let rel_price_head = format!("Price: {}", orderbook.rel); - writeln_safe_io!( - writer, - "{}", - orderbook::AskBidRow::new( - base_vol_head.as_str(), - rel_price_head.as_str(), - "Uuid", - "Min volume", - "Max volume", - "Age(sec.)", - "Public", - "Address", - "Order conf (bc,bn:rc,rn)", - &orderbook_config - ) - ); - - let price_prec = config.orderbook_price_precision(); - let vol_prec = config.orderbook_volume_precision(); - - if orderbook.asks.is_empty() { - writeln_safe_io!( - writer, - "{}", - orderbook::AskBidRow::new("", "No asks found", "", "", "", "", "", "", "", &orderbook_config) - ); - } else { - let skip = orderbook - .asks - .len() - .checked_sub(orderbook_config.asks_limit.unwrap_or(usize::MAX)) - .unwrap_or_default(); - - orderbook - .asks - .iter() - .sorted_by(orderbook::cmp_asks) - .skip(skip) - .map(|entry| orderbook::AskBidRow::from_orderbook_entry(entry, vol_prec, price_prec, &orderbook_config)) - .for_each(|row: orderbook::AskBidRow| writeln_safe_io!(writer, "{}", row)); - } - writeln_safe_io!(writer, "{}", orderbook::AskBidRow::new_delimiter(&orderbook_config)); - - if orderbook.bids.is_empty() { - writeln_safe_io!( - writer, - "{}", - orderbook::AskBidRow::new("", "No bids found", "", "", "", "", "", "", "", &orderbook_config) - ); - } else { - orderbook - .bids - .iter() - .sorted_by(orderbook::cmp_bids) - .take(orderbook_config.bids_limit.unwrap_or(usize::MAX)) - .map(|entry| orderbook::AskBidRow::from_orderbook_entry(entry, vol_prec, price_prec, &orderbook_config)) - .for_each(|row: orderbook::AskBidRow| writeln_safe_io!(writer, "{}", row)); - } - Ok(()) - } - - fn on_get_enabled_response(&self, enabled: &Mm2RpcResult) -> Result<()> { - let mut writer = self.writer.borrow_mut(); - writeln_safe_io!(writer, "{:8} {}", "Ticker", "Address"); - for row in &enabled.result { - writeln_safe_io!(writer, "{:8} {}", row.ticker, row.address); - } - Ok(()) - } - - fn on_version_response(&self, response: &MmVersionResponse) -> Result<()> { - let mut writer = self.writer.borrow_mut(); - writeln_safe_io!(writer, "Version: {}", response.result); - writeln_safe_io!(writer, "Datetime: {}", response.datetime); - Ok(()) - } - - fn on_enable_response(&self, response: &CoinInitResponse) -> Result<()> { - let mut writer = self.writer.borrow_mut(); - writeln_safe_io!( - writer, - "coin: {}\naddress: {}\nbalance: {}\nunspendable_balance: {}\nrequired_confirmations: {}\nrequires_notarization: {}", - response.coin, - response.address, - response.balance, - response.unspendable_balance, - response.required_confirmations, - if response.requires_notarization { "Yes" } else { "No" } - ); - if let Some(mature_confirmations) = response.mature_confirmations { - writeln_safe_io!(writer, "mature_confirmations: {}", mature_confirmations); - } - Ok(()) - } - - fn on_balance_response(&self, response: &BalanceResponse) -> Result<()> { - writeln_safe_io!( - self.writer.borrow_mut(), - "coin: {}\nbalance: {}\nunspendable: {}\naddress: {}", - response.coin, - response.balance, - response.unspendable_balance, - response.address - ); - Ok(()) - } - - fn on_sell_response(&self, response: &Mm2RpcResult) -> Result<()> { - writeln_safe_io!(self.writer.borrow_mut(), "Order uuid: {}", response.request.uuid); - Ok(()) - } - - fn on_buy_response(&self, response: &Mm2RpcResult) -> Result<()> { - writeln_safe_io!(self.writer.borrow_mut(), "Buy order uuid: {}", response.request.uuid); - Ok(()) - } - - fn on_stop_response(&self, response: &Mm2RpcResult) -> Result<()> { - writeln_safe_io!(self.writer.borrow_mut(), "Service stopped: {}", response.result); - Ok(()) - } -} - -struct SimpleCliTable<'a> { - key: &'a String, - value: &'a Json, -} - -impl<'a> SimpleCliTable<'a> { - fn from_pair(pair: (&'a String, &'a Json)) -> Self { - SimpleCliTable { - key: pair.0, - value: pair.1, - } - } -} diff --git a/mm2src/adex_cli/src/adex_proc/response_handler/orderbook.rs b/mm2src/adex_cli/src/adex_proc/response_handler/orderbook.rs deleted file mode 100644 index c3641f5aa5..0000000000 --- a/mm2src/adex_cli/src/adex_proc/response_handler/orderbook.rs +++ /dev/null @@ -1,181 +0,0 @@ -use mm2_number::bigdecimal::ToPrimitive; -use mm2_rpc::data::legacy::{AggregatedOrderbookEntry, OrderConfirmationsSettings}; -use std::cmp::Ordering; -use std::fmt::{Display, Formatter}; - -use super::{smart_fraction_fmt::{SmartFractPrecision, SmartFractionFmt}, - OrderbookConfig}; - -pub(super) fn cmp_bids(left: &&AggregatedOrderbookEntry, right: &&AggregatedOrderbookEntry) -> Ordering { - let cmp = left.entry.price.cmp(&right.entry.price).reverse(); - if cmp.is_eq() { - return left - .entry - .base_max_volume - .base_max_volume - .cmp(&right.entry.base_max_volume.base_max_volume) - .reverse(); - } - cmp -} - -pub(super) fn cmp_asks(left: &&AggregatedOrderbookEntry, right: &&AggregatedOrderbookEntry) -> Ordering { - let cmp = left.entry.price.cmp(&right.entry.price).reverse(); - if cmp.is_eq() { - return left - .entry - .base_max_volume - .base_max_volume - .cmp(&right.entry.base_max_volume.base_max_volume); - } - cmp -} - -enum AskBidRowVal { - Value(String), - Delim, -} - -pub(super) struct AskBidRow<'a> { - volume: AskBidRowVal, - price: AskBidRowVal, - uuid: AskBidRowVal, - min_volume: AskBidRowVal, - max_volume: AskBidRowVal, - age: AskBidRowVal, - public: AskBidRowVal, - address: AskBidRowVal, - is_mine: AskBidRowVal, - conf_settings: AskBidRowVal, - config: &'a OrderbookConfig, -} - -impl<'a> AskBidRow<'a> { - #[allow(clippy::too_many_arguments)] - pub(crate) fn new( - volume: &str, - price: &str, - uuid: &str, - min_volume: &str, - max_volume: &str, - age: &str, - public: &str, - address: &str, - conf_settings: &str, - config: &'a OrderbookConfig, - ) -> Self { - Self { - is_mine: AskBidRowVal::Value(String::new()), - volume: AskBidRowVal::Value(volume.to_string()), - price: AskBidRowVal::Value(price.to_string()), - uuid: AskBidRowVal::Value(uuid.to_string()), - min_volume: AskBidRowVal::Value(min_volume.to_string()), - max_volume: AskBidRowVal::Value(max_volume.to_string()), - age: AskBidRowVal::Value(age.to_string()), - public: AskBidRowVal::Value(public.to_string()), - address: AskBidRowVal::Value(address.to_string()), - conf_settings: AskBidRowVal::Value(conf_settings.to_string()), - config, - } - } - - pub(super) fn new_delimiter(config: &'a OrderbookConfig) -> Self { - Self { - is_mine: AskBidRowVal::Delim, - volume: AskBidRowVal::Delim, - price: AskBidRowVal::Delim, - uuid: AskBidRowVal::Delim, - min_volume: AskBidRowVal::Delim, - max_volume: AskBidRowVal::Delim, - age: AskBidRowVal::Delim, - public: AskBidRowVal::Delim, - address: AskBidRowVal::Delim, - conf_settings: AskBidRowVal::Delim, - config, - } - } - - pub(super) fn from_orderbook_entry( - entry: &AggregatedOrderbookEntry, - vol_prec: &SmartFractPrecision, - price_prec: &SmartFractPrecision, - config: &'a OrderbookConfig, - ) -> Self { - AskBidRow { - is_mine: AskBidRowVal::Value((if entry.entry.is_mine { "*" } else { "" }).to_string()), - volume: AskBidRowVal::Value( - SmartFractionFmt::new( - vol_prec.0, - vol_prec.1, - entry.entry.base_max_volume.base_max_volume.to_f64().unwrap(), - ) - .expect("volume smart fraction should be constructed properly") - .to_string(), - ), - price: AskBidRowVal::Value( - SmartFractionFmt::new(price_prec.0, price_prec.1, entry.entry.price.to_f64().unwrap()) - .expect("price smart fraction should be constructed properly") - .to_string(), - ), - uuid: AskBidRowVal::Value(entry.entry.uuid.to_string()), - min_volume: AskBidRowVal::Value( - SmartFractionFmt::new(vol_prec.0, vol_prec.1, entry.entry.min_volume.to_f64().unwrap()) - .expect("min_volume smart fraction should be constructed properly") - .to_string(), - ), - max_volume: AskBidRowVal::Value( - SmartFractionFmt::new(vol_prec.0, vol_prec.1, entry.entry.max_volume.to_f64().unwrap()) - .expect("max_volume smart fraction should be constructed properly") - .to_string(), - ), - age: AskBidRowVal::Value(entry.entry.age.to_string()), - public: AskBidRowVal::Value(entry.entry.pubkey.clone()), - address: AskBidRowVal::Value(entry.entry.address.clone()), - conf_settings: AskBidRowVal::Value( - entry - .entry - .conf_settings - .as_ref() - .map_or("none".to_string(), format_confirmation_settings), - ), - config, - } - } -} - -impl Display for AskBidRow<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - macro_rules! write_ask_bid_row { - ($value: expr, $width: expr, $alignment: literal) => { - if let AskBidRowVal::Value(value) = &$value { - write!(f, concat!("{:", $alignment, "width$} "), value, width = $width)?; - } else { - write!(f, "{:- { - if $config { - write_ask_bid_row!($value, $width, $alignment); - } - }; - } - write_ask_bid_row!(self.is_mine, 1, "<"); - write_ask_bid_row!(self.volume, 15, ">"); - write_ask_bid_row!(self.price, 13, "<"); - write_ask_bid_row!(self.config.uuids, self.uuid, 36, "<"); - write_ask_bid_row!(self.config.min_volume, self.min_volume, 10, "<"); - write_ask_bid_row!(self.config.max_volume, self.max_volume, 10, "<"); - write_ask_bid_row!(self.config.age, self.age, 10, "<"); - write_ask_bid_row!(self.config.publics, self.public, 66, "<"); - write_ask_bid_row!(self.config.address, self.address, 34, "<"); - write_ask_bid_row!(self.config.conf_settings, self.conf_settings, 24, "<"); - Ok(()) - } -} - -fn format_confirmation_settings(settings: &OrderConfirmationsSettings) -> String { - format!( - "{},{}:{},{}", - settings.base_confs, settings.base_nota, settings.rel_confs, settings.rel_nota - ) -} diff --git a/mm2src/adex_cli/src/cli.rs b/mm2src/adex_cli/src/cli.rs deleted file mode 100644 index 3ab9672a1c..0000000000 --- a/mm2src/adex_cli/src/cli.rs +++ /dev/null @@ -1,315 +0,0 @@ -use anyhow::Result; -use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum}; -use common::serde_derive::Serialize; -use mm2_number::{bigdecimal::ParseBigDecimalError, BigDecimal, MmNumber}; -use mm2_rpc::data::legacy::{MatchBy, OrderType, SellBuyRequest}; -use rpc::v1::types::H256 as H256Json; -use std::collections::HashSet; -use std::mem::take; -use std::str::FromStr; -use uuid::Uuid; - -use crate::adex_config::{get_config, set_config, AdexConfig}; -use crate::adex_proc::{AdexProc, OrderbookConfig, ResponseHandler}; -use crate::scenarios::{get_status, init, start_process, stop_process}; -use crate::transport::SlurpTransport; - -const MM2_CONFIG_FILE_DEFAULT: &str = "MM2.json"; -const COINS_FILE_DEFAULT: &str = "coins"; -const ORDERBOOK_BIDS_LIMIT: &str = "20"; -const ORDERBOOK_ASKS_LIMIT: &str = "20"; - -#[derive(Subcommand)] -enum Command { - #[command(about = "Initialize a predefined coin set and configuration to start mm2 instance with")] - Init { - #[arg(long, help = "coin set file path", default_value = COINS_FILE_DEFAULT)] - mm_coins_path: String, - #[arg(long, help = "mm2 configuration file path", default_value = MM2_CONFIG_FILE_DEFAULT)] - mm_conf_path: String, - }, - #[command(about = "Start mm2 instance")] - Start { - #[arg(long, help = "mm2 configuration file path")] - mm_conf_path: Option, - #[arg(long, help = "coin set file path")] - mm_coins_path: Option, - #[arg(long, help = "log file path")] - mm_log: Option, - }, - #[command(about = "Stop mm2 using API")] - Stop, - #[command(about = "Kill mm2 process")] - Kill, - #[command(about = "Get mm2 running status")] - Status, - #[command(about = "Gets version of intermediary mm2 service")] - Version, - #[command(subcommand, about = "To manage rpc_password and mm2 RPC URL")] - Config(ConfigSubcommand), - #[command(about = "Puts an asset to the trading index")] - Enable { - #[arg(name = "ASSET", help = "Asset to be included into the trading index")] - asset: String, - }, - #[command(about = "Gets balance of an asset")] - Balance { - #[arg(name = "ASSET", help = "Asset to get balance of")] - asset: String, - }, - #[command(about = "Lists activated assets")] - GetEnabled, - #[command(about = "Gets orderbook")] - Orderbook { - #[command(flatten)] - orderbook_args: OrderbookCliArgs, - }, - Sell { - #[command(flatten)] - order_args: SellOrderCli, - }, - Buy { - #[command(flatten)] - order_args: BuyOrderCli, - }, -} - -#[derive(Subcommand)] -enum ConfigSubcommand { - #[command(about = "Sets komodo adex cli configuration")] - Set(SetConfigArgs), - #[command(about = "Gets komodo adex cli configuration")] - Get, -} - -#[derive(Args)] -#[group(required = true, multiple = true)] -struct SetConfigArgs { - #[arg(long, help = "Set if you are going to set up a password")] - set_password: bool, - #[arg(long, name = "URI", help = "Adex RPC API Uri. http://localhost:7783")] - adex_uri: Option, -} - -#[derive(Parser)] -#[clap(author, version, about, long_about = None)] -pub(super) struct Cli { - #[command(subcommand)] - command: Command, -} - -impl Cli { - pub(super) async fn execute( - args: impl Iterator, - config: &Cfg, - printer: &P, - ) -> Result<()> { - let transport = config.rpc_uri().map(SlurpTransport::new); - - let proc = AdexProc { - transport: transport.as_ref(), - response_handler: printer, - config, - }; - - let mut parsed_cli = Self::parse_from(args); - match &mut parsed_cli.command { - Command::Init { - mm_coins_path: coins_file, - mm_conf_path: mm2_cfg_file, - } => init(mm2_cfg_file, coins_file).await, - Command::Start { - mm_conf_path: mm2_cfg_file, - mm_coins_path: coins_file, - mm_log: log_file, - } => start_process(mm2_cfg_file, coins_file, log_file), - Command::Version => proc.get_version().await?, - Command::Kill => stop_process(), - Command::Status => get_status(), - Command::Stop => proc.send_stop().await?, - Command::Config(ConfigSubcommand::Set(SetConfigArgs { set_password, adex_uri })) => { - set_config(*set_password, adex_uri.take())? - }, - Command::Config(ConfigSubcommand::Get) => get_config(), - Command::Enable { asset } => proc.enable(asset).await?, - Command::Balance { asset } => proc.get_balance(asset).await?, - Command::GetEnabled => proc.get_enabled().await?, - Command::Orderbook { ref orderbook_args } => { - proc.get_orderbook( - &orderbook_args.base, - &orderbook_args.rel, - OrderbookConfig::from(orderbook_args), - ) - .await? - }, - Command::Sell { - order_args: SellOrderCli { order_cli }, - } => proc.sell(SellBuyRequest::from(order_cli)).await?, - Command::Buy { - order_args: BuyOrderCli { order_cli }, - } => proc.buy(SellBuyRequest::from(order_cli)).await?, - } - Ok(()) - } -} - -#[derive(Args)] -#[command(about = "Puts a selling coins request")] -struct SellOrderCli { - #[command(flatten)] - order_cli: OrderCli, -} - -#[derive(Args)] -#[command(about = "Puts a buying coins request")] -struct BuyOrderCli { - #[command(flatten)] - order_cli: OrderCli, -} - -#[derive(Args, Serialize, Debug)] -struct OrderbookCliArgs { - #[arg(help = "Base currency of a pair")] - base: String, - #[arg(help = "Related currency, also can be called \"quote currency\" according to exchange terms")] - rel: String, - #[arg(long, help = "Orderbook asks count limitation", default_value = ORDERBOOK_ASKS_LIMIT)] - asks_limit: Option, - #[arg(long, help = "Orderbook bids count limitation", default_value = ORDERBOOK_BIDS_LIMIT)] - bids_limit: Option, - #[arg(long, help = "Enables `uuid` column")] - uuids: bool, - #[arg(long, help = "Enables `min_volume` column")] - min_volume: bool, - #[arg(long, help = "Enables `max_volume` column")] - max_volume: bool, - #[arg(long, help = "Enables `public` column")] - publics: bool, - #[arg(long, help = "Enables `address` column")] - address: bool, - #[arg(long, help = "Enables `age` column")] - age: bool, - #[arg(long, help = "Enables order confirmation settings column")] - conf_settings: bool, -} - -impl From<&OrderbookCliArgs> for OrderbookConfig { - fn from(value: &OrderbookCliArgs) -> Self { - OrderbookConfig { - uuids: value.uuids, - min_volume: value.min_volume, - max_volume: value.max_volume, - publics: value.publics, - address: value.address, - age: value.age, - conf_settings: value.conf_settings, - asks_limit: value.asks_limit, - bids_limit: value.bids_limit, - } - } -} - -#[derive(Args, Serialize, Debug)] -struct OrderCli { - #[arg(help = "Base currency of a pair")] - base: String, - #[arg(help = "Related currency")] - rel: String, - #[arg(help = "Amount of coins the user is willing to sell/buy of the base coin", value_parser=parse_mm_number )] - volume: MmNumber, - #[arg(help = "Price in rel the user is willing to receive/pay per one unit of the base coin", value_parser=parse_mm_number)] - price: MmNumber, - #[arg(long, value_enum, default_value_t = OrderTypeCli::GoodTillCancelled, help="The GoodTillCancelled order is automatically converted to a maker order if not matched in 30 seconds, and this maker order stays in the orderbook until explicitly cancelled. On the other hand, a FillOrKill is cancelled if not matched within 30 seconds")] - order_type: OrderTypeCli, - #[arg(long, - help = "Amount of base coin that will be used as min_volume of GoodTillCancelled order after conversion to maker", - value_parser=parse_mm_number - )] - min_volume: Option, - #[arg(short='u', long="uuid", action = ArgAction::Append, help="The created order is matched using a set of uuid")] - match_uuids: Vec, - #[arg(short='p', - long="public", - value_parser=H256Json::from_str, - action = ArgAction::Append, - help="The created order is matched using a set of publics to select specific nodes (ignored if uuids not empty)")] - match_publics: Vec, - #[arg( - long, - help = "Number of required blockchain confirmations for base coin atomic swap transaction" - )] - base_confs: Option, - #[arg( - long, - help = "Whether dPoW notarization is required for base coin atomic swap transaction" - )] - base_nota: Option, - #[arg( - long, - help = "Number of required blockchain confirmations for rel coin atomic swap transaction" - )] - rel_confs: Option, - #[arg( - long, - help = "Whether dPoW notarization is required for rel coin atomic swap transaction" - )] - rel_nota: Option, - #[arg( - long, - help = "If true, each order's short record history is stored else the only order status will be temporarily stored while in progress" - )] - save_in_history: bool, -} - -fn parse_mm_number(value: &str) -> Result { - let decimal: BigDecimal = BigDecimal::from_str(value)?; - Ok(MmNumber::from(decimal)) -} - -#[derive(Debug, Copy, Clone, ValueEnum, Serialize)] -enum OrderTypeCli { - FillOrKill, - GoodTillCancelled, -} - -impl From for OrderType { - fn from(value: OrderTypeCli) -> Self { - match value { - OrderTypeCli::GoodTillCancelled => OrderType::GoodTillCancelled, - OrderTypeCli::FillOrKill => OrderType::FillOrKill, - } - } -} - -impl From<&mut OrderCli> for SellBuyRequest { - fn from(value: &mut OrderCli) -> Self { - let match_by = if !value.match_uuids.is_empty() { - MatchBy::Orders(HashSet::from_iter(value.match_uuids.drain(..))) - } else if !value.match_publics.is_empty() { - MatchBy::Pubkeys(HashSet::from_iter(value.match_publics.drain(..))) - } else { - MatchBy::Any - }; - - let will_be_substituted = String::new(); - SellBuyRequest { - base: take(&mut value.base), - rel: take(&mut value.rel), - price: take(&mut value.price), - volume: take(&mut value.volume), - timeout: None, - duration: None, - method: will_be_substituted, - gui: None, - dest_pub_key: H256Json::default(), - match_by, - order_type: value.order_type.into(), - base_confs: value.base_confs, - base_nota: value.base_nota, - rel_confs: value.rel_confs, - rel_nota: value.rel_nota, - min_volume: take(&mut value.min_volume), - save_in_history: value.save_in_history, - } - } -} diff --git a/mm2src/adex_cli/src/rpc_data.rs b/mm2src/adex_cli/src/rpc_data.rs deleted file mode 100644 index f8e1329453..0000000000 --- a/mm2src/adex_cli/src/rpc_data.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Contains rpc data layer structures that are not ready to become a part of the mm2_rpc::data module -//! -//! *Note: it's expected that the following data types will be moved to mm2_rpc::data when mm2 is refactored to be able to handle them* -//! - -use mm2_rpc::data::legacy::{ElectrumProtocol, GasStationPricePolicy, UtxoMergeParams}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Deserialize, Serialize)] -#[serde(tag = "method", rename_all = "lowercase")] -pub(crate) enum ActivationRequest { - Enable(EnableRequest), - Electrum(ElectrumRequest), -} - -#[derive(Debug, Deserialize, Serialize)] -pub(crate) struct EnableRequest { - coin: String, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - urls: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - swap_contract_address: Option, - #[serde(skip_serializing_if = "Option::is_none")] - fallback_swap_contract: Option, - #[serde(skip_serializing_if = "Option::is_none")] - gas_station_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - gas_station_decimals: Option, - #[serde(skip_serializing_if = "Option::is_none")] - gas_station_policy: Option, - #[serde(skip_serializing_if = "Option::is_none")] - mm2: Option, - #[serde(default)] - tx_history: bool, - #[serde(skip_serializing_if = "Option::is_none")] - required_confirmations: Option, - #[serde(skip_serializing_if = "Option::is_none")] - requires_notarization: Option, - #[serde(default)] - contract_supports_watchers: Option, -} - -#[derive(Debug, Deserialize, Serialize)] -pub(crate) struct ElectrumRequest { - coin: String, - #[serde(skip_serializing_if = "Vec::is_empty")] - pub(super) servers: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - mm2: Option, - #[serde(default)] - tx_history: bool, - #[serde(skip_serializing_if = "Option::is_none")] - required_confirmations: Option, - #[serde(skip_serializing_if = "Option::is_none")] - requires_notarization: Option, - #[serde(skip_serializing_if = "Option::is_none")] - swap_contract_address: Option, - #[serde(skip_serializing_if = "Option::is_none")] - fallback_swap_contract: Option, - #[serde(skip_serializing_if = "Option::is_none")] - utxo_merge_params: Option, -} - -#[derive(Debug, Deserialize, Serialize)] -pub(super) struct Server { - url: String, - #[serde(default)] - protocol: ElectrumProtocol, - #[serde(default)] - disable_cert_verification: bool, -} diff --git a/mm2src/adex_cli/src/tests/mod.rs b/mm2src/adex_cli/src/tests/mod.rs deleted file mode 100644 index 9777a268da..0000000000 --- a/mm2src/adex_cli/src/tests/mod.rs +++ /dev/null @@ -1,255 +0,0 @@ -use std::io::Write; -use std::time::Duration; -use tokio::io::AsyncWriteExt; -use tokio::net::{TcpListener, TcpStream}; - -use crate::activation_scheme_db::{get_activation_scheme, get_activation_scheme_path, init_activation_scheme}; -use crate::adex_config::AdexConfigImpl; -use crate::adex_proc::ResponseHandlerImpl; -use crate::cli::Cli; -use crate::rpc_data::ActivationRequest; - -const FAKE_SERVER_COOLDOWN_TIMEOUT_MS: u64 = 10; -const FAKE_SERVER_WARMUP_TIMEOUT_MS: u64 = 100; - -#[tokio::test] -async fn test_get_version() { - tokio::spawn(fake_mm2_server(7784, include_bytes!("http_mock_data/version.http"))); - tokio::time::sleep(Duration::from_millis(FAKE_SERVER_WARMUP_TIMEOUT_MS)).await; - let mut buffer: Vec = vec![]; - let response_handler = ResponseHandlerImpl { - writer: (&mut buffer as &mut dyn Write).into(), - }; - let config = AdexConfigImpl::new("dummy", "http://127.0.0.1:7784"); - let args = vec!["adex-cli", "version"]; - Cli::execute(args.iter().map(|arg| arg.to_string()), &config, &response_handler) - .await - .unwrap(); - - let result = String::from_utf8(buffer).unwrap(); - assert_eq!( - "Version: 1.0.1-beta_824ca36f3\nDatetime: 2023-04-06T22:35:43+05:00\n", - result - ); -} - -#[tokio::test] -async fn test_get_orderbook() { - tokio::spawn(fake_mm2_server(7785, include_bytes!("http_mock_data/orderbook.http"))); - tokio::time::sleep(Duration::from_millis(FAKE_SERVER_WARMUP_TIMEOUT_MS)).await; - let mut buffer: Vec = vec![]; - let response_handler = ResponseHandlerImpl { - writer: (&mut buffer as &mut dyn Write).into(), - }; - let config = AdexConfigImpl::new("dummy", "http://127.0.0.1:7785"); - let args = vec!["adex-cli", "orderbook", "RICK", "MORTY"]; - Cli::execute(args.iter().map(|arg| arg.to_string()), &config, &response_handler) - .await - .unwrap(); - - let result = String::from_utf8(buffer).unwrap(); - assert_eq!(RICK_AND_MORTY_ORDERBOOK, result); -} - -#[tokio::test] -async fn test_get_orderbook_with_uuids() { - tokio::spawn(fake_mm2_server(7786, include_bytes!("http_mock_data/orderbook.http"))); - tokio::time::sleep(Duration::from_millis(FAKE_SERVER_WARMUP_TIMEOUT_MS)).await; - let mut buffer: Vec = vec![]; - let response_handler = ResponseHandlerImpl { - writer: (&mut buffer as &mut dyn Write).into(), - }; - let config = AdexConfigImpl::new("dummy", "http://127.0.0.1:7786"); - let args = vec!["adex-cli", "orderbook", "RICK", "MORTY", "--uuids"]; - Cli::execute(args.iter().map(|arg| arg.to_string()), &config, &response_handler) - .await - .unwrap(); - - let result = String::from_utf8(buffer).unwrap(); - assert_eq!(RICK_AND_MORTY_ORDERBOOK_WITH_UUIDS, result); -} - -#[tokio::test] -async fn test_get_orderbook_with_publics() { - tokio::spawn(fake_mm2_server(7787, include_bytes!("http_mock_data/orderbook.http"))); - tokio::time::sleep(Duration::from_millis(FAKE_SERVER_WARMUP_TIMEOUT_MS)).await; - let mut buffer: Vec = vec![]; - let response_handler = ResponseHandlerImpl { - writer: (&mut buffer as &mut dyn Write).into(), - }; - let config = AdexConfigImpl::new("dummy", "http://127.0.0.1:7787"); - let args = vec!["adex-cli", "orderbook", "RICK", "MORTY", "--publics"]; - Cli::execute(args.iter().map(|arg| arg.to_string()), &config, &response_handler) - .await - .unwrap(); - - let result = String::from_utf8(buffer).unwrap(); - assert_eq!(RICK_AND_MORTY_ORDERBOOK_WITH_PUBLICS, result); -} - -#[tokio::test] -async fn test_get_enabled() { - tokio::spawn(fake_mm2_server(7788, include_bytes!("http_mock_data/get_enabled.http"))); - tokio::time::sleep(Duration::from_millis(FAKE_SERVER_WARMUP_TIMEOUT_MS)).await; - let mut buffer: Vec = vec![]; - let response_handler = ResponseHandlerImpl { - writer: (&mut buffer as &mut dyn Write).into(), - }; - let config = AdexConfigImpl::new("dummy", "http://127.0.0.1:7788"); - let args = vec!["adex-cli", "get-enabled"]; - Cli::execute(args.iter().map(|arg| arg.to_string()), &config, &response_handler) - .await - .unwrap(); - - let result = String::from_utf8(buffer).unwrap(); - assert_eq!(ENABLED_COINS, result); -} - -#[tokio::test] -async fn test_get_balance() { - tokio::spawn(fake_mm2_server(7789, include_bytes!("http_mock_data/balance.http"))); - tokio::time::sleep(Duration::from_millis(FAKE_SERVER_WARMUP_TIMEOUT_MS)).await; - let mut buffer: Vec = vec![]; - let response_handler = ResponseHandlerImpl { - writer: (&mut buffer as &mut dyn Write).into(), - }; - let config = AdexConfigImpl::new("dummy", "http://127.0.0.1:7789"); - let args = vec!["adex-cli", "balance", "RICK"]; - Cli::execute(args.iter().map(|arg| arg.to_string()), &config, &response_handler) - .await - .unwrap(); - - let result = String::from_utf8(buffer).unwrap(); - assert_eq!(RICK_BALANCE, result); -} - -#[tokio::test] -async fn test_enable() { - tokio::spawn(fake_mm2_server(7790, include_bytes!("http_mock_data/enable.http"))); - test_activation_scheme().await; - tokio::time::sleep(Duration::from_millis(FAKE_SERVER_WARMUP_TIMEOUT_MS)).await; - let mut buffer: Vec = vec![]; - let response_handler = ResponseHandlerImpl { - writer: (&mut buffer as &mut dyn Write).into(), - }; - let config = AdexConfigImpl::new("dummy", "http://127.0.0.1:7790"); - let args = vec!["adex-cli", "enable", "ETH"]; - Cli::execute(args.iter().map(|arg| arg.to_string()), &config, &response_handler) - .await - .unwrap(); - - let result = String::from_utf8(buffer).unwrap(); - assert_eq!(ENABLE_OUTPUT, result); -} - -async fn test_activation_scheme() { - let _ = std::fs::remove_file(get_activation_scheme_path().unwrap()); - init_activation_scheme().await.unwrap(); - let scheme = get_activation_scheme().unwrap(); - let kmd_scheme = scheme.get_activation_method("KMD"); - let Ok(ActivationRequest::Electrum(electrum)) = kmd_scheme else { - panic!("Failed to get electrum scheme") - }; - assert_ne!(electrum.servers.len(), 0); -} - -#[tokio::test] -async fn test_buy_morty_for_rick() { - tokio::spawn(fake_mm2_server(7791, include_bytes!("http_mock_data/buy.http"))); - tokio::time::sleep(Duration::from_millis(FAKE_SERVER_WARMUP_TIMEOUT_MS)).await; - let mut buffer: Vec = vec![]; - let response_handler = ResponseHandlerImpl { - writer: (&mut buffer as &mut dyn Write).into(), - }; - let config = AdexConfigImpl::new("dummy", "http://127.0.0.1:7791"); - let args = vec!["adex-cli", "buy", "MORTY", "RICK", "0.01", "0.5"]; - Cli::execute(args.iter().map(|arg| arg.to_string()), &config, &response_handler) - .await - .unwrap(); - - let result = String::from_utf8(buffer).unwrap(); - assert_eq!("Buy order uuid: 4685e133-dfb3-4b31-8d4c-0ffa79933c8e\n", result); -} - -async fn fake_mm2_server(port: u16, predefined_response: &'static [u8]) { - let server = TcpListener::bind(("0.0.0.0", port)) - .await - .expect("Failed to bind tcp server"); - - if let Ok((stream, _)) = server.accept().await { - tokio::spawn(handle_connection(stream, predefined_response)); - } -} - -async fn handle_connection(mut stream: TcpStream, predefined_response: &'static [u8]) { - let (reader, mut writer) = stream.split(); - reader.readable().await.unwrap(); - writer.write_all(predefined_response).await.unwrap(); - tokio::time::sleep(Duration::from_millis(FAKE_SERVER_COOLDOWN_TIMEOUT_MS)).await; -} - -const RICK_AND_MORTY_ORDERBOOK: &str = r" Volume: RICK Price: MORTY - 0.23 1.00000000 - 340654.03 1.00000000 - 2.00 0.99999999 - 2.00 0.99999999 - 2.00 0.99999999 -- --------------- ------------- - 0.96 1.02438024 - 1.99 1.00000001 - 1.99 1.00000001 - 1.99 1.00000001 - 32229.14 1.00000000 - 0.22 1.00000000 -"; - -const RICK_AND_MORTY_ORDERBOOK_WITH_UUIDS: &str = r" Volume: RICK Price: MORTY Uuid - 0.23 1.00000000 c7585a1b-6060-4319-9da6-c67321628a06 - 340654.03 1.00000000 d69fe2a9-51ca-4d69-96ad-b141a01d8bb4 - 2.00 0.99999999 a2337218-7f6f-46a1-892e-6febfb7f5403 - 2.00 0.99999999 c172c295-7fe3-4131-9c81-c3a7182f0617 - 2.00 0.99999999 fbbc44d2-fb50-4b4b-8ac3-d9857cae16b6 -- --------------- ------------- ------------------------------------ - 0.96 1.02438024 c480675b-3352-4159-9b3c-55cb2b1329de - 1.99 1.00000001 fdb0de9c-e283-48c3-9de6-8117fecf0aff - 1.99 1.00000001 6a3bb75d-8e91-4192-bf50-d8190a69600d - 1.99 1.00000001 b24b40de-e93d-4218-8d93-1940ceadce7f - 32229.14 1.00000000 652a7e97-f42c-4f87-bc26-26bd1a0fea24 - 0.22 1.00000000 1082c93c-8c23-4944-b8f1-a92ec703b03a -"; - -const RICK_AND_MORTY_ORDERBOOK_WITH_PUBLICS: &str = r" Volume: RICK Price: MORTY Public - 0.23 1.00000000 022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846 - 340654.03 1.00000000 0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732 - 2.00 0.99999999 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 - 2.00 0.99999999 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 - 2.00 0.99999999 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 -- --------------- ------------- ------------------------------------------------------------------ - 0.96 1.02438024 02d6c3e22a419a4034272acb215f1d39cd6a0413cfd83ac0c68f482db80accd89a - 1.99 1.00000001 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 - 1.99 1.00000001 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 - 1.99 1.00000001 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 - 32229.14 1.00000000 0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732 - 0.22 1.00000000 022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846 -"; - -const ENABLED_COINS: &str = r"Ticker Address -MORTY RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM -RICK RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM -KMD RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM -ETH 0x224050fb7EB13Fa0D342F5b245f1237bAB531650 -"; - -const RICK_BALANCE: &str = r"coin: RICK -balance: 0.5767226 -unspendable: 0 -address: RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM -"; - -const ENABLE_OUTPUT: &str = r"coin: ETH -address: 0x224050fb7EB13Fa0D342F5b245f1237bAB531650 -balance: 0.02 -unspendable_balance: 0 -required_confirmations: 3 -requires_notarization: No -"; diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 83a7f9d77f..d64284cadc 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -93,20 +93,21 @@ use super::{coin_conf, lp_coinfind_or_err, AsyncMutex, BalanceError, BalanceFut, EthValidateFeeArgs, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, IguanaPrivKey, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, MyAddressError, MyWalletAddress, NegotiateSwapContractAddrErr, NumConversError, NumConversResult, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, - PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, - RawTransactionRequest, RawTransactionRes, RawTransactionResult, RefundError, RefundPaymentArgs, - RefundResult, RewardTarget, RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared, - SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignEthTransactionParams, - SignRawTransactionEnum, SignRawTransactionRequest, SignatureError, SignatureResult, SpendPaymentArgs, - SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, - TradePreimageValue, Transaction, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, - TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, - ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, - ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, - WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult, - EARLY_CONFIRMATION_ERR_LOG, INVALID_CONTRACT_ADDRESS_ERR_LOG, INVALID_PAYMENT_STATE_ERR_LOG, - INVALID_RECEIVER_ERR_LOG, INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG}; + PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, RawTransactionResult, + RefundError, RefundPaymentArgs, RefundResult, RewardTarget, RpcClientType, RpcTransportEventHandler, + RpcTransportEventHandlerShared, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, + SendPaymentArgs, SignEthTransactionParams, SignRawTransactionEnum, SignRawTransactionRequest, + SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, + TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, + TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TransactionType, TxMarshalingErr, + UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, + ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, + VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, + WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult, EARLY_CONFIRMATION_ERR_LOG, + INVALID_CONTRACT_ADDRESS_ERR_LOG, INVALID_PAYMENT_STATE_ERR_LOG, INVALID_RECEIVER_ERR_LOG, + INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG}; +use mm2_rpc::data::version2::wallet::{GetRawTransactionRequest, GetRawTransactionResponse}; pub use rlp; mod eth_balance_events; @@ -633,7 +634,7 @@ impl EthCoinImpl { } } -async fn get_raw_transaction_impl(coin: EthCoin, req: RawTransactionRequest) -> RawTransactionResult { +async fn get_raw_transaction_impl(coin: EthCoin, req: GetRawTransactionRequest) -> RawTransactionResult { let tx = match req.tx_hash.strip_prefix("0x") { Some(tx) => tx, None => &req.tx_hash, @@ -648,7 +649,7 @@ async fn get_tx_hex_by_hash_impl(coin: EthCoin, tx_hash: H256) -> RawTransaction .await? .or_mm_err(|| RawTransactionError::HashNotExist(tx_hash.to_string()))?; let raw = signed_tx_from_web3_tx(web3_tx).map_to_mm(RawTransactionError::InternalError)?; - Ok(RawTransactionRes { + Ok(GetRawTransactionResponse { tx_hex: BytesJson(rlp::encode(&raw).to_vec()), }) } @@ -2534,7 +2535,7 @@ async fn sign_raw_eth_tx(coin: &EthCoin, args: &SignEthTransactionParams) -> Raw } => { return sign_transaction_with_keypair(ctx, coin, key_pair, value, action, data, args.gas_limit) .await - .map(|(signed_tx, _)| RawTransactionRes { + .map(|(signed_tx, _)| GetRawTransactionResponse { tx_hex: signed_tx.tx_hex().into(), }) .map_to_mm(|err| RawTransactionError::TransactionError(err.get_plain_text_format())); @@ -5136,7 +5137,7 @@ impl MmCoin for EthCoin { fn spawner(&self) -> CoinFutSpawner { CoinFutSpawner::new(&self.abortable_system) } - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, req: GetRawTransactionRequest) -> RawTransactionFut { Box::new(get_raw_transaction_impl(self.clone(), req).boxed().compat()) } diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 4698a595ac..1541e38eaa 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -18,16 +18,16 @@ use crate::utxo::{sat_from_big_decimal, utxo_common, BlockchainNetwork}; use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, - RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionResult, RefundError, - RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, - SendPaymentArgs, SignRawTransactionRequest, SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, - TakerSwapMakerCoin, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, - TransactionEnum, TransactionErr, TransactionFut, TransactionResult, TxMarshalingErr, - UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidateFeeArgs, - ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, - ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, VerificationResult, - WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest}; + RawTransactionError, RawTransactionFut, RawTransactionResult, RefundError, RefundPaymentArgs, + RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, + SignRawTransactionRequest, SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, + TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, TransactionEnum, + TransactionErr, TransactionFut, TransactionResult, TxMarshalingErr, UnexpectedDerivationMethod, + UtxoStandardCoin, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, + ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, + VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, + WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcoin::bech32::ToBase32; use bitcoin::hashes::Hash; @@ -66,6 +66,7 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_net::ip_addr::myipaddr; use mm2_number::{BigDecimal, MmNumber}; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; use parking_lot::Mutex as PaMutex; use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; use script::TransactionInputSigner; @@ -1270,7 +1271,7 @@ impl MmCoin for LightningCoin { fn spawner(&self) -> CoinFutSpawner { CoinFutSpawner::new(&self.platform.abortable_system) } - fn get_raw_transaction(&self, _req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, _req: GetRawTransactionRequest) -> RawTransactionFut { let fut = async move { MmError::err(RawTransactionError::InternalError( "get_raw_transaction method is not supported for lightning, please use get_payment_details method instead.".into(), diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 5179a5db80..642f4662fe 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -66,7 +66,7 @@ use mm2_err_handle::prelude::*; use mm2_metrics::MetricsWeak; use mm2_number::{bigdecimal::{BigDecimal, ParseBigDecimalError, Zero}, MmNumber}; -use mm2_rpc::data::legacy::{EnabledCoin, GetEnabledResponse, Mm2RpcResult}; +use mm2_rpc::data::legacy::{EnabledCoin, GetEnabledResponse, Mm2RpcResult, SetRequiredNotaRequest}; use parking_lot::Mutex as PaMutex; use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -225,6 +225,8 @@ pub mod hd_pubkey; pub mod hd_wallet; use hd_wallet::{HDAccountAddressId, HDAddress}; +use mm2_rpc::data::legacy::SetRequiredConfRequest; +use mm2_rpc::data::version2::wallet::{GetRawTransactionRequest, GetRawTransactionResponse}; pub mod hd_wallet_storage; #[cfg(not(target_arch = "wasm32"))] pub mod lightning; @@ -315,9 +317,9 @@ pub type TradePreimageFut = Box = Result>; pub type TxHistoryFut = Box> + Send>; pub type TxHistoryResult = Result>; -pub type RawTransactionResult = Result>; +pub type RawTransactionResult = Result>; pub type RawTransactionFut<'a> = - Box> + Send + 'a>; + Box> + Send + 'a>; pub type RefundResult = Result>; /// Helper type used for swap transactions' spend preimage generation result pub type GenPreimageResult = MmResult, TxGenError>; @@ -2951,7 +2953,7 @@ pub trait MmCoin: fn withdraw(&self, req: WithdrawRequest) -> WithdrawFut; - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut; + fn get_raw_transaction(&self, req: GetRawTransactionRequest) -> RawTransactionFut; fn get_tx_hex_by_hash(&self, tx_hash: Vec) -> RawTransactionFut; @@ -4218,7 +4220,7 @@ pub async fn withdraw(ctx: MmArc, req: WithdrawRequest) -> WithdrawResult { coin.withdraw(req).compat().await } -pub async fn get_raw_transaction(ctx: MmArc, req: RawTransactionRequest) -> RawTransactionResult { +pub async fn get_raw_transaction(ctx: MmArc, req: GetRawTransactionRequest) -> RawTransactionResult { let coin = lp_coinfind_or_err(&ctx, &req.coin).await?; coin.get_raw_transaction(req).compat().await } @@ -4422,14 +4424,8 @@ pub async fn get_enabled_coins(ctx: MmArc) -> Result>, String> Ok(try_s!(Response::builder().body(res))) } -#[derive(Deserialize)] -pub struct ConfirmationsReq { - coin: String, - confirmations: u64, -} - pub async fn set_required_confirmations(ctx: MmArc, req: Json) -> Result>, String> { - let req: ConfirmationsReq = try_s!(json::from_value(req)); + let req: SetRequiredConfRequest = try_s!(json::from_value(req)); let coin = match lp_coinfind(&ctx, &req.coin).await { Ok(Some(t)) => t, Ok(None) => return ERR!("No such coin {}", req.coin), @@ -4445,14 +4441,8 @@ pub async fn set_required_confirmations(ctx: MmArc, req: Json) -> Result Result>, String> { - let req: RequiresNotaReq = try_s!(json::from_value(req)); + let req: SetRequiredNotaRequest = try_s!(json::from_value(req)); let coin = match lp_coinfind(&ctx, &req.coin).await { Ok(Some(t)) => t, Ok(None) => return ERR!("No such coin {}", req.coin), diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 806fe3c0e2..c09143edba 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -20,11 +20,11 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, Coi DexFee, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, IguanaPrivKey, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, - RawTransactionFut, RawTransactionRequest, RawTransactionResult, RefundError, RefundPaymentArgs, - RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, - SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, - TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, - TransactionEnum, TransactionErr, TransactionFut, TransactionResult, TransactionType, TxMarshalingErr, + RawTransactionFut, RawTransactionResult, RefundError, RefundPaymentArgs, RefundResult, + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, + SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageError, + TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, + TransactionErr, TransactionFut, TransactionResult, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, @@ -48,6 +48,7 @@ use keys::{Address as UtxoAddress, KeyPair, Public}; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; #[cfg(test)] use mocktopus::macros::*; use rpc::v1::types::{Bytes as BytesJson, ToTxHash, Transaction as RpcTransaction, H160 as H160Json, H256 as H256Json}; use script::{Builder as ScriptBuilder, Opcode, Script, TransactionInputSigner}; @@ -1324,7 +1325,7 @@ impl MmCoin for Qrc20Coin { Box::new(qrc20_withdraw(self.clone(), req).boxed().compat()) } - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, req: GetRawTransactionRequest) -> RawTransactionFut { Box::new(utxo_common::get_raw_transaction(&self.utxo, req).boxed().compat()) } diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index e209c02172..6664f1a551 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -5,16 +5,16 @@ use crate::solana::spl::SplTokenInfo; use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, - PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, RawTransactionRequest, - RawTransactionResult, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, - SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, SignatureResult, - SpendPaymentArgs, TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult, TradePreimageValue, - TransactionDetails, TransactionFut, TransactionResult, TransactionType, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, - ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, - ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; + PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, RawTransactionResult, RefundError, + RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, + SendPaymentArgs, SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, TakerSwapMakerCoin, + TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, + TransactionResult, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, + ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, + ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, + WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, + WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, + WithdrawResult}; use async_trait::async_trait; use base58::ToBase58; use bincode::{deserialize, serialize}; @@ -28,6 +28,7 @@ use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; use rpc::v1::types::Bytes as BytesJson; use serde_json::{self as json, Value as Json}; use solana_client::rpc_request::TokenAccountsFilter; @@ -702,7 +703,7 @@ impl MmCoin for SolanaCoin { Box::new(Box::pin(withdraw_impl(self.clone(), req)).compat()) } - fn get_raw_transaction(&self, _req: RawTransactionRequest) -> RawTransactionFut { unimplemented!() } + fn get_raw_transaction(&self, _req: GetRawTransactionRequest) -> RawTransactionFut { unimplemented!() } fn get_tx_hex_by_hash(&self, tx_hash: Vec) -> RawTransactionFut { unimplemented!() } diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 253b1187c0..375eb40034 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -4,10 +4,10 @@ use crate::solana::solana_common::{ui_amount_to_amount, PrepareTransferData, Suf use crate::solana::{solana_common, AccountError, SolanaCommonOps, SolanaFeeDetails}; use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, - PaymentInstructions, PaymentInstructionsErr, RawTransactionError, RawTransactionFut, - RawTransactionRequest, RawTransactionResult, RefundError, RefundPaymentArgs, RefundResult, - SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, - SignatureResult, SolanaCoin, SpendPaymentArgs, TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult, + PaymentInstructions, PaymentInstructionsErr, RawTransactionError, RawTransactionFut, RawTransactionResult, + RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, + SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, SignatureResult, + SolanaCoin, SpendPaymentArgs, TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, TransactionResult, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, @@ -24,6 +24,7 @@ use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; use rpc::v1::types::Bytes as BytesJson; use serde_json::Value as Json; use solana_client::{rpc_client::RpcClient, rpc_request::TokenAccountsFilter}; @@ -523,7 +524,7 @@ impl MmCoin for SplToken { Box::new(Box::pin(withdraw_impl(self.clone(), req)).compat()) } - fn get_raw_transaction(&self, _req: RawTransactionRequest) -> RawTransactionFut { unimplemented!() } + fn get_raw_transaction(&self, _req: GetRawTransactionRequest) -> RawTransactionFut { unimplemented!() } fn get_tx_hex_by_hash(&self, tx_hash: Vec) -> RawTransactionFut { unimplemented!() } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index dbbb609e82..37ee16e443 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -18,16 +18,16 @@ use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, PrivKeyPolicy, - PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, - RawTransactionResult, RefundError, RefundPaymentArgs, RefundResult, RpcCommonOps, - SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, - SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, - TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, - TransactionEnum, TransactionErr, TransactionFut, TransactionResult, TransactionType, TxFeeDetails, - TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, - ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, - ValidateWatcherSpendInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, - WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, RawTransactionResult, RefundError, + RefundPaymentArgs, RefundResult, RpcCommonOps, SearchForSwapTxSpendInput, + SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, SignatureError, + SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageError, + TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, + TransactionErr, TransactionFut, TransactionResult, TransactionType, TxFeeDetails, TxMarshalingErr, + UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, + ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, + VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, + WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFrom, WithdrawFut, WithdrawRequest}; use async_std::prelude::FutureExt as AsyncStdFutureExt; use async_trait::async_trait; @@ -64,6 +64,7 @@ use mm2_core::mm_ctx::{MmArc, MmWeak}; use mm2_err_handle::prelude::*; use mm2_git::{FileMetadata, GitController, GithubClient, RepositoryOperations, GITHUB_API_URI}; use mm2_number::MmNumber; +use mm2_rpc::data::version2::wallet::{GetRawTransactionRequest, GetRawTransactionResponse}; use parking_lot::Mutex as PaMutex; use primitives::hash::H256; use prost::{DecodeError, Message}; @@ -2078,12 +2079,12 @@ impl MmCoin for TendermintCoin { Box::new(fut.boxed().compat()) } - fn get_raw_transaction(&self, mut req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, mut req: GetRawTransactionRequest) -> RawTransactionFut { let coin = self.clone(); let fut = async move { req.tx_hash.make_ascii_uppercase(); let tx_from_rpc = coin.request_tx(req.tx_hash).await?; - Ok(RawTransactionRes { + Ok(GetRawTransactionResponse { tx_hex: tx_from_rpc.encode_to_vec().into(), }) }; @@ -2095,7 +2096,7 @@ impl MmCoin for TendermintCoin { let hash = hex::encode_upper(H256::from(tx_hash.as_slice())); let fut = async move { let tx_from_rpc = coin.request_tx(hash).await?; - Ok(RawTransactionRes { + Ok(GetRawTransactionResponse { tx_hex: tx_from_rpc.encode_to_vec().into(), }) }; diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index 34a637ef67..404a2b7cc1 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -12,11 +12,11 @@ use crate::{big_decimal_from_sat_unsigned, utxo::sat_from_big_decimal, BalanceFu CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MyAddressError, NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, RawTransactionError, - RawTransactionFut, RawTransactionRequest, RawTransactionResult, RefundError, RefundPaymentArgs, - RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, - SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, - TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, - TransactionErr, TransactionFut, TransactionResult, TransactionType, TxFeeDetails, TxMarshalingErr, + RawTransactionFut, RawTransactionResult, RefundError, RefundPaymentArgs, RefundResult, + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, + SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageFut, + TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, + TransactionFut, TransactionResult, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherSearchForSwapTxSpendInput, @@ -38,6 +38,7 @@ use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::MmNumber; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; use rpc::v1::types::Bytes as BytesJson; use serde_json::Value as Json; use std::ops::Deref; @@ -793,7 +794,7 @@ impl MmCoin for TendermintToken { Box::new(fut.boxed().compat()) } - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, req: GetRawTransactionRequest) -> RawTransactionFut { self.platform_coin.get_raw_transaction(req) } diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 5ac52ef31d..45f62ddfd8 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -1,7 +1,7 @@ #![allow(clippy::all)] use super::{CoinBalance, FundingTxSpend, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, - RawTransactionRequest, SearchForFundingSpendErr, SwapOps, TradeFee, TransactionEnum, TransactionFut, + SearchForFundingSpendErr, SwapOps, TradeFee, TransactionEnum, TransactionFut, WaitForTakerPaymentSpendError}; use crate::coin_errors::ValidatePaymentResult; use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinAssocTypes, @@ -26,6 +26,7 @@ use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; use mocktopus::macros::*; use rpc::v1::types::Bytes as BytesJson; use serde_json::Value as Json; @@ -330,7 +331,7 @@ impl MmCoin for TestCoin { fn spawner(&self) -> CoinFutSpawner { unimplemented!() } - fn get_raw_transaction(&self, _req: RawTransactionRequest) -> RawTransactionFut { unimplemented!() } + fn get_raw_transaction(&self, _req: GetRawTransactionRequest) -> RawTransactionFut { unimplemented!() } fn get_tx_hex_by_hash(&self, tx_hash: Vec) -> RawTransactionFut { unimplemented!() } diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 1bba5184c2..dc13c76472 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -72,6 +72,7 @@ use mm2_err_handle::prelude::*; use mm2_metrics::MetricsArc; use mm2_number::BigDecimal; use mm2_rpc::data::legacy::UtxoMergeParams; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; #[cfg(test)] use mocktopus::macros::*; use num_traits::ToPrimitive; use primitives::hash::{H160, H256, H264}; @@ -105,10 +106,10 @@ use self::rpc_clients::{electrum_script_hash, ElectrumClient, ElectrumRpcRequest use super::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BalanceResult, CoinBalance, CoinFutSpawner, CoinsContext, DerivationMethod, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, KmdRewardsDetails, MarketCoinOps, MmCoin, NumConversError, NumConversResult, PrivKeyActivationPolicy, PrivKeyPolicy, - PrivKeyPolicyNotAllowed, RawTransactionFut, RpcTransportEventHandler, RpcTransportEventHandlerShared, - TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, Transaction, TransactionDetails, - TransactionEnum, TransactionErr, UnexpectedDerivationMethod, VerificationError, WithdrawError, - WithdrawRequest}; + PrivKeyPolicyNotAllowed, RawTransactionFut, RawTransactionResult, RpcTransportEventHandler, + RpcTransportEventHandlerShared, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, + Transaction, TransactionDetails, TransactionEnum, TransactionErr, UnexpectedDerivationMethod, + VerificationError, WithdrawError, WithdrawRequest}; use crate::coin_balance::{EnableCoinScanPolicy, EnabledCoinBalanceParams, HDAddressBalanceScanner}; use crate::hd_wallet::{HDAccountOps, HDAccountsMutex, HDAddress, HDAddressId, HDWalletCoinOps, HDWalletOps, InvalidBip44ChainError}; diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index 68ade20347..aa55b0d33b 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -12,15 +12,14 @@ use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetails use crate::{BlockHeightAndTime, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinProtocol, CoinWithDerivationMethod, ConfirmPaymentInput, DexFee, IguanaPrivKey, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, - PrivKeyBuildPolicy, RawTransactionFut, RawTransactionRequest, RawTransactionResult, RefundError, - RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, - SendPaymentArgs, SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, SwapOps, - TakerSwapMakerCoin, TradePreimageValue, TransactionFut, TransactionResult, TransactionType, TxFeeDetails, - TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, - ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, - ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, - WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawFut}; + PrivKeyBuildPolicy, RawTransactionFut, RefundError, RefundPaymentArgs, RefundResult, + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, + SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradePreimageValue, TransactionFut, + TransactionResult, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, + ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, + VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut}; use common::executor::{AbortableSystem, AbortedError}; use common::log::warn; use derive_more::Display; @@ -31,6 +30,7 @@ use keys::CashAddress; pub use keys::NetworkPrefix as CashAddrPrefix; use mm2_metrics::MetricsArc; use mm2_number::MmNumber; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; use serde_json::{self as json, Value as Json}; use serialization::{deserialize, CoinVariant}; use std::sync::MutexGuard; @@ -1222,7 +1222,7 @@ impl MmCoin for BchCoin { fn spawner(&self) -> CoinFutSpawner { CoinFutSpawner::new(&self.as_ref().abortable_system) } - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, req: GetRawTransactionRequest) -> RawTransactionFut { Box::new(utxo_common::get_raw_transaction(&self.utxo_arc, req).boxed().compat()) } diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 3bfe1f6959..ba8cbca92d 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -28,9 +28,9 @@ use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetails use crate::{eth, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithDerivationMethod, ConfirmPaymentInput, DelegationError, DelegationFut, DexFee, GetWithdrawSenderAddress, IguanaPrivKey, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, - PaymentInstructionsErr, PrivKeyBuildPolicy, RawTransactionRequest, RawTransactionResult, RefundError, - RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, - SendPaymentArgs, SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, StakingInfosFut, SwapOps, + PaymentInstructionsErr, PrivKeyBuildPolicy, RawTransactionResult, RefundError, RefundPaymentArgs, + RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, + SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, StakingInfosFut, SwapOps, TakerSwapMakerCoin, TradePreimageValue, TransactionFut, TransactionResult, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, @@ -892,7 +892,7 @@ impl MmCoin for QtumCoin { fn spawner(&self) -> CoinFutSpawner { CoinFutSpawner::new(&self.as_ref().abortable_system) } - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, req: GetRawTransactionRequest) -> RawTransactionFut { Box::new(utxo_common::get_raw_transaction(&self.utxo_arc, req).boxed().compat()) } diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 2b5087e0e0..aa00d57053 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -16,17 +16,17 @@ use crate::utxo::{generate_and_send_tx, sat_from_big_decimal, ActualTxFee, Addit use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, NumConversError, PaymentInstructionArgs, PaymentInstructions, - PaymentInstructionsErr, PrivKeyPolicyNotAllowed, RawTransactionFut, RawTransactionRequest, - RawTransactionResult, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, - SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, SignatureResult, - SpendPaymentArgs, SwapOps, SwapTxTypeWithSecretHash, TakerSwapMakerCoin, TradeFee, TradePreimageError, - TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, - TransactionErr, TransactionFut, TransactionResult, TxFeeDetails, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, - ValidateOtherPubKeyErr, ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, - VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest}; + PaymentInstructionsErr, PrivKeyPolicyNotAllowed, RawTransactionFut, RawTransactionResult, RefundError, + RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, + SendPaymentArgs, SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, SwapOps, + SwapTxTypeWithSecretHash, TakerSwapMakerCoin, TradeFee, TradePreimageError, TradePreimageFut, + TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, + TransactionFut, TransactionResult, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, + ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, VerificationResult, + WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, + WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, + WithdrawRequest}; use async_trait::async_trait; use bitcrypto::dhash160; use chain::constants::SEQUENCE_FINAL; @@ -45,6 +45,7 @@ use keys::{AddressHashEnum, CashAddrType, CashAddress, CompactSignature, KeyPair use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; use primitives::hash::H256; use rpc::v1::types::{Bytes as BytesJson, ToTxHash, H256 as H256Json}; use script::bytes::Bytes; @@ -1589,7 +1590,7 @@ impl MmCoin for SlpToken { fn spawner(&self) -> CoinFutSpawner { CoinFutSpawner::new(&self.conf.abortable_system) } - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, req: GetRawTransactionRequest) -> RawTransactionFut { Box::new( utxo_common::get_raw_transaction(self.platform_coin.as_ref(), req) .boxed() diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 88ff31c2d8..f16127ed61 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -17,8 +17,7 @@ use crate::utxo::utxo_withdraw::{InitUtxoWithdraw, StandardUtxoWithdraw, UtxoWit use crate::watcher_common::validate_watcher_reward; use crate::{CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, ConfirmPaymentInput, DexFee, GenPreimageResult, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, GetWithdrawSenderAddress, HDAccountAddressId, - RawTransactionError, RawTransactionRequest, RawTransactionRes, RawTransactionResult, - RefundFundingSecretArgs, RefundMakerPaymentArgs, RefundPaymentArgs, RewardTarget, + RawTransactionError, RefundFundingSecretArgs, RefundMakerPaymentArgs, RefundPaymentArgs, RewardTarget, SearchForSwapTxSpendInput, SendMakerPaymentArgs, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SendTakerFundingArgs, SignRawTransactionEnum, SignRawTransactionRequest, SignUtxoTransactionParams, SignatureError, SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, SwapOps, @@ -68,6 +67,7 @@ use utxo_signer::with_key_pair::{calc_and_sign_sighash, p2sh_spend, signature_ha use utxo_signer::UtxoSignerOps; pub use chain::Transaction as UtxoTx; +use mm2_rpc::data::version2::wallet::{GetRawTransactionRequest, GetRawTransactionResponse}; pub mod utxo_tx_history_v2_common; @@ -3184,7 +3184,7 @@ async fn sign_raw_utxo_tx + UtxoTxGenerationOps>( .map_err(|err| RawTransactionError::SigningError(err.to_string()))?; let tx_signed_bytes = serialize_with_flags(&tx_signed, SERIALIZE_TRANSACTION_WITNESS); - Ok(RawTransactionRes { + Ok(GetRawTransactionResponse { tx_hex: tx_signed_bytes.into(), }) } @@ -3320,7 +3320,7 @@ pub fn min_trading_vol(coin: &UtxoCoinFields) -> MmNumber { pub fn is_asset_chain(coin: &UtxoCoinFields) -> bool { coin.conf.asset_chain } -pub async fn get_raw_transaction(coin: &UtxoCoinFields, req: RawTransactionRequest) -> RawTransactionResult { +pub async fn get_raw_transaction(coin: &UtxoCoinFields, req: GetRawTransactionRequest) -> RawTransactionResult { let hash = H256Json::from_str(&req.tx_hash).map_to_mm(|e| RawTransactionError::InvalidHashError(e.to_string()))?; let hex = coin .rpc_client @@ -3328,7 +3328,7 @@ pub async fn get_raw_transaction(coin: &UtxoCoinFields, req: RawTransactionReque .compat() .await .map_err(|e| RawTransactionError::Transport(e.to_string()))?; - Ok(RawTransactionRes { tx_hex: hex }) + Ok(GetRawTransactionResponse { tx_hex: hex }) } pub async fn get_tx_hex_by_hash(coin: &UtxoCoinFields, tx_hash: Vec) -> RawTransactionResult { @@ -3338,7 +3338,7 @@ pub async fn get_tx_hex_by_hash(coin: &UtxoCoinFields, tx_hash: Vec) -> RawT .compat() .await .map_err(|e| RawTransactionError::Transport(e.to_string()))?; - Ok(RawTransactionRes { tx_hex: hex }) + Ok(GetRawTransactionResponse { tx_hex: hex }) } pub async fn withdraw(coin: T, req: WithdrawRequest) -> WithdrawResult diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 1453acc346..e76b303400 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -28,11 +28,11 @@ use crate::{CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithDeriva DexFee, FundingTxSpend, GenPreimageResult, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, GetWithdrawSenderAddress, IguanaPrivKey, MakerCoinSwapOpsV2, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, - PrivKeyBuildPolicy, RawTransactionRequest, RawTransactionResult, RefundError, RefundFundingSecretArgs, - RefundMakerPaymentArgs, RefundPaymentArgs, RefundResult, SearchForFundingSpendErr, - SearchForSwapTxSpendInput, SendMakerPaymentArgs, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, - SendTakerFundingArgs, SignRawTransactionRequest, SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, - SwapOps, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, TakerSwapMakerCoin, ToBytes, TradePreimageValue, + PrivKeyBuildPolicy, RawTransactionResult, RefundError, RefundFundingSecretArgs, RefundMakerPaymentArgs, + RefundPaymentArgs, RefundResult, SearchForFundingSpendErr, SearchForSwapTxSpendInput, + SendMakerPaymentArgs, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SendTakerFundingArgs, + SignRawTransactionRequest, SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, SwapOps, + SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, TakerSwapMakerCoin, ToBytes, TradePreimageValue, TransactionFut, TransactionResult, TxMarshalingErr, TxPreimageWithSig, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateMakerPaymentArgs, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, ValidateSwapV2TxResult, @@ -907,7 +907,7 @@ impl MmCoin for UtxoStandardCoin { fn spawner(&self) -> CoinFutSpawner { CoinFutSpawner::new(&self.as_ref().abortable_system) } - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, req: GetRawTransactionRequest) -> RawTransactionFut { Box::new(utxo_common::get_raw_transaction(&self.utxo_arc, req).boxed().compat()) } diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 0b55cbf41d..e08ef7a0a3 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -29,10 +29,10 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, Coi DexFee, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, NumConversError, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyActivationPolicy, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, - RawTransactionFut, RawTransactionRequest, RawTransactionResult, RefundError, RefundPaymentArgs, - RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, - SignRawTransactionRequest, SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, - TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails, + RawTransactionFut, RawTransactionResult, RefundError, RefundPaymentArgs, RefundResult, + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, + SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, + TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails, TransactionEnum, TransactionFut, TransactionResult, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, @@ -60,6 +60,7 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_event_stream::behaviour::{EventBehaviour, EventInitStatus}; use mm2_number::{BigDecimal, MmNumber}; +use mm2_rpc::data::version2::wallet::GetRawTransactionRequest; #[cfg(test)] use mocktopus::macros::*; use primitives::bytes::Bytes; use rpc::v1::types::{Bytes as BytesJson, Transaction as RpcTransaction, H256 as H256Json}; @@ -1663,7 +1664,7 @@ impl MmCoin for ZCoin { )))) } - fn get_raw_transaction(&self, req: RawTransactionRequest) -> RawTransactionFut { + fn get_raw_transaction(&self, req: GetRawTransactionRequest) -> RawTransactionFut { Box::new(utxo_common::get_raw_transaction(&self.utxo_arc, req).boxed().compat()) } diff --git a/mm2src/common/write_safe/io.rs b/mm2src/common/write_safe/io.rs index 42982c3322..90933d5f69 100644 --- a/mm2src/common/write_safe/io.rs +++ b/mm2src/common/write_safe/io.rs @@ -1,7 +1,5 @@ -use std::cell::RefMut; use std::fmt; use std::io::Write; -use std::ops::DerefMut; #[macro_export] macro_rules! write_safe_io { @@ -17,14 +15,10 @@ macro_rules! writeln_safe_io { }}; } -pub trait WriteSafeIO { - fn write_safe<'a>(&mut self, args: fmt::Arguments<'_>) - where - Self: DerefMut, - { - let writer = self.deref_mut(); - Write::write_fmt(writer, args).expect("`write_fmt` should never fail for `WriteSafeIO` types") +pub trait WriteSafeIO: Write { + fn write_safe(&mut self, args: fmt::Arguments<'_>) { + Write::write_fmt(self, args).expect("`write_fmt` should never fail for `WriteSafeIO` types") } } -impl WriteSafeIO for RefMut<'_, &'_ mut dyn Write> {} +impl<'a> WriteSafeIO for dyn Write + 'a {} diff --git a/mm2src/crypto/src/crypto_ctx.rs b/mm2src/crypto/src/crypto_ctx.rs index 92ac1f2196..2994fd4804 100644 --- a/mm2src/crypto/src/crypto_ctx.rs +++ b/mm2src/crypto/src/crypto_ctx.rs @@ -327,14 +327,14 @@ impl CryptoCtx { } } -enum KeyPairPolicyBuilder { +pub enum KeyPairPolicyBuilder { Iguana, GlobalHDAccount, } impl KeyPairPolicyBuilder { /// [`KeyPairPolicyBuilder::build`] is fired if all checks pass **only**. - fn build(self, passphrase: &str) -> CryptoInitResult<(KeyPair, KeyPairPolicy)> { + pub fn build(self, passphrase: &str) -> CryptoInitResult<(KeyPair, KeyPairPolicy)> { match self { KeyPairPolicyBuilder::Iguana => { let secp256k1_key_pair = key_pair_from_seed(passphrase)?; diff --git a/mm2src/crypto/src/lib.rs b/mm2src/crypto/src/lib.rs index da9ff99a29..c7bddf62ff 100644 --- a/mm2src/crypto/src/lib.rs +++ b/mm2src/crypto/src/lib.rs @@ -17,7 +17,8 @@ mod xpub; #[cfg(target_arch = "wasm32")] mod metamask_login; pub use bip32_child::{Bip32Child, Bip32DerPathError, Bip32DerPathOps, Bip44Tail}; -pub use crypto_ctx::{CryptoCtx, CryptoCtxError, CryptoInitError, CryptoInitResult, HwCtxInitError, KeyPairPolicy}; +pub use crypto_ctx::{CryptoCtx, CryptoCtxError, CryptoInitError, CryptoInitResult, HwCtxInitError, KeyPairPolicy, + KeyPairPolicyBuilder}; pub use global_hd_ctx::{derive_secp256k1_secret, GlobalHDAccountArc}; pub use hw_client::{HwClient, HwConnectionStatus, HwDeviceInfo, HwProcessingError, HwPubkey, HwWalletType, TrezorConnectProcessor}; diff --git a/mm2src/derives/enum_derives/Cargo.toml b/mm2src/derives/enum_derives/Cargo.toml index 9518bc6d1a..1152ae2461 100644 --- a/mm2src/derives/enum_derives/Cargo.toml +++ b/mm2src/derives/enum_derives/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "enum_derives" version = "0.1.0" +authors = ["Samuel Onoja "] edition = "2021" [lib] diff --git a/mm2src/derives/skip_serializing_none/Cargo.toml b/mm2src/derives/skip_serializing_none/Cargo.toml new file mode 100644 index 0000000000..b7346c79c1 --- /dev/null +++ b/mm2src/derives/skip_serializing_none/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "skip_serializing_none" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +quote = "1.0" +proc-macro2 = "1.0" +syn = { version = "1.0", features = ["full"] } + +[lib] +proc-macro = true diff --git a/mm2src/derives/skip_serializing_none/src/lib.rs b/mm2src/derives/skip_serializing_none/src/lib.rs new file mode 100644 index 0000000000..dfb3fec55f --- /dev/null +++ b/mm2src/derives/skip_serializing_none/src/lib.rs @@ -0,0 +1,185 @@ +use proc_macro::TokenStream; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens}; +use syn::{parse::Parser, parse_quote, punctuated::Punctuated, spanned::Spanned, Error, Field, Fields, ItemEnum, + ItemStruct, Meta, Token, Type}; + +pub(crate) trait IteratorExt { + fn collect_error(self) -> Result<(), Error> + where + Self: Iterator> + Sized, + { + let accu = Ok(()); + self.fold(accu, |accu, error| match (accu, error) { + (Ok(()), error) => error, + (accu, Ok(())) => accu, + (Err(mut err), Err(error)) => { + err.combine(error); + Err(err) + }, + }) + } +} +impl IteratorExt for I where I: Iterator> + Sized {} + +#[proc_macro_attribute] +pub fn skip_serializing_none(_args: TokenStream, input: TokenStream) -> TokenStream { + let res = match apply_function_to_struct_and_enum_fields(input, skip_serializing_none_add_attr_to_field) { + Ok(res) => res, + Err(err) => err.to_compile_error(), + }; + TokenStream::from(res) +} + +fn apply_function_to_struct_and_enum_fields(input: TokenStream, function: F) -> Result +where + F: Copy, + F: Fn(&mut Field) -> Result<(), String>, +{ + fn apply_on_fields(fields: &mut Fields, function: F) -> Result<(), Error> + where + F: Fn(&mut Field) -> Result<(), String>, + { + match fields { + Fields::Unit => Ok(()), + Fields::Named(ref mut fields) => fields + .named + .iter_mut() + .map(|field| function(field).map_err(|err| Error::new(field.span(), err))) + .collect_error(), + Fields::Unnamed(ref mut fields) => fields + .unnamed + .iter_mut() + .map(|field| function(field).map_err(|err| Error::new(field.span(), err))) + .collect_error(), + } + } + if let Ok(mut input) = syn::parse::(input.clone()) { + apply_on_fields(&mut input.fields, function)?; + Ok(quote!(#input)) + } else if let Ok(mut input) = syn::parse::(input) { + input + .variants + .iter_mut() + .map(|variant| apply_on_fields(&mut variant.fields, function)) + .collect_error()?; + Ok(quote!(#input)) + } else { + Err(Error::new( + Span::call_site(), + "The attribute can only be applied to struct or enum definitions.", + )) + } +} + +fn is_serialize_always(attr: &syn::Attribute) -> Result { + attr.parse_meta() + .map_err(|e| e.to_string()) + .map(|meta| meta.path().is_ident("serialize_always")) +} + +fn skip_serializing_none_add_attr_to_field(field: &mut Field) -> Result<(), String> { + if !is_std_option(&field.ty) { + for attr in field.attrs.iter() { + if is_serialize_always(attr)? { + return Err("`serialize_always` may only be used on fields of type `Option`.".into()); + } + } + return Ok(()); + } + let has_skip_serializing_if = field_has_attribute(field, "serde", "skip_serializing_if"); + let mut has_always_attr = false; + let mut attrs_to_retain = Vec::new(); + for attr in &field.attrs { + let has_attr = is_serialize_always(attr)?; + has_always_attr |= has_attr; + if !has_attr { + attrs_to_retain.push(attr.clone()); + } + } + field.attrs = attrs_to_retain; + if has_always_attr && has_skip_serializing_if { + let mut msg = r#"The attributes `serialize_always` and `serde(skip_serializing_if = "...")` cannot be used on the same field"#.to_string(); + if let Some(ident) = &field.ident { + msg += ": `"; + msg += &ident.to_string(); + msg += "`"; + } + msg += "."; + return Err(msg); + } + if !has_skip_serializing_if && !has_always_attr { + field.attrs.push(parse_quote!( + #[serde(skip_serializing_if = "Option::is_none")] + )); + } + Ok(()) +} + +fn is_std_option(type_: &Type) -> bool { + match type_ { + Type::Array(_) + | Type::BareFn(_) + | Type::ImplTrait(_) + | Type::Infer(_) + | Type::Macro(_) + | Type::Never(_) + | Type::Ptr(_) + | Type::Reference(_) + | Type::Slice(_) + | Type::TraitObject(_) + | Type::Tuple(_) + | Type::Verbatim(_) => false, + + Type::Group(syn::TypeGroup { elem, .. }) + | Type::Paren(syn::TypeParen { elem, .. }) + | Type::Path(syn::TypePath { + qself: Some(syn::QSelf { ty: elem, .. }), + .. + }) => is_std_option(elem), + + Type::Path(syn::TypePath { qself: None, path }) => { + (path.leading_colon.is_none() && path.segments.len() == 1 && path.segments[0].ident == "Option") + || (path.segments.len() == 3 + && (path.segments[0].ident == "std" || path.segments[0].ident == "core") + && path.segments[1].ident == "option" + && path.segments[2].ident == "Option") + }, + _ => false, + } +} + +fn field_has_attribute(field: &Field, namespace: &str, name: &str) -> bool { + for attr in &field.attrs { + if let Ok(meta) = attr.parse_meta() { + if meta.path().is_ident(namespace) { + if let Ok(Meta::List(expr)) = &attr.parse_meta() { + let nested = match Punctuated::::parse_terminated.parse2(expr.to_token_stream()) { + Ok(nested) => nested, + Err(_) => continue, + }; + for expr in nested { + match expr { + Meta::NameValue(expr) => { + if let Some(ident) = expr.path.get_ident() { + if *ident == name { + return true; + } + } + }, + Meta::Path(expr) => { + if let Some(ident) = expr.get_ident() { + if *ident == name { + return true; + } + } + }, + _ => (), + } + } + } + } + } + } + false +} diff --git a/mm2src/adex_cli/Cargo.lock b/mm2src/komodefi_cli/Cargo.lock similarity index 79% rename from mm2src/adex_cli/Cargo.lock rename to mm2src/komodefi_cli/Cargo.lock index 35ee092f9f..6367905857 100644 --- a/mm2src/adex_cli/Cargo.lock +++ b/mm2src/komodefi_cli/Cargo.lock @@ -11,40 +11,6 @@ dependencies = [ "gimli", ] -[[package]] -name = "adex-cli" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "clap", - "common", - "derive_more", - "directories", - "env_logger 0.7.1", - "gstuff", - "h2", - "http 0.2.9", - "hyper", - "hyper-rustls", - "inquire", - "itertools", - "log", - "mm2_net", - "mm2_number", - "mm2_rpc", - "passwords", - "rpc", - "rustls 0.20.4", - "serde", - "serde_json", - "sysinfo", - "tiny-bip39", - "tokio", - "uuid", - "winapi", -] - [[package]] name = "adler" version = "1.0.2" @@ -159,6 +125,107 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" +[[package]] +name = "async-channel" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 1.9.0", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" +dependencies = [ + "async-lock", + "autocfg 1.1.0", + "concurrent-queue", + "futures-lite", + "libc", + "log", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" + [[package]] name = "async-trait" version = "0.1.52" @@ -166,10 +233,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 1.0.95", + "quote 1.0.33", + "syn 1.0.109", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atty" version = "0.2.14" @@ -206,7 +279,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide", + "miniz_oxide 0.5.4", "object", "rustc-demangle", ] @@ -256,6 +329,22 @@ dependencies = [ "serde", ] +[[package]] +name = "bip32" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d0f0fc59c7ba0333eed9dcc1b6980baa7b7a4dc7c6c5885994d0674f7adf34" +dependencies = [ + "bs58", + "hkd32", + "hmac 0.11.0", + "ripemd160", + "secp256k1 0.20.3", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "bitcoin" version = "0.29.2" @@ -283,7 +372,7 @@ dependencies = [ "serialization", "sha-1", "sha2", - "sha3", + "sha3 0.9.1", "siphasher", ] @@ -293,6 +382,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "bitvec" version = "1.0.1" @@ -326,12 +421,46 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block-padding" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "blocking" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite", + "piper", + "tracing", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -366,6 +495,33 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cache-padded" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" + [[package]] name = "cc" version = "1.0.74" @@ -430,7 +586,7 @@ checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" dependencies = [ "anstream", "anstyle", - "bitflags", + "bitflags 1.3.2", "clap_lex", "strsim", ] @@ -443,8 +599,8 @@ checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", "proc-macro2", - "quote 1.0.27", - "syn 2.0.16", + "quote 1.0.33", + "syn 2.0.39", ] [[package]] @@ -459,7 +615,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -492,7 +648,7 @@ dependencies = [ "chrono", "crossbeam", "derive_more", - "env_logger 0.9.3", + "env_logger", "findshlibs", "fnv", "futures 0.1.31", @@ -533,6 +689,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "concurrent-queue" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +dependencies = [ + "cache-padded", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -574,6 +739,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "crossbeam" version = "0.8.2" @@ -648,7 +822,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio", @@ -673,6 +847,57 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto" +version = "1.0.0" +dependencies = [ + "arrayref", + "async-trait", + "bip32", + "bitcrypto", + "bs58", + "common", + "derive_more", + "enum-primitive-derive", + "enum_derives", + "futures 0.3.28", + "hex", + "http 0.2.9", + "hw_common", + "keys", + "lazy_static", + "mm2_core", + "mm2_err_handle", + "mm2_eth", + "mm2_metamask", + "num-traits", + "parking_lot", + "primitives", + "rpc", + "rpc_task", + "rustc-hex", + "secp256k1 0.20.3", + "ser_error", + "ser_error_derive", + "serde", + "serde_derive", + "serde_json", + "tiny-bip39", + "trezor", + "wasm-bindgen-test", + "web3", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "crypto-mac" version = "0.8.0" @@ -683,6 +908,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "cxx" version = "1.0.81" @@ -705,9 +950,9 @@ dependencies = [ "codespan-reporting", "lazy_static", "proc-macro2", - "quote 1.0.27", + "quote 1.0.33", "scratch", - "syn 1.0.95", + "syn 1.0.109", ] [[package]] @@ -723,8 +968,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 1.0.95", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -749,8 +994,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 1.0.95", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -762,6 +1007,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", +] + [[package]] name = "directories" version = "5.0.1" @@ -801,6 +1056,15 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "endian-type" version = "0.1.2" @@ -808,16 +1072,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] -name = "env_logger" -version = "0.7.1" +name = "enum-primitive-derive" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e" dependencies = [ - "atty", - "humantime 1.3.0", - "log", - "regex", - "termcolor", + "num-traits", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "enum_derives" +version = "0.1.0" +dependencies = [ + "itertools", + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -827,7 +1099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" dependencies = [ "atty", - "humantime 2.1.0", + "humantime", "log", "regex", "termcolor", @@ -860,6 +1132,23 @@ dependencies = [ "libc", ] +[[package]] +name = "ethabi" +version = "17.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4966fba78396ff92db3b817ee71143eccd98acf0f876b8d600e585a670c5d1b" +dependencies = [ + "ethereum-types", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3 0.10.8", + "thiserror", + "uint", +] + [[package]] name = "ethbloom" version = "0.12.1" @@ -905,6 +1194,12 @@ dependencies = [ "tiny-keccak 1.4.4", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -917,6 +1212,21 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "findshlibs" version = "0.5.0" @@ -939,12 +1249,48 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -1012,6 +1358,21 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.28" @@ -1019,8 +1380,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 2.0.16", + "quote 1.0.33", + "syn 2.0.39", ] [[package]] @@ -1051,6 +1412,10 @@ name = "futures-timer" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper", +] [[package]] name = "futures-util" @@ -1100,8 +1465,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1110,14 +1477,26 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "groestl" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2432787a9b8f0d58dca43fe2240399479b7582dc8afa2126dc7652b864029e47" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "opaque-debug", ] @@ -1211,14 +1590,36 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkd32" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f2a5541afe0725f0b95619d6af614f48c1b176385b8aa30918cfb8c4bfafc8" +dependencies = [ + "hmac 0.11.0", + "rand_core 0.6.4", + "sha2", + "zeroize", +] + [[package]] name = "hmac" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" dependencies = [ - "crypto-mac", - "digest", + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", ] [[package]] @@ -1278,21 +1679,33 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hw_common" +version = "0.1.0" +dependencies = [ + "async-trait", + "bip32", + "common", + "derive_more", + "futures 0.3.28", + "js-sys", + "mm2_err_handle", + "rusb", + "secp256k1 0.20.3", + "serde", + "serde_derive", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", +] + [[package]] name = "hyper" version = "0.14.26" @@ -1333,6 +1746,19 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.4.0", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -1357,6 +1783,27 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -1391,8 +1838,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 1.0.95", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -1421,7 +1868,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33e7c1ddeb15c9abcbfef6029d8e29f69b52b6d6c891031b88ed91b5065803b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm", "dyn-clone", "lazy_static", @@ -1511,6 +1958,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures 0.3.28", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "keccak" version = "0.1.4" @@ -1537,6 +1999,59 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "komodefi-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "clap", + "common", + "crypto", + "derive_more", + "directories", + "env_logger", + "ethereum-types", + "gstuff", + "hex", + "http 0.2.9", + "hyper", + "hyper-rustls", + "inquire", + "itertools", + "keys", + "lightning-invoice", + "log", + "mm2_net", + "mm2_number", + "mm2_rpc", + "passwords", + "reqwest", + "rpc", + "rustls 0.20.4", + "serde", + "serde_json", + "skip_serializing_none", + "sysinfo", + "term-table", + "tiny-bip39", + "tokio", + "url", + "uuid", + "winapi", + "zip", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1560,6 +2075,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libusb1-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22e89d08bbe6816c6c5d446203b859eba35b8fa94bf1b7edb2f6d25d43f023f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "lightning" version = "0.0.113" @@ -1569,6 +2096,20 @@ dependencies = [ "bitcoin", ] +[[package]] +name = "lightning-invoice" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9680857590c3529cf8c7d32b04501f215f2bf1e029fdfa22f4112f66c1741e4" +dependencies = [ + "bech32", + "bitcoin_hashes", + "lightning", + "num-traits", + "secp256k1 0.24.3", + "serde", +] + [[package]] name = "link-cplusplus" version = "1.0.7" @@ -1600,6 +2141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", + "value-bag", ] [[package]] @@ -1611,6 +2153,12 @@ dependencies = [ "libc", ] +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -1673,8 +2221,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 2.0.16", + "quote 1.0.33", + "syn 2.0.39", ] [[package]] @@ -1696,6 +2244,22 @@ dependencies = [ "sketches-ddsketch", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg 1.1.0", +] + [[package]] name = "miniz_oxide" version = "0.5.4" @@ -1758,6 +2322,22 @@ dependencies = [ "serde_json", ] +[[package]] +name = "mm2_eth" +version = "0.1.0" +dependencies = [ + "ethabi", + "ethkey", + "hex", + "indexmap 1.9.3", + "itertools", + "mm2_err_handle", + "secp256k1 0.20.3", + "serde", + "serde_json", + "web3", +] + [[package]] name = "mm2_event_stream" version = "0.1.0" @@ -1772,6 +2352,29 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "mm2_metamask" +version = "0.1.0" +dependencies = [ + "async-trait", + "common", + "derive_more", + "futures 0.3.28", + "itertools", + "js-sys", + "jsonrpc-core", + "lazy_static", + "mm2_err_handle", + "mm2_eth", + "parking_lot", + "serde", + "serde_derive", + "serde_json", + "wasm-bindgen", + "wasm-bindgen-futures", + "web3", +] + [[package]] name = "mm2_metrics" version = "0.1.0" @@ -1854,6 +2457,7 @@ dependencies = [ "ser_error_derive", "serde", "serde_json", + "skip_serializing_none", "uuid", ] @@ -1861,7 +2465,25 @@ dependencies = [ name = "mm2_state_machine" version = "0.1.0" dependencies = [ - "async-trait", + "async-trait", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] @@ -1966,12 +2588,50 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +dependencies = [ + "bitflags 2.4.1", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -2009,10 +2669,16 @@ checksum = "c45ed1f39709f5a89338fab50e59816b2e8815f5bb58276e7ddf9afd495f73f8" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.27", - "syn 1.0.95", + "quote 1.0.33", + "syn 1.0.109", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" version = "0.12.0" @@ -2072,7 +2738,33 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" dependencies = [ - "crypto-mac", + "crypto-mac 0.8.0", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.39", ] [[package]] @@ -2087,12 +2779,37 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "polling" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" +dependencies = [ + "autocfg 1.1.0", + "cfg-if 1.0.0", + "libc", + "log", + "wepoll-ffi", + "windows-sys 0.42.0", +] + [[package]] name = "portable-atomic" version = "1.3.2" @@ -2172,8 +2889,8 @@ dependencies = [ "anyhow", "itertools", "proc-macro2", - "quote 1.0.27", - "syn 1.0.95", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -2192,12 +2909,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "0.3.15" @@ -2206,9 +2917,9 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.27" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -2434,8 +3145,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b86292cf41ccfc96c5de7165c1c53d5b4ac540c5bab9d1857acbe9eba5f1a0b" dependencies = [ "proc-macro-hack", - "quote 1.0.27", - "syn 2.0.16", + "quote 1.0.33", + "syn 2.0.39", ] [[package]] @@ -2453,7 +3164,7 @@ version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -2499,7 +3210,16 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -2529,6 +3249,42 @@ version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +[[package]] +name = "reqwest" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" +dependencies = [ + "base64 0.13.1", + "bytes 1.4.0", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.9", + "http-body 0.4.5", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.16.20" @@ -2550,8 +3306,8 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "opaque-debug", ] @@ -2581,13 +3337,38 @@ dependencies = [ "serialization", ] +[[package]] +name = "rpc_task" +version = "0.1.0" +dependencies = [ + "async-trait", + "common", + "derive_more", + "futures 0.3.28", + "mm2_err_handle", + "ser_error", + "ser_error_derive", + "serde", + "serde_derive", +] + +[[package]] +name = "rusb" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c470dc7dc6e4710b6f85e9c4aa4650bc742260b39a36328180578db76fa258c1" +dependencies = [ + "libc", + "libusb1-sys", +] + [[package]] name = "rusqlite" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2628,7 +3409,7 @@ version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -2793,7 +3574,7 @@ version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -2825,6 +3606,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "ser_error" version = "0.1.0" @@ -2837,16 +3624,16 @@ name = "ser_error_derive" version = "0.1.0" dependencies = [ "proc-macro2", - "quote 1.0.27", + "quote 1.0.33", "ser_error", - "syn 1.0.95", + "syn 1.0.109", ] [[package]] name = "serde" -version = "1.0.164" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -2864,13 +3651,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 2.0.16", + "quote 1.0.33", + "syn 2.0.39", ] [[package]] @@ -2892,8 +3679,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 1.0.95", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.1", + "ryu", + "serde", ] [[package]] @@ -2920,10 +3719,10 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if 1.0.0", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] @@ -2933,10 +3732,10 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if 1.0.0", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] @@ -2946,12 +3745,22 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "keccak", "opaque-debug", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "shared_ref_counter" version = "0.1.0" @@ -2998,6 +3807,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68a406c1882ed7f29cd5e248c9848a80e7cb6ae0fea82346d2746f2f941c07e1" +[[package]] +name = "skip_serializing_none" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "slab" version = "0.4.8" @@ -3062,9 +3880,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -3079,23 +3897,23 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.95" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote 1.0.27", + "quote 1.0.33", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.16" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", - "quote 1.0.27", + "quote 1.0.33", "unicode-ident", ] @@ -3129,6 +3947,30 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if 1.0.0", + "fastrand 1.9.0", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "term-table" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5e59d7fb313157de2a568be8d81e4d7f9af6e50e697702e8e00190a6566d3b8" +dependencies = [ + "lazy_static", + "regex", + "unicode-width", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -3161,8 +4003,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 1.0.95", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -3182,7 +4024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" dependencies = [ "anyhow", - "hmac", + "hmac 0.8.1", "once_cell", "pbkdf2", "rand 0.7.3", @@ -3262,8 +4104,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 1.0.95", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", ] [[package]] @@ -3324,8 +4176,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 2.0.16", + "quote 1.0.33", + "syn 2.0.39", ] [[package]] @@ -3337,6 +4189,31 @@ dependencies = [ "once_cell", ] +[[package]] +name = "trezor" +version = "0.1.1" +dependencies = [ + "async-std", + "async-trait", + "bip32", + "byteorder", + "common", + "derive_more", + "futures 0.3.28", + "hw_common", + "js-sys", + "mm2_err_handle", + "prost", + "rand 0.7.3", + "rpc_task", + "serde", + "serde_derive", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", +] + [[package]] name = "try-lock" version = "0.2.4" @@ -3361,6 +4238,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.0" @@ -3400,6 +4283,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna 0.5.0", + "percent-encoding", +] + [[package]] name = "utf8parse" version = "0.2.1" @@ -3417,6 +4311,16 @@ dependencies = [ "serde", ] +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -3429,6 +4333,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + [[package]] name = "want" version = "0.3.0" @@ -3471,8 +4381,8 @@ dependencies = [ "log", "once_cell", "proc-macro2", - "quote 1.0.27", - "syn 2.0.16", + "quote 1.0.33", + "syn 2.0.39", "wasm-bindgen-shared", ] @@ -3494,7 +4404,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ - "quote 1.0.27", + "quote 1.0.33", "wasm-bindgen-macro-support", ] @@ -3505,8 +4415,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 2.0.16", + "quote 1.0.33", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3538,7 +4448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c2e18093f11c19ca4e188c177fecc7c372304c311189f12c2f9bea5b7324ac7" dependencies = [ "proc-macro2", - "quote 1.0.27", + "quote 1.0.33", ] [[package]] @@ -3551,6 +4461,35 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web3" +version = "0.19.0" +source = "git+https://github.com/KomodoPlatform/rust-web3?tag=v0.19.0#ec5e72a5c95e3935ea0c9ab77b501e3926686fa9" +dependencies = [ + "arrayvec 0.7.1", + "derive_more", + "ethabi", + "ethereum-types", + "futures 0.3.28", + "futures-timer", + "getrandom 0.2.9", + "hex", + "idna 0.2.3", + "js-sys", + "jsonrpc-core", + "log", + "parking_lot", + "pin-project", + "rand 0.8.5", + "rlp", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tiny-keccak 2.0.2", + "wasm-bindgen", + "wasm-bindgen-futures", +] + [[package]] name = "webpki" version = "0.21.4" @@ -3580,6 +4519,15 @@ dependencies = [ "webpki 0.22.0", ] +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3801,6 +4749,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + [[package]] name = "wyz" version = "0.5.1" @@ -3826,6 +4783,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", - "quote 1.0.27", - "syn 2.0.16", + "quote 1.0.33", + "syn 2.0.39", +] + +[[package]] +name = "zip" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +dependencies = [ + "byteorder", + "bzip2", + "crc32fast", + "flate2", + "thiserror", + "time", ] diff --git a/mm2src/adex_cli/Cargo.toml b/mm2src/komodefi_cli/Cargo.toml similarity index 53% rename from mm2src/adex_cli/Cargo.toml rename to mm2src/komodefi_cli/Cargo.toml index b09916344e..959c918497 100644 --- a/mm2src/adex_cli/Cargo.toml +++ b/mm2src/komodefi_cli/Cargo.toml @@ -1,38 +1,51 @@ [package] -name = "adex-cli" +name = "komodefi-cli" version = "0.1.0" edition = "2021" -authors = ["Rozhkov Dmitrii "] -description = "Provides a CLI interface and facilitates interoperating to komodo atomic dex through the mm2 service" +authors = ["Rozhkov Dmitrii ", "Samuel Onoja "] +description = "Provides a CLI interface and facilitates interoperating to komodo defi platform through the mm2 service" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -anyhow = { version = "=1.0.42", features = ["std"] } -async-trait = "=0.1.52" -clap = { version = "4.2", features = ["derive"] } +anyhow = { version = "1.0.42", features = ["std"] } +async-trait = "0.1.52" +clap = { version = "4.0", features = ["derive"] } +chrono = "0.4.23" common = { path = "../common" } +crypto = { path = "../crypto" } derive_more = "0.99" directories = "5.0" -env_logger = "0.7.1" +env_logger = "0.9.3" +ethereum-types = { version = "0.13", default-features = false, features = ["std", "serialize"] } +hex = "0.4.3" http = "0.2" hyper = { version = "0.14.26", features = ["client", "http2", "tcp"] } hyper-rustls = "=0.23.0" gstuff = { version = "=0.7.4" , features = [ "nightly" ]} inquire = "0.6" itertools = "0.10" +keys = { path = "../mm2_bitcoin/keys" } log = "0.4" +lightning-invoice = { version = "0.21.0", features = ["serde"] } mm2_net = { path = "../mm2_net" } mm2_number = { path = "../mm2_number" } mm2_rpc = { path = "../mm2_rpc"} passwords = "3.1" +reqwest = { version = "0.11.9", features = ["blocking", "json"] } rpc = { path = "../mm2_bitcoin/rpc" } rustls = { version = "=0.20.4", features = [ "dangerous_configuration" ] } serde = "1.0" serde_json = { version = "1", features = ["preserve_order", "raw_value"] } +skip_serializing_none = { path = "../derives/skip_serializing_none" } sysinfo = "0.28" +term-table = "1.3.2" tiny-bip39 = "0.8.0" -tokio = { version = "=1.25.0", features = [ "macros" ] } -uuid = { version = "=1.2.2", features = ["fast-rng", "serde", "v4"] } +tokio = { version = "1.25.0", features = [ "macros"] } +url = { version = "2.2.2" } +uuid = { version = "1.2.2", features = ["fast-rng", "serde", "v4"] } +zip = "0.5" + [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.3", features = ["processthreadsapi", "winnt"] } + diff --git a/mm2src/adex_cli/src/activation_scheme_db/activation_scheme_impl.rs b/mm2src/komodefi_cli/src/activation_scheme_db/activation_scheme_impl.rs similarity index 71% rename from mm2src/adex_cli/src/activation_scheme_db/activation_scheme_impl.rs rename to mm2src/komodefi_cli/src/activation_scheme_db/activation_scheme_impl.rs index 63c89e5efc..2b1727a42c 100644 --- a/mm2src/adex_cli/src/activation_scheme_db/activation_scheme_impl.rs +++ b/mm2src/komodefi_cli/src/activation_scheme_db/activation_scheme_impl.rs @@ -7,7 +7,7 @@ use common::log::{debug, error}; use super::init_activation_scheme::get_activation_scheme_path; use crate::helpers::read_json_file; use crate::logging::{error_anyhow, error_bail}; -use crate::rpc_data::ActivationRequest; +use crate::rpc_data::activation::{ActivationMethod, ActivationMethodLegacy, ActivationMethodV2}; #[derive(Default)] pub(crate) struct ActivationScheme { @@ -15,14 +15,32 @@ pub(crate) struct ActivationScheme { } impl ActivationScheme { - pub(crate) fn get_activation_method(&self, coin: &str) -> Result { + pub(crate) fn get_activation_method(&self, coin: &str) -> Result { let method_json = self .scheme .get(coin) - .ok_or_else(|| error_anyhow!("Coin is not in activation scheme data: {}", coin))?; - let method: ActivationRequest = serde_json::from_value(method_json.clone()) - .map_err(|error| error_anyhow!("Failed to deserialize json data: {:?}, error: {}", method_json, error))?; - Ok(method) + .ok_or_else(|| error_anyhow!("Coin is not in activation scheme data: {}", coin))? + .clone(); + + if let Some(_mmrpc) = method_json.get("mmrpc") { + match serde_json::from_value::(method_json.clone()) { + Ok(method) => Ok(ActivationMethod::V2(method)), + Err(error) => error_bail!( + "Failed to deserialize v2_enable_request from data: {}, error: {}", + method_json, + error + ), + } + } else { + match serde_json::from_value::(method_json.clone()) { + Ok(method) => Ok(ActivationMethod::Legacy(method)), + Err(error) => error_bail!( + "Failed to deserialize legacy_enable_method from data: {}, error: {}", + method_json, + error + ), + } + } } fn init(&mut self) -> Result<()> { diff --git a/mm2src/adex_cli/src/activation_scheme_db/init_activation_scheme.rs b/mm2src/komodefi_cli/src/activation_scheme_db/init_activation_scheme.rs similarity index 94% rename from mm2src/adex_cli/src/activation_scheme_db/init_activation_scheme.rs rename to mm2src/komodefi_cli/src/activation_scheme_db/init_activation_scheme.rs index b0dd8c956c..6ee447f8cf 100644 --- a/mm2src/adex_cli/src/activation_scheme_db/init_activation_scheme.rs +++ b/mm2src/komodefi_cli/src/activation_scheme_db/init_activation_scheme.rs @@ -7,7 +7,7 @@ use std::fs::OpenOptions; use std::io::Write; use std::path::PathBuf; -use crate::adex_config::AdexConfigImpl; +use crate::cli::get_cli_root; use crate::error_anyhow; const ACTIVATION_SCHEME_FILE: &str = "activation_scheme.json"; @@ -31,7 +31,7 @@ pub(crate) async fn init_activation_scheme() -> Result<()> { } pub(crate) fn get_activation_scheme_path() -> Result { - let mut config_path = AdexConfigImpl::get_config_dir()?; + let mut config_path = get_cli_root()?; config_path.push(ACTIVATION_SCHEME_FILE); Ok(config_path) } diff --git a/mm2src/adex_cli/src/activation_scheme_db/mod.rs b/mm2src/komodefi_cli/src/activation_scheme_db/mod.rs similarity index 100% rename from mm2src/adex_cli/src/activation_scheme_db/mod.rs rename to mm2src/komodefi_cli/src/activation_scheme_db/mod.rs diff --git a/mm2src/adex_cli/src/adex_app.rs b/mm2src/komodefi_cli/src/app.rs similarity index 53% rename from mm2src/adex_cli/src/adex_app.rs rename to mm2src/komodefi_cli/src/app.rs index 01a3a34385..e63bd6ee69 100644 --- a/mm2src/adex_cli/src/adex_app.rs +++ b/mm2src/komodefi_cli/src/app.rs @@ -1,18 +1,18 @@ use std::env; use std::io::Write; -use super::adex_config::AdexConfigImpl; -use super::adex_proc::ResponseHandlerImpl; use super::cli; +use super::config::KomodefiConfigImpl; +use super::komodefi_proc::ResponseHandlerImpl; -pub(super) struct AdexApp { - config: AdexConfigImpl, +pub(super) struct KomodefiApp { + config: KomodefiConfigImpl, } -impl AdexApp { - pub(super) fn new() -> AdexApp { - let config = AdexConfigImpl::read_config().unwrap_or_default(); - AdexApp { config } +impl KomodefiApp { + pub(super) fn new() -> KomodefiApp { + let config = KomodefiConfigImpl::read_config().unwrap_or_default(); + KomodefiApp { config } } pub(super) async fn execute(&self) { diff --git a/mm2src/komodefi_cli/src/cli.rs b/mm2src/komodefi_cli/src/cli.rs new file mode 100644 index 0000000000..30fcbd8400 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli.rs @@ -0,0 +1,231 @@ +use crate::logging::error_anyhow; +use anyhow::{anyhow, Result}; +use clap::{Parser, Subcommand}; +use common::log::error; +use directories::ProjectDirs; +use std::fs; +use std::mem::take; +use std::path::PathBuf; + +use crate::config::{get_config, set_config, KomodefiConfig}; +use crate::komodefi_proc::{KomodefiProc, ResponseHandler}; +use crate::scenarios::{download_binary_and_extract_to_bin_folder, get_status, init, start_process, stop_process}; +use crate::transport::SlurpTransport; + +use super::cli_cmd_args::prelude::*; + +const COINS_FILE_DEFAULT: &str = "coins"; +const MM2_CONFIG_FILE_DEFAULT: &str = "MM2.json"; +const PROJECT_QUALIFIER: &str = "com"; +const PROJECT_COMPANY: &str = "komodoplatform"; +const PROJECT_APP: &str = "komodefi-cli"; + +#[derive(Subcommand)] +enum Command { + #[command(about = "Initialize a predefined coin set and configuration to start mm2 instance with")] + Init { + #[arg(long, visible_alias = "coins", help = "Coin set file path", default_value = COINS_FILE_DEFAULT)] + mm_coins_path: String, + #[arg(long, visible_alias = "conf", help = "mm2 configuration file path", default_value = MM2_CONFIG_FILE_DEFAULT)] + mm_conf_path: String, + }, + #[command(subcommand, about = "Manage rpc_password and mm2 RPC URL")] + Config(ConfigSubcommand), + #[command(subcommand, about = "Manage mm2 instance commands")] + Mm2(Mm2Commands), + #[command(subcommand, about = "Coin commands: enable, disable etc.")] + Coin(CoinCommands), + #[command(subcommand, about = "Wallet commands: balance, withdraw etc.")] + Wallet(WalletCommands), + Sell(SellOrderArgs), + Buy(BuyOrderArgs), + SetPrice(SetPriceArgs), + #[command(visible_alias = "update", about = "Update order on the orderbook")] + UpdateMakerOrder(UpdateMakerOrderArgs), + #[command( + subcommand, + visible_alias = "orders", + about = "Order listing commands: book, history, depth etc." + )] + Order(OrderCommands), + #[command(subcommand, visible_alias = "swap", about = "Swap related commands")] + Swaps(SwapCommands), + #[command(subcommand, about = "Cancel one or many orders")] + Cancel(CancelSubcommand), + #[command(subcommand, visible_aliases = ["util", "pubkeys", "pubkey"], about = "Utility commands")] + Utility(UtilityCommands), + #[command(subcommand, about = "Message signing commands: sign, verify)")] + Message(MessageCommands), + #[command(subcommand, about = "Network commands")] + Network(NetworkCommands), + #[command(subcommand, visible_aliases = ["stat", "vstat"], about = "Version statistic commands")] + VersionStat(VersionStatCommands), + #[command(subcommand, about = "Tracking the status of long-running commands")] + Task(TaskSubcommand), +} + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +pub(super) struct Cli { + #[command(subcommand)] + command: Command, +} + +impl Cli { + pub(super) async fn execute( + args: impl Iterator, + config: &Cfg, + printer: &P, + ) -> Result<()> { + let transport = config.rpc_uri().map(SlurpTransport::new); + + let proc = KomodefiProc { + transport: transport.as_ref(), + response_handler: printer, + config, + }; + + let mut parsed_cli = Self::parse_from(args); + match &mut parsed_cli.command { + Command::Init { + mm_coins_path: coins_file, + mm_conf_path: mm2_cfg_file, + } => init(mm2_cfg_file, coins_file).await, + Command::Mm2(Mm2Commands::Start { + mm_conf_path: mm2_cfg_file, + mm_coins_path: coins_file, + mm_log: log_file, + }) => start_process(mm2_cfg_file, coins_file, log_file), + Command::Mm2(Mm2Commands::Version) => proc.get_version().await?, + Command::Mm2(Mm2Commands::Download) => download_binary_and_extract_to_bin_folder().await?, + Command::Mm2(Mm2Commands::Kill) => stop_process(), + Command::Mm2(Mm2Commands::Status) => get_status(), + Command::Mm2(Mm2Commands::Stop) => proc.send_stop().await?, + Command::Config(ConfigSubcommand::Set(config)) => set_config(config)?, + Command::Config(ConfigSubcommand::Get(option)) => get_config(option), + Command::Coin(CoinCommands::Enable(args)) => { + proc.enable(&args.coin, args.keep_progress, args.tx_history).await? + }, + Command::Coin(CoinCommands::Disable(args)) => proc.disable(args.into()).await?, + Command::Coin(CoinCommands::GetEnabled) => proc.get_enabled().await?, + Command::Coin(CoinCommands::SetRequiredConf(args)) => proc.set_required_confirmations(args.into()).await?, + Command::Coin(CoinCommands::SetRequiredNota(args)) => proc.set_required_nota(args.into()).await?, + Command::Coin(CoinCommands::CoinsToKickStart) => proc.coins_to_kick_start().await?, + Command::Order(OrderCommands::Orderbook(obook_args)) => { + proc.get_orderbook(obook_args.into(), obook_args.into()).await? + }, + Command::Order(OrderCommands::OrderbookDepth(orderbook_depth_args)) => { + proc.orderbook_depth(orderbook_depth_args.into()).await? + }, + Command::Order(OrderCommands::OrdersHistory(history_args)) => { + proc.orders_history(history_args.into(), history_args.into()).await? + }, + Command::Order(OrderCommands::OrderStatus(order_status_args)) => { + proc.order_status(order_status_args.into()).await? + }, + Command::Order(OrderCommands::MyOrders) => proc.my_orders().await?, + Command::Order(OrderCommands::BestOrders(best_orders_args)) => { + let show_orig_tickets = best_orders_args.show_orig_tickets; + proc.best_orders(best_orders_args.into(), show_orig_tickets).await? + }, + Command::Cancel(CancelSubcommand::Order(args)) => proc.cancel_order(args.into()).await?, + Command::Cancel(CancelSubcommand::All) => proc.cancel_all_orders().await?, + Command::Cancel(CancelSubcommand::ByPair(args)) => proc.cancel_by_pair(args.into()).await?, + Command::Cancel(CancelSubcommand::ByCoin(args)) => proc.cancel_by_coin(args.into()).await?, + + Command::Sell(sell_args) => proc.sell(sell_args.into()).await?, + Command::Buy(buy_args) => proc.buy(buy_args.into()).await?, + + Command::SetPrice(set_price_args) => proc.set_price(set_price_args.into()).await?, + Command::UpdateMakerOrder(update_maker_args) => proc.update_maker_order(update_maker_args.into()).await?, + Command::Swaps(SwapCommands::ActiveSwaps(args)) => proc.active_swaps(args.include_status).await?, + Command::Swaps(SwapCommands::MySwapStatus(args)) => proc.swap_status(args.uuid).await?, + Command::Swaps(SwapCommands::MyRecentSwaps(args)) => proc.recent_swaps(args.into()).await?, + Command::Swaps(SwapCommands::RecoverFundsOfSwap(args)) => proc.recover_funds_of_swap(args.into()).await?, + Command::Swaps(SwapCommands::MinTradingVol { coin }) => proc.min_trading_vol(take(coin)).await?, + Command::Swaps(SwapCommands::MaxTakerVol { coin }) => proc.max_taker_vol(take(coin)).await?, + Command::Swaps(SwapCommands::TradePreimage(args)) => proc.trade_preimage(args.into()).await?, + Command::Network(NetworkCommands::GetGossipMesh) => proc.get_gossip_mesh().await?, + Command::Network(NetworkCommands::GetRelayMesh) => proc.get_relay_mesh().await?, + Command::Network(NetworkCommands::GetGossipPeerTopics) => proc.get_gossip_peer_topics().await?, + Command::Network(NetworkCommands::GetGossipTopicPeers) => proc.get_gossip_topic_peers().await?, + Command::Network(NetworkCommands::GetMyPeerId) => proc.get_my_peer_id().await?, + Command::Network(NetworkCommands::GetPeersInfo) => proc.get_peers_info().await?, + Command::Utility(UtilityCommands::BanPubkey(args)) => proc.ban_pubkey(args.into()).await?, + Command::Utility(UtilityCommands::ListBannedPubkeys) => proc.list_banned_pubkeys().await?, + Command::Utility(UtilityCommands::UnbanPubkeys(args)) => proc.unban_pubkeys(args.into()).await?, + Command::Utility(UtilityCommands::GetCurrentMtp(args)) => proc.get_current_mtp(args.into()).await?, + Command::Wallet(WalletCommands::MyBalance(my_balance_args)) => { + proc.get_balance(my_balance_args.into()).await? + }, + Command::Wallet(WalletCommands::SendRawTransaction(args)) => { + proc.send_raw_transaction(args.into(), args.bare_output).await? + }, + Command::Wallet(WalletCommands::Withdraw(args)) => { + proc.withdraw(args.try_into()?, args.bare_output).await? + }, + Command::Wallet(WalletCommands::GetRawTransaction(args)) => { + proc.get_raw_transaction(args.into(), args.bare_output).await? + }, + Command::Wallet(WalletCommands::TxHistory(args)) => proc.tx_history(args).await?, + Command::Wallet(WalletCommands::ShowPrivKey(args)) => proc.show_priv_key(args.into()).await?, + Command::Wallet(WalletCommands::ValidateAddress(args)) => proc.validate_address(args.into()).await?, + Command::Wallet(WalletCommands::KmdRewardsInfo) => proc.get_kmd_rewards_info().await?, + Command::Wallet(WalletCommands::ConvertAddress(args)) => proc.convert_address(args.try_into()?).await?, + Command::Wallet(WalletCommands::ConvertUtxoAddress(args)) => proc.convert_utxo_address(args.into()).await?, + Command::Wallet(WalletCommands::GetPublicKey) => proc.get_public_key().await?, + Command::Wallet(WalletCommands::GetPublicKeyHash) => proc.get_public_key_hash().await?, + Command::Task(TaskSubcommand::Status(TaskSubcommandStatus::Zcoin { task_id })) => { + proc.enable_zcoin_status(*task_id, None).await? + }, + Command::Task(TaskSubcommand::Cancel(TaskSubcommandCancel::Zcoin { task_id })) => { + proc.enable_zcoin_cancel(*task_id).await? + }, + Command::VersionStat(VersionStatCommands::AddNode(args)) => proc.version_stat_add_node(args.into()).await?, + Command::VersionStat(VersionStatCommands::RemoveNode(args)) => { + proc.version_stat_remove_node(args.into()).await? + }, + Command::VersionStat(VersionStatCommands::StartCollect(args)) => { + proc.version_stat_start_collection(args.into()).await? + }, + Command::VersionStat(VersionStatCommands::StopCollect) => proc.version_stat_stop_collection().await?, + Command::VersionStat(VersionStatCommands::UpdateCollect(args)) => { + proc.version_stat_update_collection(args.into()).await? + }, + Command::Message(MessageCommands::Sign(args)) => proc.sign_message(args.into()).await?, + Command::Message(MessageCommands::Verify(args)) => proc.verify_message(args.into()).await?, + } + Ok(()) + } +} + +pub fn get_cli_root() -> Result { + if let Ok(cli_root) = std::env::var("KOMODEFI_CLI_ROOT") { + let cli_root = PathBuf::from(cli_root); + fs::create_dir_all(&cli_root) + .map_err(|error| error_anyhow!("Failed to create config_dir: {cli_root:?}, error: {error}"))?; + Ok(cli_root) + } else { + let project_dirs = ProjectDirs::from(PROJECT_QUALIFIER, PROJECT_COMPANY, PROJECT_APP) + .ok_or_else(|| error_anyhow!("Failed to get project_dirs"))?; + let cli_root: PathBuf = project_dirs.config_dir().into(); + fs::create_dir_all(&cli_root) + .map_err(|error| error_anyhow!("Failed to create config_dir: {cli_root:?}, error: {error}"))?; + + Ok(cli_root) + } +} + +#[derive(Debug, clap::Parser)] +pub(crate) struct GetOption { + #[arg(long, short)] + pub(crate) unhide: bool, +} + +#[derive(Subcommand)] +enum ConfigSubcommand { + #[command(about = "Set komodo komodefi cli configuration")] + Set(SetConfigArgs), + #[command(about = "Get komodo komodefi cli configuration")] + Get(GetOption), +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/cmd_sell_buy.rs b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_sell_buy.rs new file mode 100644 index 0000000000..a7c010b318 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_sell_buy.rs @@ -0,0 +1,177 @@ +use clap::{Args, ValueEnum}; +use rpc::v1::types::H256 as H256Json; +use std::collections::HashSet; +use std::mem::take; +use std::str::FromStr; +use uuid::Uuid; + +use mm2_number::MmNumber; +use mm2_rpc::data::legacy::{BuyRequest, MatchBy, OrderType, SellBuyRequest, SellRequest}; + +use super::parse_mm_number; + +#[derive(Args)] +#[command(about = "Put a selling request")] +pub(crate) struct SellOrderArgs { + #[command(flatten)] + order_args: OrderArgs, +} + +#[derive(Args)] +#[command(about = "Put a buying request")] +pub(crate) struct BuyOrderArgs { + #[command(flatten)] + order_args: OrderArgs, +} + +#[derive(Args)] +struct OrderArgs { + #[arg(help = "Base currency of a pair")] + base: String, + #[arg(help = "Related currency of a pair")] + rel: String, + #[arg( + value_parser = parse_mm_number, + help = "Amount of coins the user is willing to sell/buy of the base coin", + )] + volume: MmNumber, + #[arg( + value_parser = parse_mm_number, + help = "Price in rel the user is willing to receive/pay per one unit of the base coin", + )] + price: MmNumber, + #[arg( + long, + short = 't', + value_enum, + visible_alias = "type", + default_value_t = OrderTypeCli::GoodTillCancelled, + help = "The GoodTillCancelled order is automatically converted to a maker order if not matched in \ + 30 seconds, and this maker order stays in the orderbook until explicitly cancelled. \ + On the other hand, a FillOrKill is cancelled if not matched within 30 seconds" + )] + order_type: OrderTypeCli, + #[arg( + long, + short = 'm', + value_parser=parse_mm_number, + help = "Amount of base coin that will be used as min_volume of GoodTillCancelled order after conversion to maker", + )] + min_volume: Option, + #[command(flatten)] + matching: OrderMatchingGroup, + #[arg( + long, + visible_alias = "bc", + help = "Number of required blockchain confirmations for base coin atomic swap transaction" + )] + base_confs: Option, + #[arg( + long, + visible_alias = "bn", + help = "Whether dPoW notarization is required for base coin atomic swap transaction" + )] + base_nota: Option, + #[arg( + long, + visible_alias = "rc", + help = "Number of required blockchain confirmations for rel coin atomic swap transaction" + )] + rel_confs: Option, + #[arg( + long, + visible_alias = "rn", + help = "Whether dPoW notarization is required for rel coin atomic swap transaction" + )] + rel_nota: Option, + #[arg( + long, + short, + visible_alias = "save", + help = "If true, each order's short record history is stored else the only order status will be temporarily stored while in progress" + )] + save_in_history: bool, +} + +#[derive(Args)] +#[group(required = false, multiple = false)] +struct OrderMatchingGroup { + #[arg( + long = "uuid", + short = 'u', + group = "order-matching", + help = "The created order is matched using a set of uuid" + )] + match_uuids: Vec, + #[arg( + long = "public", + short = 'p', + value_parser = H256Json::from_str, + help = "The created order is matched using a set of publics to select specific nodes" + )] + match_publics: Vec, +} + +#[derive(Clone, ValueEnum)] +enum OrderTypeCli { + FillOrKill, + GoodTillCancelled, +} + +impl From<&OrderTypeCli> for OrderType { + fn from(value: &OrderTypeCli) -> Self { + match value { + OrderTypeCli::GoodTillCancelled => OrderType::GoodTillCancelled, + OrderTypeCli::FillOrKill => OrderType::FillOrKill, + } + } +} + +impl From<&mut SellOrderArgs> for SellRequest { + fn from(value: &mut SellOrderArgs) -> Self { + SellRequest { + delegate: SellBuyRequest::from(&mut value.order_args), + } + } +} + +impl From<&mut BuyOrderArgs> for BuyRequest { + fn from(value: &mut BuyOrderArgs) -> Self { + BuyRequest { + delegate: SellBuyRequest::from(&mut value.order_args), + } + } +} + +impl From<&mut OrderArgs> for SellBuyRequest { + fn from(value: &mut OrderArgs) -> Self { + let match_by = if !value.matching.match_uuids.is_empty() { + MatchBy::Orders(HashSet::from_iter(value.matching.match_uuids.drain(..))) + } else if !value.matching.match_publics.is_empty() { + MatchBy::Pubkeys(HashSet::from_iter(value.matching.match_publics.drain(..))) + } else { + MatchBy::Any + }; + + let will_set_by_serde_tag = String::default(); + SellBuyRequest { + base: take(&mut value.base), + rel: take(&mut value.rel), + price: take(&mut value.price), + volume: take(&mut value.volume), + timeout: None, + duration: None, + method: will_set_by_serde_tag, + gui: None, + dest_pub_key: H256Json::default(), + match_by, + order_type: (&value.order_type).into(), + base_confs: value.base_confs, + base_nota: value.base_nota, + rel_confs: value.rel_confs, + rel_nota: value.rel_nota, + min_volume: take(&mut value.min_volume), + save_in_history: value.save_in_history, + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/cmd_set_config.rs b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_set_config.rs new file mode 100644 index 0000000000..06334fa99c --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_set_config.rs @@ -0,0 +1,105 @@ +use crate::cli::get_cli_root; +use crate::error_anyhow; + +use anyhow::{anyhow, Result}; +use clap::Args; +use common::log::error; +use inquire::{Confirm, Text}; +use std::fs::File; +use std::io::Read; + +const DEFAULT_RPC_URL: &str = "127.0.0.1:7783"; + +#[derive(Args)] +#[group(required = false, multiple = true)] +pub(crate) struct SetConfigArgs { + #[arg(long, short, help = "Set if you are going to set up a password")] + pub(crate) password: Option, + #[arg(long, short, visible_alias = "url", help = "KomoDeFi RPC API Uri. localhost:7783")] + pub(crate) uri: Option, + #[arg( + long, + short, + help = "Set `Yes` if you want to use secure connection with your mm2 rpc. RPC should supported secure!" + )] + pub(crate) secure_conn: Option, + #[arg(long, short, help = "Set configuration from MM2.json config path")] + pub(crate) from_path: bool, +} + +impl SetConfigArgs { + pub(crate) fn inquire(&mut self) -> Result<()> { + self.inquire_secure_connection()?; + self.inquire_uri()?; + self.inquire_password() + } + + fn inquire_password(&mut self) -> Result<()> { + if self.password.is_none() { + self.password = Confirm::new("Set if you are going to set up a password:") + .with_default(false) + .with_placeholder("No") + .prompt() + .map_err(|error| error_anyhow!("Failed to get password option: {error}"))? + .into(); + } + + Ok(()) + } + + fn inquire_secure_connection(&mut self) -> Result<()> { + if self.secure_conn.is_none() { + self.secure_conn = Confirm::new("Use secure connection for rpc uri:") + .with_default(false) + .with_placeholder("No") + .prompt() + .map_err(|error| error_anyhow!("Failed to get secure_conn option: {error}"))? + .into(); + } + + Ok(()) + } + + fn inquire_uri(&mut self) -> Result<()> { + if self.uri.is_none() { + self.uri = Text::new("What is the rpc_uri without https/http?:") + .with_placeholder(DEFAULT_RPC_URL) + .with_default(DEFAULT_RPC_URL) + .prompt() + .map_err(|error| error_anyhow!("Failed to get rpc_uri: {error}"))? + .into(); + } + Ok(()) + } + + pub(crate) fn source_from_path(&self) -> Result<(String, String)> { + let mut root = get_cli_root()?; + root.push("MM2.json"); + + let mut file = File::open(root).map_err(|error| error_anyhow!("Failed to open path: {error}"))?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer) + .map_err(|error| error_anyhow!("Failed to read file: {error}"))?; + + let mm2: Mm2Config = serde_json::from_str(&String::from_utf8_lossy(&buffer)) + .map_err(|error| error_anyhow!("Failed to write json: {error}"))?; + + let scheme = if mm2.secure_conn.unwrap_or_default() { + "https://" + } else { + "http://" + }; + Ok(( + format!("{scheme}{}:{}", mm2.rpcip.trim(), mm2.rpcport), + mm2.rpc_password.trim().to_string(), + )) + } +} + +#[derive(serde::Deserialize)] +struct Mm2Config { + rpcip: String, + rpcport: u16, + secure_conn: Option, + rpc_password: String, +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/cmd_set_price.rs b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_set_price.rs new file mode 100644 index 0000000000..a1fb5223e1 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_set_price.rs @@ -0,0 +1,115 @@ +use clap::Args; +use std::mem::take; + +use mm2_number::MmNumber; +use mm2_rpc::data::legacy::SetPriceRequest; + +use super::parse_mm_number; + +#[derive(Args)] +#[command(about = "Place an order on the orderbook. The setprice order is always considered a sell")] +pub(crate) struct SetPriceArgs { + #[arg(help = "The name of the coin the user desires to sell")] + base: String, + #[arg(help = "The name of the coin the user desires to receive")] + rel: String, + #[arg( + value_parser = parse_mm_number, + help = "The price in rel the user is willing to receive per one unit of the base coin" + )] + price: MmNumber, + #[command(flatten)] + delegate: SetPriceVolumeGroup, + #[arg( + long, + short, + value_parser = parse_mm_number, + help = "The minimum amount of base coin available for the order; it must be less or equal than volume param; \ + the following values must be greater than or equal to the min_trading_vol of the corresponding coin", + )] + min_volume: Option, + #[arg( + long, + short, + visible_alias = "cancel", + help = "Cancel all existing orders for the give pair" + )] + cancel_prev: bool, + #[arg( + long, + visible_alias = "bc", + help = "Number of required blockchain confirmations for base coin atomic swap transaction" + )] + base_confs: Option, + #[arg( + long, + visible_alias = "bn", + help = "Whether dPoW notarization is required for base coin atomic swap transaction" + )] + base_nota: Option, + #[arg( + long, + visible_alias = "rc", + help = "Number of required blockchain confirmations for rel coin atomic swap transaction" + )] + rel_confs: Option, + #[arg( + long, + visible_alias = "rn", + help = "Whether dPoW notarization is required for rel coin atomic swap transaction" + )] + rel_nota: Option, + #[arg( + long, + short, + visible_alias = "save", + help = "If true, each order's short record history is stored in a local SQLite database table, \ + and when the order is cancelled or fully matched, it's history will be saved as a json file", + default_value_t = true + )] + save_in_history: bool, +} + +impl From<&mut SetPriceArgs> for SetPriceRequest { + fn from(set_price_args: &mut SetPriceArgs) -> Self { + SetPriceRequest { + base: take(&mut set_price_args.base), + rel: take(&mut set_price_args.rel), + price: take(&mut set_price_args.price), + max: set_price_args.delegate.max, + volume: set_price_args + .delegate + .volume + .as_mut() + .map_or_else(MmNumber::default, take), + min_volume: take(&mut set_price_args.min_volume), + cancel_previous: set_price_args.cancel_prev, + base_confs: take(&mut set_price_args.base_confs), + base_nota: take(&mut set_price_args.base_nota), + rel_confs: take(&mut set_price_args.rel_confs), + rel_nota: take(&mut set_price_args.rel_nota), + save_in_history: set_price_args.save_in_history, + } + } +} + +#[derive(Args)] +#[group(required = true, multiple = false)] +struct SetPriceVolumeGroup { + #[arg( + group = "set-price-volume", + long, + short = 'M', + help = "Use the entire coin balance for the order, taking 0.001 coins into reserve to account for fees", + default_value_t = false + )] + max: bool, + #[arg( + group = "set-price-volume", + long, + short, + help = "The maximum amount of base coin available for the order, ignored if max is true; the following values must be greater than or equal to the min_trading_vol of the corresponding coin", + value_parser = parse_mm_number + )] + volume: Option, +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/cmd_task.rs b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_task.rs new file mode 100644 index 0000000000..e386d47e63 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_task.rs @@ -0,0 +1,21 @@ +use clap::Subcommand; + +#[derive(Subcommand)] +pub(crate) enum TaskSubcommand { + #[command(subcommand, about = "Get status of task")] + Status(TaskSubcommandStatus), + #[command(subcommand, about = "Cancel task")] + Cancel(TaskSubcommandCancel), +} + +#[derive(Subcommand)] +pub(crate) enum TaskSubcommandStatus { + #[command(about = "Get zcoin enabling status")] + Zcoin { task_id: u64 }, +} + +#[derive(Subcommand)] +pub(crate) enum TaskSubcommandCancel { + #[command(about = "Cancel enabling zcoin")] + Zcoin { task_id: u64 }, +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/cmd_update_maker_order.rs b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_update_maker_order.rs new file mode 100644 index 0000000000..2f7d2f353f --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/cmd_update_maker_order.rs @@ -0,0 +1,87 @@ +use clap::Args; +use mm2_number::MmNumber; +use mm2_rpc::data::legacy::UpdateMakerOrderRequest; +use std::mem::take; +use uuid::Uuid; + +use crate::cli_cmd_args::parse_mm_number; + +#[derive(Args, Clone)] +pub(crate) struct UpdateMakerOrderArgs { + #[arg(long, short, help = "Uuid of the order the user desires to update")] + uuid: Uuid, + #[arg( + long, + short, + value_parser = parse_mm_number, + help = "Price in rel the user is willing to receive per one unit of the base coin" + )] + price: Option, + #[command(flatten)] + volume: UpdateMakerVolumeArg, + #[arg( + long, + short, + value_parser = parse_mm_number, + help = "Minimum amount of base coin available for the order; it must be less or equal than the new volume; the following values must be greater than or equal to the min_trading_vol of the corresponding coin" + )] + min_volume: Option, + #[arg( + long, + visible_alias = "bc", + help = "Number of required blockchain confirmations for base coin atomic swap transaction" + )] + base_confs: Option, + #[arg( + long, + visible_alias = "bn", + help = "Whether dPoW notarization is required for base coin atomic swap transaction" + )] + base_nota: Option, + #[arg( + long, + visible_alias = "rc", + help = "Number of required blockchain confirmations for rel coin atomic swap transaction" + )] + rel_confs: Option, + #[arg( + long, + visible_alias = "rn", + help = "Whether dPoW notarization is required for rel coin atomic swap transaction" + )] + rel_nota: Option, +} + +#[derive(Args, Clone)] +#[group(required = false, multiple = false)] +struct UpdateMakerVolumeArg { + #[arg( + long, + short = 'M', + help = "Whether to use the entire coin balance for the order, taking 0.001 coins into reserve to account for fees", + default_value_t = false + )] + max_volume: bool, + #[arg( + long, + short = 'd', + value_parser = parse_mm_number, help = "Volume added to or subtracted from the max_base_vol of the order to be updated, resulting in the new volume which is the maximum amount of base coin available for the order, ignored if max is true" + )] + volume_delta: Option, +} + +impl From<&mut UpdateMakerOrderArgs> for UpdateMakerOrderRequest { + fn from(value: &mut UpdateMakerOrderArgs) -> Self { + UpdateMakerOrderRequest { + uuid: take(&mut value.uuid), + new_price: value.price.take(), + max: value.volume.max_volume.then_some(true), + volume_delta: value.volume.volume_delta.take(), + min_volume: value.min_volume.take(), + base_confs: value.base_confs.take(), + base_nota: value.base_nota.take(), + rel_confs: value.rel_confs.take(), + rel_nota: value.rel_nota.take(), + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_cancel.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_cancel.rs new file mode 100644 index 0000000000..916b5e6d04 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_cancel.rs @@ -0,0 +1,74 @@ +use clap::{Args, Subcommand}; +use std::mem::take; +use uuid::Uuid; + +use mm2_rpc::data::legacy::{CancelAllOrdersRequest, CancelBy, CancelOrderRequest}; + +#[derive(Subcommand)] +pub(crate) enum CancelSubcommand { + #[command(short_flag = 'o', about = "Cancels given order by uuid")] + Order(CancelOrderArgs), + #[command(short_flag = 'a', about = "Cancels all orders of current node")] + All, + #[command( + short_flag = 'p', + visible_alias = "pair", + about = "Cancels all orders of specific pair" + )] + ByPair(CancelByPairArgs), + #[command( + short_flag = 'c', + visible_alias = "coin", + about = "Cancels all orders using the coin ticker as base or rel" + )] + ByCoin(CancelByCoinArgs), +} + +#[derive(Args)] +pub(crate) struct CancelOrderArgs { + #[arg(help = "Order identifier")] + uuid: Uuid, +} + +impl From<&mut CancelOrderArgs> for CancelOrderRequest { + fn from(value: &mut CancelOrderArgs) -> Self { + CancelOrderRequest { + uuid: take(&mut value.uuid), + } + } +} + +#[derive(Args)] +pub(crate) struct CancelByPairArgs { + #[arg(help = "Base coin of the pair")] + base: String, + #[arg(help = "Rel coin of the pair")] + rel: String, +} + +impl From<&mut CancelByPairArgs> for CancelAllOrdersRequest { + fn from(value: &mut CancelByPairArgs) -> Self { + CancelAllOrdersRequest { + cancel_by: CancelBy::Pair { + base: take(&mut value.base), + rel: take(&mut value.rel), + }, + } + } +} + +#[derive(Args)] +pub(crate) struct CancelByCoinArgs { + #[arg(help = "Order is cancelled if it uses ticker as base or rel")] + ticker: String, +} + +impl From<&mut CancelByCoinArgs> for CancelAllOrdersRequest { + fn from(value: &mut CancelByCoinArgs) -> Self { + CancelAllOrdersRequest { + cancel_by: CancelBy::Coin { + ticker: take(&mut value.ticker), + }, + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_coin.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_coin.rs new file mode 100644 index 0000000000..8c31f5731b --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_coin.rs @@ -0,0 +1,106 @@ +use clap::{Args, Subcommand}; +use std::mem::take; + +use mm2_rpc::data::legacy::{SetRequiredConfRequest, SetRequiredNotaRequest}; + +use crate::rpc_data::DisableCoinRequest; + +#[derive(Subcommand)] +pub(crate) enum CoinCommands { + #[command(about = "Put a coin to the trading index")] + Enable(EnableArgs), + #[command(about = "Deactivates enabled coin and also cancels all active orders that use the given coin.")] + Disable(DisableCoinArgs), + #[command(visible_alias = "enabled", about = "List activated coins")] + GetEnabled, + #[command( + visible_alias = "set-conf", + about = "Set the number of confirmations to wait for the given coin" + )] + SetRequiredConf(SetRequiredConfArgs), + #[command( + visible_alias = "set-nota", + about = "Whether to wait for a dPoW notarization of the given atomic swap transactions" + )] + SetRequiredNota(SetRequiredNotaArgs), + #[command( + visible_alias = "to-kick", + about = "Return the coins that should be activated to continue the interrupted swaps" + )] + CoinsToKickStart, +} + +#[derive(Args)] +pub(crate) struct EnableArgs { + #[arg(help = "Coin to be included into the trading index")] + pub(crate) coin: String, + #[arg( + long, + short = 'k', + visible_aliases = ["track", "keep", "progress"], + default_value_t = 0, + help = "Whether to keep progress on task based commands" + )] + pub(crate) keep_progress: u64, + #[arg( + long, + default_value_t = false, + visible_alias = "history", + short = 'H', + help = "Whether to save tx history for the coin" + )] + pub(crate) tx_history: bool, +} + +#[derive(Args)] +pub(crate) struct DisableCoinArgs { + #[arg(name = "COIN", help = "Coin to disable")] + coin: String, +} + +impl From<&mut DisableCoinArgs> for DisableCoinRequest { + fn from(value: &mut DisableCoinArgs) -> Self { + DisableCoinRequest { + coin: take(&mut value.coin), + } + } +} + +#[derive(Args)] +pub(crate) struct SetRequiredConfArgs { + #[arg(help = "Ticker of the given coin")] + pub(crate) coin: String, + #[arg(visible_alias = "conf", help = "Number of confirmations to require")] + pub(crate) confirmations: u64, +} + +impl From<&mut SetRequiredConfArgs> for SetRequiredConfRequest { + fn from(value: &mut SetRequiredConfArgs) -> Self { + SetRequiredConfRequest { + coin: take(&mut value.coin), + confirmations: value.confirmations, + } + } +} + +#[derive(Args)] +pub(crate) struct SetRequiredNotaArgs { + #[arg(help = "Ticker of the given coin")] + pub(crate) coin: String, + #[arg( + long, + short = 'n', + visible_alias = "requires-nota", + help = "Whether the node should wait for dPoW notarization of atomic swap transactions" + )] + pub(crate) requires_notarization: bool, +} + +impl From<&mut SetRequiredNotaArgs> for SetRequiredNotaRequest { + fn from(value: &mut SetRequiredNotaArgs) -> Self { + SetRequiredNotaRequest { + coin: take(&mut value.coin), + requires_notarization: value.requires_notarization, + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_message.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_message.rs new file mode 100644 index 0000000000..495d99e2cd --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_message.rs @@ -0,0 +1,56 @@ +use clap::{Args, Subcommand}; +use std::mem::take; + +use crate::rpc_data::message_signing::{SignatureRequest, VerificationRequest}; + +#[derive(Subcommand)] +pub(crate) enum MessageCommands { + #[command( + short_flag = 's', + about = "If your coins file contains the correct message prefix definitions, you can sign \ + message to prove ownership of an address" + )] + Sign(SignMessageArgs), + #[command(short_flag = 'v', about = "Verify message signature")] + Verify(VerifyMessageArgs), +} + +#[derive(Args)] +pub(crate) struct SignMessageArgs { + #[arg(long, short, help = "The coin to sign a message with")] + coin: String, + #[arg(long, short, help = "The message you want to sign")] + message: String, +} + +impl From<&mut SignMessageArgs> for SignatureRequest { + fn from(value: &mut SignMessageArgs) -> Self { + SignatureRequest { + coin: take(&mut value.coin), + message: take(&mut value.message), + } + } +} + +#[derive(Args)] +pub(crate) struct VerifyMessageArgs { + #[arg(long, short, help = "The coin to sign a message with")] + coin: String, + #[arg(long, short, help = "The message input via the sign_message method sign")] + message: String, + #[arg(long, short, help = "The signature generated for the message")] + signature: String, + #[arg(long, short, help = "The address used to sign the message")] + address: String, +} + +impl From<&mut VerifyMessageArgs> for VerificationRequest { + fn from(value: &mut VerifyMessageArgs) -> Self { + VerificationRequest { + coin: take(&mut value.coin), + message: take(&mut value.message), + signature: take(&mut value.signature), + address: take(&mut value.address), + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_mm2.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_mm2.rs new file mode 100644 index 0000000000..8db7aa8165 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_mm2.rs @@ -0,0 +1,24 @@ +use clap::Subcommand; + +#[derive(Subcommand)] +pub(crate) enum Mm2Commands { + #[command(about = "Start mm2 instance")] + Start { + #[arg(long, visible_alias = "conf", help = "mm2 configuration file path")] + mm_conf_path: Option, + #[arg(long, visible_alias = "coins", help = "Coin set file path")] + mm_coins_path: Option, + #[arg(long, visible_alias = "log", help = "Log file path")] + mm_log: Option, + }, + #[command(about = "Stop mm2 using API")] + Stop, + #[command(about = "Kill mm2 process")] + Kill, + #[command(about = "Check if mm2 is running")] + Status, + #[command(about = "Get version of intermediary mm2 service")] + Version, + #[command(about = "Download latest available mm2 binary version and extract to bin folder for use.")] + Download, +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_network.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_network.rs new file mode 100644 index 0000000000..50d7dc56cc --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_network.rs @@ -0,0 +1,36 @@ +use clap::Subcommand; + +#[allow(clippy::enum_variant_names)] +#[derive(Subcommand)] +pub(crate) enum NetworkCommands { + #[command( + visible_alias = "gossip-mesh", + about = "Return an array of peerIDs added to a topics' mesh for each known gossipsub topic" + )] + GetGossipMesh, + #[command( + visible_alias = "relay-mesh", + about = "Return a list of peerIDs included in our local relay mesh" + )] + GetRelayMesh, + #[command( + visible_alias = "peer-topics", + about = "Return a map of peerIDs to an array of the topics to which they are subscribed" + )] + GetGossipPeerTopics, + #[command( + visible_alias = "topic-peers", + about = "Return a map of topics to an array of the PeerIDs which are subscribers" + )] + GetGossipTopicPeers, + #[command( + visible_alias = "my-peer-id", + about = "Return your unique identifying Peer ID on the network" + )] + GetMyPeerId, + #[command( + visible_alias = "peers-info", + about = "Return all connected peers with their multiaddresses" + )] + GetPeersInfo, +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_order.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order.rs new file mode 100644 index 0000000000..92f1a8a7f6 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order.rs @@ -0,0 +1,43 @@ +use clap::Subcommand; + +#[path = "commands_order/cmd_best_orders.rs"] +mod cmd_best_orders; +#[path = "commands_order/cmd_order_status.rs"] +mod cmd_order_status; +#[path = "commands_order/cmd_orderbook.rs"] mod cmd_orderbook; +#[path = "commands_order/cmd_orderbook_depth.rs"] +mod cmd_orderbook_depth; +#[path = "commands_order/cmd_orders_history.rs"] +mod cmd_orders_history; + +pub(crate) use cmd_best_orders::BestOrderArgs; +pub(crate) use cmd_order_status::OrderStatusArgs; +pub(crate) use cmd_orderbook::OrderbookArgs; +pub(crate) use cmd_orderbook_depth::OrderbookDepthArgs; +pub(crate) use cmd_orders_history::OrdersHistoryArgs; + +#[allow(clippy::large_enum_variant)] +#[derive(Subcommand)] +pub(crate) enum OrderCommands { + #[command(visible_aliases = ["book"], about = "Get orderbook")] + Orderbook(OrderbookArgs), + #[command(about = "Get orderbook depth")] + OrderbookDepth(OrderbookDepthArgs), + #[command( + visible_alias = "status", + about = "Return the data of the order with the given uuid created by the current node" + )] + OrderStatus(OrderStatusArgs), + #[command( + visible_alias = "best", + about = "Return the best priced trades available on the orderbook" + )] + BestOrders(BestOrderArgs), + #[command(about = "Get my orders", visible_aliases = ["my", "mine"])] + MyOrders, + #[command( + visible_aliases = ["history", "filter"], + about = "Return all orders whether active or inactive that match the given filters" + )] + OrdersHistory(OrdersHistoryArgs), +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_best_orders.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_best_orders.rs new file mode 100644 index 0000000000..b7468ad41b --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_best_orders.rs @@ -0,0 +1,85 @@ +use clap::{Args, ValueEnum}; +use std::mem::take; + +use mm2_number::MmNumber; +use mm2_rpc::data::version2::{BestOrdersAction, BestOrdersByRequest, BestOrdersRequestV2}; + +use crate::cli_cmd_args::parse_mm_number; + +#[derive(Args)] +pub(crate) struct BestOrderArgs { + #[arg(value_enum, help = "The coin to get best orders")] + coin: String, + #[arg(help = "Whether to buy or sell the given coin")] + action: OrderActionArg, + #[command(flatten)] + delegate: BestOrdersByArg, + #[arg( + long, + short = 'o', + visible_aliases = ["show-origin", "original-tickers", "origin"], + help = "Whether to show the original tickers if they are configured for the queried coin", + default_value = "false" + )] + pub(crate) show_orig_tickets: bool, + #[arg(long, short, help = "Exclude orders that is mine", default_value = "false")] + exclude_mine: bool, +} + +#[derive(Args)] +#[group(required = true, multiple = false)] +struct BestOrdersByArg { + #[arg( + long, + short, + group = "best-orders", + value_parser=parse_mm_number, + help="The returned results will show the best prices for trades that can fill the requested volume" + )] + volume: Option, + #[arg( + long, + short, + group = "best-orders", + help = "The returned results will show a list of the best prices" + )] + number: Option, +} + +#[derive(Clone, ValueEnum)] +enum OrderActionArg { + Buy, + Sell, +} + +impl From<&mut BestOrdersByArg> for BestOrdersByRequest { + fn from(value: &mut BestOrdersByArg) -> Self { + if let Some(number) = value.number { + BestOrdersByRequest::Number(number) + } else if let Some(ref mut volume) = value.volume { + BestOrdersByRequest::Volume(take(volume)) + } else { + panic!("Unreachable state during converting BestOrdersCli into BestOrdersByRequest"); + } + } +} + +impl From<&OrderActionArg> for BestOrdersAction { + fn from(value: &OrderActionArg) -> Self { + match value { + OrderActionArg::Buy => BestOrdersAction::Buy, + OrderActionArg::Sell => BestOrdersAction::Sell, + } + } +} + +impl From<&mut BestOrderArgs> for BestOrdersRequestV2 { + fn from(value: &mut BestOrderArgs) -> Self { + BestOrdersRequestV2 { + coin: take(&mut value.coin), + action: (&value.action).into(), + request_by: (&mut value.delegate).into(), + exclude_mine: value.exclude_mine, + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_order_status.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_order_status.rs new file mode 100644 index 0000000000..411c73567d --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_order_status.rs @@ -0,0 +1,18 @@ +use clap::Args; +use std::mem::take; +use uuid::Uuid; + +use mm2_rpc::data::legacy::OrderStatusRequest; + +#[derive(Args)] +pub(crate) struct OrderStatusArgs { + uuid: Uuid, +} + +impl From<&mut OrderStatusArgs> for OrderStatusRequest { + fn from(value: &mut OrderStatusArgs) -> Self { + OrderStatusRequest { + uuid: take(&mut value.uuid), + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_orderbook.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_orderbook.rs new file mode 100644 index 0000000000..8622569142 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_orderbook.rs @@ -0,0 +1,61 @@ +use clap::Args; +use std::mem::take; + +use common::serde_derive::Serialize; +use mm2_rpc::data::legacy::OrderbookRequest; + +use crate::komodefi_proc::OrderbookSettings; + +const ORDERBOOK_BIDS_LIMIT: &str = "20"; +const ORDERBOOK_ASKS_LIMIT: &str = "20"; + +#[derive(Args, Debug, Serialize)] +pub(crate) struct OrderbookArgs { + #[arg(help = "Base currency of a pair")] + base: String, + #[arg(help = "Related currency, also can be called \"quote currency\" according to exchange terms")] + rel: String, + #[arg(long, short, help = "Enable `uuid` column")] + uuids: bool, + #[arg(long, short, visible_alias = "min", help = "Enable `min_volume` column")] + min_volume: bool, + #[arg(long, short = 'M', visible_alias = "max", help = "Enable `max_volume` column")] + max_volume: bool, + #[arg(long, short, help = "Enable `public` column")] + publics: bool, + #[arg(long, short = 'a', help = "Enable `address` column")] + address: bool, + #[arg(long, short = 'A', help = "Enable `age` column")] + age: bool, + #[arg(long, short, help = "Enable order confirmation settings column")] + conf_settings: bool, + #[arg(long, help = "Orderbook asks count limitation", default_value = ORDERBOOK_ASKS_LIMIT)] + asks_limit: Option, + #[arg(long, help = "Orderbook bids count limitation", default_value = ORDERBOOK_BIDS_LIMIT)] + bids_limit: Option, +} + +impl From<&mut OrderbookArgs> for OrderbookSettings { + fn from(value: &mut OrderbookArgs) -> Self { + OrderbookSettings { + uuids: value.uuids, + min_volume: value.min_volume, + max_volume: value.max_volume, + publics: value.publics, + address: value.address, + age: value.age, + conf_settings: value.conf_settings, + asks_limit: value.asks_limit, + bids_limit: value.bids_limit, + } + } +} + +impl From<&mut OrderbookArgs> for OrderbookRequest { + fn from(value: &mut OrderbookArgs) -> Self { + OrderbookRequest { + rel: take(&mut value.rel), + base: take(&mut value.base), + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_orderbook_depth.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_orderbook_depth.rs new file mode 100644 index 0000000000..da2de5dbfa --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_orderbook_depth.rs @@ -0,0 +1,42 @@ +use anyhow::anyhow; +use clap::Args; +use std::mem::take; +use std::str::FromStr; + +use mm2_rpc::data::legacy::OrderbookDepthRequest; + +#[derive(Args)] +#[command( + about = "Return the number of asks and bids for the specified pairs", + visible_alias = "depth" +)] +pub(crate) struct OrderbookDepthArgs { + #[arg(required = true, value_name = "BASE/REL")] + pairs: Vec, +} + +#[derive(Clone)] +struct BaseRel(String, String); + +impl From<&mut OrderbookDepthArgs> for OrderbookDepthRequest { + fn from(value: &mut OrderbookDepthArgs) -> Self { + OrderbookDepthRequest { + pairs: value.pairs.drain(..).map(<(String, String)>::from).collect(), + } + } +} + +impl From for (String, String) { + fn from(mut value: BaseRel) -> Self { (take(&mut value.0), take(&mut value.1)) } +} + +impl FromStr for BaseRel { + type Err = anyhow::Error; + fn from_str(s: &str) -> std::result::Result { + let split = s + .split_once('/') + .ok_or_else(|| anyhow!("Failed to get base_rel from: {s}"))?; + + Ok(BaseRel(split.0.to_string(), split.1.to_string())) + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_orders_history.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_orders_history.rs new file mode 100644 index 0000000000..d87f3adbf7 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_order/cmd_orders_history.rs @@ -0,0 +1,134 @@ +use chrono::{DateTime, Utc}; +use clap::{Args, ValueEnum}; +use derive_more::Display; + +use mm2_number::MmNumber; +use mm2_rpc::data::legacy::OrdersHistoryRequest; + +use crate::cli_cmd_args::{parse_datetime, parse_mm_number}; +use crate::komodefi_proc; + +#[derive(Clone, Display, ValueEnum)] +enum OrderTypeFilter { + Taker, + Maker, +} + +#[derive(Clone, Display, ValueEnum)] +enum InitialActionFilter { + Sell, + Buy, +} + +#[derive(Clone, Display, ValueEnum)] +enum StatusFilter { + Created, + Updated, + Fulfilled, + InsuficcientBalance, + Cancelled, + TimedOut, +} + +#[derive(Args)] +pub(crate) struct OrdersHistoryArgs { + #[command(flatten)] + settings: OrdersHistorySettings, + #[arg(long = "type", value_enum, help = "Return only orders that match the type")] + order_type: Option, + #[arg( + long = "action", + value_enum, + help = "Return only orders that match the initial action. Note that maker order initial_action is considered \"Sell\"" + )] + initial_action: Option, + #[arg(long, help = "Return only orders that match the order.base")] + base: Option, + #[arg(long, help = "Return only orders that match the order.rel")] + rel: Option, + #[arg(long, value_parser = parse_mm_number, help = "Return only orders whose price is more or equal the from_price")] + from_price: Option, + #[arg(long, value_parser = parse_mm_number, help = "Return only orders whose price is less or equal the to_price")] + to_price: Option, + #[arg(long, value_parser = parse_mm_number, help = "Return only orders whose volume is more or equal the from_volume")] + from_volume: Option, + #[arg(long, value_parser = parse_mm_number, help = "Return only orders whose volume is less or equal the to_volume")] + to_volume: Option, + #[arg( + long, + value_parser = parse_datetime, + help = "Return only orders that match the order.created_at >= from_dt. Datetime fmt: \"%y-%m-%dT%H:%M:%S\"" + )] + from_dt: Option>, + #[arg( + long, + value_parser = parse_datetime, + help = "Return only orders that match the order.created_at <= to_dt. Datetime fmt: \"%y-%m-%dT%H:%M:%S\"" + )] + to_dt: Option>, + #[arg( + long, + help = "Return only GoodTillCancelled orders that got converted from taker to maker" + )] + was_taker: bool, + #[arg(long, value_enum, help = "Return only orders that match the status")] + status: Option, +} + +impl From<&mut OrdersHistoryArgs> for OrdersHistoryRequest { + fn from(value: &mut OrdersHistoryArgs) -> Self { + OrdersHistoryRequest { + order_type: value.order_type.as_ref().map(OrderTypeFilter::to_string), + initial_action: value.initial_action.as_ref().map(InitialActionFilter::to_string), + base: value.base.take(), + rel: value.rel.take(), + from_price: value.from_price.take(), + to_price: value.to_price.take(), + from_volume: value.from_volume.take(), + to_volume: value.to_volume.take(), + from_timestamp: value.from_dt.map(|dt| dt.timestamp() as u64), + to_timestamp: value.to_dt.map(|dt| dt.timestamp() as u64), + was_taker: Some(value.was_taker), + status: value.status.as_ref().map(StatusFilter::to_string), + include_details: value.settings.takers || value.settings.makers, + } + } +} + +#[derive(Args, Clone)] +#[group(required = true, multiple = true)] +struct OrdersHistorySettings { + #[arg( + long, + short, + default_value_t = false, + help = "Whether to show taker orders detailed history" + )] + takers: bool, + #[arg( + long, + short, + default_value_t = false, + help = "Whether to show maker orders detailed history" + )] + makers: bool, + #[arg(long, short, default_value_t = false, help = "Whether to show warnings")] + warnings: bool, + #[arg(long, short, default_value_t = false, help = "Whether to show common history data")] + all: bool, +} + +impl From<&OrdersHistorySettings> for komodefi_proc::OrdersHistorySettings { + fn from(value: &OrdersHistorySettings) -> Self { + komodefi_proc::OrdersHistorySettings { + takers_detailed: value.takers, + makers_detailed: value.makers, + warnings: value.warnings, + all: value.all, + } + } +} + +impl From<&mut OrdersHistoryArgs> for komodefi_proc::OrdersHistorySettings { + fn from(value: &mut OrdersHistoryArgs) -> Self { komodefi_proc::OrdersHistorySettings::from(&value.settings) } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_swap.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_swap.rs new file mode 100644 index 0000000000..881e7602bd --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_swap.rs @@ -0,0 +1,152 @@ +#[path = "commands_swap/cmd_trade_preimage.rs"] +mod cmd_trade_preimage; + +use chrono::{DateTime, Utc}; +pub(crate) use cmd_trade_preimage::TradePreimageArgs; + +use clap::{Args, Subcommand}; +use uuid::Uuid; + +use mm2_rpc::data::legacy::MySwapsFilter; + +use super::parse_datetime; +use crate::rpc_data::{MyRecentSwapsRequest, Params, RecoverFundsOfSwapRequest}; + +#[derive(Subcommand)] +pub(crate) enum SwapCommands { + #[command( + short_flag = 'a', + visible_alias = "active", + about = "Get all the swaps that are currently running" + )] + ActiveSwaps(ActiveSwapsArgs), + #[command( + short_flag = 's', + visible_alias = "status", + about = "Return the data of an atomic swap" + )] + MySwapStatus(MySwapStatusArgs), + #[command( + short_flag = 'r', + visible_alias = "recent", + about = "Return the data of the most recent atomic swaps by filter" + )] + MyRecentSwaps(MyRecentSwapsArgs), + #[command( + short_flag = 'R', + visible_aliases = ["recover", "recover-funds", "refund"], + about = "Reclaim the user funds from the swap-payment address, if possible" + )] + RecoverFundsOfSwap(RecoverFundsOfSwapArgs), + #[command(about = "Return the minimum required volume for buy/sell/setprice methods for the selected coin")] + MinTradingVol { coin: String }, + #[command( + about = "Returns the maximum available volume for buy/sell methods for the given coin. \ + The result should be used as is for sell method or divided by price for buy method." + )] + MaxTakerVol { coin: String }, + #[command( + visible_alias = "preimage", + about = "Return the approximate fee amounts that are paid per the whole swap" + )] + TradePreimage(TradePreimageArgs), +} + +#[derive(Args, Debug)] +pub(crate) struct ActiveSwapsArgs { + #[arg( + long, + short = 's', + default_value_t = false, + help = "Whether to include swap statuses in response; defaults to false" + )] + pub(crate) include_status: bool, +} + +#[derive(Args, Debug)] +pub(crate) struct MySwapStatusArgs { + #[arg(help = "Uuid of swap, typically received from the buy/sell call")] + pub(crate) uuid: Uuid, +} + +#[derive(Args, Debug)] +pub(crate) struct MyRecentSwapsArgs { + #[arg( + long, + short = 'l', + default_value_t = 10, + help = "Limits the number of returned swaps" + )] + pub(crate) limit: usize, + #[arg( + long, + short = 'u', + help = "Skip records until this uuid, skipping the from_uuid as well" + )] + pub(crate) from_uuid: Option, + #[arg( + long, + visible_alias = "page", + short = 'p', + help = "Return swaps from the given page; This param will be ignored if from_uuid is set" + )] + pub(crate) page_number: Option, + #[arg( + long, + short = 'm', + visible_alias = "mine", + help = "Return only swaps that match the swap.my_coin = request.my_coin condition" + )] + pub(crate) my_coin: Option, + #[arg( + long, + short = 'o', + visible_alias = "other", + help = "Return only swaps that match the swap.other_coin = request.other_coin condition" + )] + pub(crate) other_coin: Option, + #[arg( + long, + short = 't', + value_parser = parse_datetime, + help = "Return only swaps that match the swap.started_at >= request.from_timestamp condition. Datetime fmt: \"%y-%m-%dT%H:%M:%S\"" + )] + pub(crate) from_timestamp: Option>, + #[arg( + long, + short = 'T', + value_parser = parse_datetime, + help = "Return only swaps that match the swap.started_at < request.to_timestamp condition. Datetime fmt: \"%y-%m-%dT%H:%M:%S\"" + )] + pub(crate) to_timestamp: Option>, +} + +impl From<&mut MyRecentSwapsArgs> for MyRecentSwapsRequest { + fn from(value: &mut MyRecentSwapsArgs) -> Self { + MyRecentSwapsRequest { + limit: value.limit, + from_uuid: value.from_uuid.take(), + page_number: value.page_number.take(), + filter: MySwapsFilter { + my_coin: value.my_coin.take(), + other_coin: value.other_coin.take(), + from_timestamp: value.from_timestamp.map(|dt| dt.timestamp() as u64), + to_timestamp: value.to_timestamp.map(|dt| dt.timestamp() as u64), + }, + } + } +} + +#[derive(Args, Debug)] +pub(crate) struct RecoverFundsOfSwapArgs { + #[arg(help = "Uuid of the swap to recover the funds")] + pub(crate) uuid: Uuid, +} + +impl From<&mut RecoverFundsOfSwapArgs> for RecoverFundsOfSwapRequest { + fn from(value: &mut RecoverFundsOfSwapArgs) -> Self { + RecoverFundsOfSwapRequest { + params: Params { uuid: value.uuid }, + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_swap/cmd_trade_preimage.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_swap/cmd_trade_preimage.rs new file mode 100644 index 0000000000..a40ad4b5d9 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_swap/cmd_trade_preimage.rs @@ -0,0 +1,72 @@ +use clap::{Args, ValueEnum}; +use std::mem::take; + +use mm2_number::MmNumber; + +use crate::cli_cmd_args::parse_mm_number; +use crate::rpc_data::{TradePreimageMethod, TradePreimageRequest}; + +#[derive(Args)] +pub(crate) struct TradePreimageArgs { + #[arg(help = "Base currency of the request")] + base: String, + #[arg(help = "Rel currency of the request")] + rel: String, + #[arg( + value_enum, + name = "METHOD", + help = "Price in rel the user is willing to pay per one unit of the base coin" + )] + swap_method: TradePreimageMethodArg, + #[arg( + value_parser = parse_mm_number, + help = "Price in rel the user is willing to pay per one unit of the base coin" + )] + price: MmNumber, + #[command(flatten)] + volume: TradePreimageVol, +} + +impl From<&mut TradePreimageArgs> for TradePreimageRequest { + fn from(value: &mut TradePreimageArgs) -> Self { + TradePreimageRequest { + base: take(&mut value.base), + rel: take(&mut value.rel), + swap_method: match value.swap_method { + TradePreimageMethodArg::SetPrice => TradePreimageMethod::SetPrice, + TradePreimageMethodArg::Sell => TradePreimageMethod::Sell, + TradePreimageMethodArg::Buy => TradePreimageMethod::Buy, + }, + price: take(&mut value.price), + volume: value.volume.volume.take(), + max: value.volume.max, + } + } +} + +#[derive(Args)] +#[group(required = true, multiple = false)] +struct TradePreimageVol { + #[arg( + long, + short, + group = "trade-preimage-vol", + value_parser=parse_mm_number, + help="Amount the user is willing to trade; ignored if max = true and swap_method = setprice, otherwise, it must be set" + )] + volume: Option, + #[arg( + long, + short, + group = "trade-preimage-vol", + help = "Whether to return the maximum available volume for setprice method; must not be set or false if swap_method is buy or sell" + )] + max: bool, +} + +#[derive(Clone, ValueEnum)] +enum TradePreimageMethodArg { + SetPrice, + Buy, + Sell, +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_utility.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_utility.rs new file mode 100644 index 0000000000..cf5d133adc --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_utility.rs @@ -0,0 +1,104 @@ +use clap::{Args, Subcommand}; +use rpc::v1::types::H256 as H256Json; +use std::mem::take; +use std::str::FromStr; + +use mm2_rpc::data::legacy::{BanPubkeysRequest, UnbanPubkeysRequest as UnbanPubkeysRequestImpl}; + +use crate::rpc_data::utility::GetCurrentMtpRequest; +use crate::rpc_data::UnbanPubkeysRequest; + +#[derive(Subcommand)] +pub(crate) enum UtilityCommands { + #[command( + visible_alias = "ban", + about = "Bans the given pubkey ignoring its order matching messages and preventing its \ + orders from displaying in the orderbook. \ + Use the secp256k1 pubkey without prefix for this method input" + )] + BanPubkey(BanPubkeyArgs), + #[command( + visible_aliases = ["list", "ban-list", "list-banned"], + about = "Returns a list of public keys of nodes that are banned from interacting with the node executing the method" + )] + ListBannedPubkeys, + #[command( + visible_alias = "unban", + about = "Remove all currently banned pubkeys from ban list, or specific pubkeys" + )] + UnbanPubkeys(UnbanPubkeysArgs), + #[command( + visible_aliases = ["current-mtp", "mtp"], + about = "Returns the Median Time Past (MTP) from electrum servers for UTXO coins" + )] + GetCurrentMtp(GetCurrentMtpArgs), +} + +#[derive(Args)] +pub(crate) struct BanPubkeyArgs { + #[arg( + long, + short, + value_parser = H256Json::from_str, + help = "Pubkey to ban" + )] + pubkey: H256Json, + #[arg(long, short, help = "Reason of banning")] + reason: String, +} + +impl From<&mut BanPubkeyArgs> for BanPubkeysRequest { + fn from(value: &mut BanPubkeyArgs) -> Self { + BanPubkeysRequest { + pubkey: take(&mut value.pubkey), + reason: take(&mut value.reason), + } + } +} + +#[derive(Args)] +#[group(required = true, multiple = false)] +pub(crate) struct UnbanPubkeysArgs { + #[arg( + long, + short, + group = "unban-pubkeys", + default_value_t = false, + help = "Whether to unban all pubkeys" + )] + pub(crate) all: bool, + #[arg( + long, + short, + group = "unban-pubkeys", + value_parser = H256Json::from_str, + help="Pubkey to unban" + )] + pub(crate) pubkey: Vec, +} + +impl From<&mut UnbanPubkeysArgs> for UnbanPubkeysRequest { + fn from(value: &mut UnbanPubkeysArgs) -> Self { + UnbanPubkeysRequest { + unban_by: if value.all { + UnbanPubkeysRequestImpl::All + } else { + UnbanPubkeysRequestImpl::Few(take(&mut value.pubkey)) + }, + } + } +} + +#[derive(Args)] +pub(crate) struct GetCurrentMtpArgs { + #[arg(help = "A compatible (UTXO) coin's ticker")] + coin: String, +} + +impl From<&mut GetCurrentMtpArgs> for GetCurrentMtpRequest { + fn from(value: &mut GetCurrentMtpArgs) -> Self { + GetCurrentMtpRequest { + coin: take(&mut value.coin), + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_version_stat.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_version_stat.rs new file mode 100644 index 0000000000..a487de3b38 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_version_stat.rs @@ -0,0 +1,109 @@ +use clap::{Args, Subcommand}; +use std::mem::take; + +use crate::rpc_data::version_stat::{VStatStartCollectionRequest, VStatUpdateCollectionRequest, + VersionStatAddNodeRequest, VersionStatRemoveNodeRequest}; + +#[derive(Subcommand)] +pub(crate) enum VersionStatCommands { + #[command( + short_flag = 'a', + visible_aliases = ["add", "add-node-to-version-stat"], + about = "Adds a Node's name, IP address and PeerID to a local database to track which version of MM2 it is running. \ + Note: To allow collection of version stats, added nodes must open port 38890" + )] + AddNode(VersionStatAddNodeArgs), + #[command( + short_flag = 'r', + visible_aliases = ["remove", "remove-node-from-version-stat"], + about = "Removes a Node (by name) from the local database which tracks which version of MM2 it is running" + )] + RemoveNode(VersionStatRemoveNodeArgs), + #[command( + short_flag = 's', + visible_aliases = ["start", "start-version-stat-collection"], + about = "Initiates storing version statistics for nodes previously registered via the add-node command" + )] + StartCollect(VStatStartCollectionArgs), + #[command( + short_flag = 'S', + visible_aliases = ["stop", "stop-version-stat-collection"], + about = "Stops the collection of version stats at the end of the current loop interval" + )] + StopCollect, + #[command( + short_flag = 'u', + visible_aliases = ["update", "update-version-stat-collection"], + about = "Updates the polling interval for version stats collection. Note: the new interval \ + will take effect after the current interval loop has completed." + )] + UpdateCollect(VStatUpdateCollectionArgs), +} + +#[derive(Args)] +pub(crate) struct VersionStatAddNodeArgs { + #[arg( + long, + short, + help = "The name assigned to the node, arbitrary identifying string, such as \"seed_alpha\" or \"dragonhound_DEV\"" + )] + name: String, + #[arg(long, short, help = "The Node's IP address or domain names")] + address: String, + #[arg( + long, + short, + help = "The Peer ID can be found in the MM2 log file after a connection has been initiated" + )] + peer_id: String, +} + +impl From<&mut VersionStatAddNodeArgs> for VersionStatAddNodeRequest { + fn from(value: &mut VersionStatAddNodeArgs) -> Self { + VersionStatAddNodeRequest { + name: take(&mut value.name), + address: take(&mut value.address), + peer_id: take(&mut value.peer_id), + } + } +} + +#[derive(Args)] +pub(crate) struct VersionStatRemoveNodeArgs { + #[arg( + help = "The name assigned to the node, arbitrary identifying string, such as \"seed_alpha\" or \"dragonhound_DEV\"" + )] + name: String, +} + +impl From<&mut VersionStatRemoveNodeArgs> for VersionStatRemoveNodeRequest { + fn from(value: &mut VersionStatRemoveNodeArgs) -> Self { + VersionStatRemoveNodeRequest { + name: take(&mut value.name), + } + } +} + +#[derive(Args)] +pub(crate) struct VStatStartCollectionArgs { + #[arg(help = "Polling rate (in seconds) to check node versions")] + interval: f64, +} + +type VStatUpdateCollectionArgs = VStatStartCollectionArgs; + +impl From<&mut VStatStartCollectionArgs> for VStatStartCollectionRequest { + fn from(value: &mut VStatStartCollectionArgs) -> Self { + VStatStartCollectionRequest { + interval: value.interval, + } + } +} + +impl From<&mut VStatUpdateCollectionArgs> for VStatUpdateCollectionRequest { + fn from(value: &mut VStatUpdateCollectionArgs) -> Self { + VStatUpdateCollectionRequest { + interval: value.interval, + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/commands_wallet.rs b/mm2src/komodefi_cli/src/cli_cmd_args/commands_wallet.rs new file mode 100644 index 0000000000..4a52c6adad --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/commands_wallet.rs @@ -0,0 +1,605 @@ +use anyhow::{anyhow, bail, Error, Result}; +use clap::{Args, Subcommand, ValueEnum}; +use hex::FromHexError; +use rpc::v1::types::Bytes as BytesJson; +use std::mem::take; +use std::num::NonZeroUsize; +use std::str::FromStr; +use std::{f64, u64}; + +use common::log::error; +use common::PagingOptionsEnum; +use mm2_number::BigDecimal; +use mm2_rpc::data::legacy::BalanceRequest; +use mm2_rpc::data::version2::GetRawTransactionRequest; + +use crate::rpc_data::wallet::{CashAddressNetwork as RpcCashAddressNetwork, + ConvertAddressFormat as RpcConvertAddressFormat, ConvertAddressRequest, + ConvertUtxoAddressRequest, MyTxHistoryRequest, MyTxHistoryRequestV2, + ShowPrivateKeyRequest, StandardHDCoinAddress, ValidateAddressRequest}; +use crate::rpc_data::{SendRawTransactionRequest, WithdrawFee, WithdrawFrom, WithdrawRequest}; +use crate::{error_anyhow, error_bail}; + +#[derive(Subcommand)] +pub(crate) enum WalletCommands { + #[command(visible_alias = "balance", about = "Get coin balance")] + MyBalance(MyBalanceArgs), + #[command( + about = "Generates, signs, and returns a transaction that transfers the amount of coin to \ + the address indicated in the to argument" + )] + Withdraw(WithdrawArgs), + #[command( + visible_aliases = ["send-raw", "send"], + about = "Broadcasts the transaction to the network of the given coin" + )] + SendRawTransaction(SendRawTransactionArgs), + #[command( + visible_aliases = ["get-raw", "raw-tx", "get"], + about = "Returns the full signed raw transaction hex for any transaction that is confirmed \ + or within the mempool" + )] + GetRawTransaction(GetRawTransactionArgs), + #[command( + visible_aliases = ["history"], + about = "Returns the blockchain transactions involving the Komodo DeFi Framework node's coin address" + )] + TxHistory(TxHistoryArgs), + #[command( + visible_aliases = ["private", "private-key"], + about = "Returns the private key of the specified coin in a format compatible with coin wallets" + )] + ShowPrivKey(ShowPrivKeyArgs), + #[command( + visible_aliases = ["validate"], + about = "Checks if an input string is a valid address of the specified coin" + )] + ValidateAddress(ValidateAddressArgs), + #[command( + visible_aliases = ["rewards"], + about = "Informs about the active user rewards that can be claimed by an address's unspent outputs" + )] + KmdRewardsInfo, + #[command( + visible_aliases = ["convert"], + about = "Converts an input address to a specified address format" + )] + ConvertAddress(ConvertAddressArgs), + #[command( + visible_aliases = ["convert-utxo"], + about = "Takes a UTXO address as input, and returns the equivalent address for another \ + UTXO coin (e.g. from BTC address to RVN address)")] + ConvertUtxoAddress(ConvertUtxoArgs), + #[command( + visible_aliases = ["get-public", "public-key", "public"], + about = "Returns the compressed secp256k1 pubkey corresponding to the user's seed phrase" + )] + GetPublicKey, + #[command( + visible_aliases = ["pubkey-hash", "hash", "pubhash"], + about = "Returns the RIPEMD-160 hash version of your public key" + )] + GetPublicKeyHash, +} + +#[derive(Args)] +pub(crate) struct MyBalanceArgs { + #[arg(name = "COIN", help = "Coin to get balance")] + coin: String, +} + +impl From<&mut MyBalanceArgs> for BalanceRequest { + fn from(value: &mut MyBalanceArgs) -> Self { + BalanceRequest { + coin: take(&mut value.coin), + } + } +} + +#[derive(Args)] +pub(crate) struct SendRawTransactionArgs { + #[arg(long, short, help = "Name of the coin network on which to broadcast the transaction")] + coin: String, + #[arg( + long, + short, + value_parser=parse_bytes, + help="Transaction bytes in hexadecimal format;" + )] + tx_hex: BytesJson, + #[arg( + long, + short, + default_value_t = false, + visible_alias = "bare", + help = "Whether to output only tx_hash" + )] + pub(crate) bare_output: bool, +} + +fn parse_bytes(value: &str) -> Result { + let bytes = hex::decode(value)?; + Ok(BytesJson(bytes)) +} + +impl From<&mut SendRawTransactionArgs> for SendRawTransactionRequest { + fn from(value: &mut SendRawTransactionArgs) -> Self { + SendRawTransactionRequest { + coin: take(&mut value.coin), + tx_hex: take(&mut value.tx_hex), + } + } +} + +#[derive(Args)] +pub(crate) struct WithdrawArgs { + #[arg(help = "Coin the user desires to withdraw")] + coin: String, + #[arg(help = "Address the user desires to withdraw to")] + to: String, + #[command(flatten)] + amount: WithdrawAmountArg, + #[arg( + long, + short, + value_parser = parse_withdraw_fee, + help = "Transaction fee [possible-values: ]" + )] + fee: Option, + #[command(flatten)] + from: Option, + #[arg( + long, + short, + default_value_t = false, + visible_alias = "bare", + help = "Whether to output only tx_hex" + )] + pub(crate) bare_output: bool, +} + +#[derive(Args)] +#[group(required = false, multiple = true)] +struct WithdrawFromArgs { + #[arg( + long, + help = "Derivation path to determine the source of the derived value in more detail" + )] + derivation_path: Option, + #[arg(long, help = "Account index of the same crypto currency")] + hd_account_id: Option, + #[arg(long, value_enum, help = "Is change")] + hd_is_change: Option, + #[arg(long, help = "An incremental address index for the account")] + hd_address_index: Option, +} + +#[derive(Clone, Debug, ValueEnum)] +enum Bip44ChainArg { + External, + Internal, +} + +#[derive(Args)] +#[group(required = false, multiple = false)] +pub(crate) struct WithdrawAmountArg { + #[arg( + long, + short = 'M', + group = "withdraw-amount", + default_value_t = false, + help = "Withdraw the maximum available amount" + )] + pub(crate) max: bool, + #[arg(long, short, group = "withdraw-amount", help = "Amount the user desires to withdraw")] + pub(crate) amount: Option, +} + +#[derive(Clone)] +pub(crate) enum WithdrawFeeArg { + UtxoFixed { amount: BigDecimal }, + UtxoPerKbyte { amount: BigDecimal }, + EthGas { gas_price: BigDecimal, gas: u64 }, + Qrc20Gas { gas_limit: u64, gas_price: u64 }, + CosmosGas { gas_limit: u64, gas_price: f64 }, +} + +fn parse_withdraw_fee(value: &str) -> Result { + #[derive(Clone, ValueEnum)] + pub(crate) enum WithdrawFeeTag { + UtxoFixed, + UtxoPerKbyte, + Eth, + Qrc, + Cosmos, + } + let mut parts = value.split(':'); + match parts.next() { + Some(tag) => match WithdrawFeeTag::from_str(tag, false) { + Ok(WithdrawFeeTag::UtxoFixed) => { + let Some(amount) = parts.next() else { + error_bail!("Failed to parse utxo-fixed fee, no amount literal"); + }; + let amount = BigDecimal::from_str(amount) + .map_err(|error| error_anyhow!("Failed to parse amount from: {}, error: {}", amount, error))?; + Ok(WithdrawFeeArg::UtxoFixed { amount }) + }, + Ok(WithdrawFeeTag::UtxoPerKbyte) => { + let Some(amount) = parts.next() else { + error_bail!("Failed to parse utxo-per-kbyte fee, no amount literal"); + }; + let amount = BigDecimal::from_str(amount) + .map_err(|error| error_anyhow!("Failed to parse amount from: {}, error: {}", amount, error))?; + Ok(WithdrawFeeArg::UtxoPerKbyte { amount }) + }, + Ok(WithdrawFeeTag::Eth) => { + let Some(gas_price) = parts.next() else { + error_bail!("Failed to parse eth fee, no gas_price literal"); + }; + let gas_price = BigDecimal::from_str(gas_price).map_err(|error| { + error_anyhow!("Failed to parse gas_price from: {}, error: {}", gas_price, error) + })?; + let Some(gas) = parts.next() else { + error_bail!("Failed to parse eth fee, no gas literal"); + }; + let gas = u64::from_str(gas) + .map_err(|error| error_anyhow!("Failed to parse gas from: {}, error: {}", gas, error))?; + + Ok(WithdrawFeeArg::EthGas { gas_price, gas }) + }, + Ok(WithdrawFeeTag::Qrc) => { + let Some(gas_limit) = parts.next() else { + error_bail!("Failed to parse qrc fee, no gas_limit literal"); + }; + let gas_limit = u64::from_str(gas_limit).map_err(|error| { + error_anyhow!("Failed to parse gas_limit from: {}, error: {}", gas_limit, error) + })?; + + let Some(gas_price) = parts.next() else { + error_bail!("Failed to parse qrc fee, no gas_price literal"); + }; + let gas_price = u64::from_str(gas_price).map_err(|error| { + error_anyhow!("Failed to parse gas_price from: {}, error: {}", gas_price, error) + })?; + Ok(WithdrawFeeArg::Qrc20Gas { gas_limit, gas_price }) + }, + Ok(WithdrawFeeTag::Cosmos) => { + let Some(gas_limit) = parts.next() else { + error_bail!("Failed to parse cosomos fee, no gas_limit literal"); + }; + let gas_limit = u64::from_str(gas_limit).map_err(|error| { + error_anyhow!("Failed to parse gas_limit from: {}, error: {}", gas_limit, error) + })?; + + let Some(gas_price) = parts.next() else { + error_bail!("Failed to parse cosmos fee, no gas_price literal"); + }; + let gas_price = f64::from_str(gas_price).map_err(|error| { + error_anyhow!("Failed to parse gas_price from: {}, error: {}", gas_price, error) + })?; + Ok(WithdrawFeeArg::CosmosGas { gas_limit, gas_price }) + }, + Err(error) => { + error_bail!("Failed to parse fee_tag: {}", error) + }, + }, + None => { + error_bail!("Failed to parse withdraw_fee: tag literal has not been found"); + }, + } +} + +impl TryFrom<&mut WithdrawArgs> for WithdrawRequest { + type Error = anyhow::Error; + + fn try_from(value: &mut WithdrawArgs) -> std::result::Result { + let from = if let Some(from) = value.from.as_mut() { + Some(WithdrawFrom::try_from(from)?) + } else { + None + }; + Ok(WithdrawRequest { + coin: take(&mut value.coin), + from, + to: take(&mut value.to), + amount: value.amount.amount.take().unwrap_or_default(), + max: value.amount.max, + fee: value.fee.as_mut().map(WithdrawFee::from), + }) + } +} + +impl From<&mut WithdrawFeeArg> for WithdrawFee { + fn from(value: &mut WithdrawFeeArg) -> Self { + match value { + WithdrawFeeArg::UtxoFixed { amount } => WithdrawFee::UtxoFixed { amount: take(amount) }, + WithdrawFeeArg::UtxoPerKbyte { amount } => WithdrawFee::UtxoPerKbyte { amount: take(amount) }, + WithdrawFeeArg::EthGas { gas_price, gas } => WithdrawFee::EthGas { + gas_price: take(gas_price), + gas: take(gas), + }, + WithdrawFeeArg::Qrc20Gas { gas_limit, gas_price } => WithdrawFee::Qrc20Gas { + gas_limit: take(gas_limit), + gas_price: take(gas_price), + }, + WithdrawFeeArg::CosmosGas { gas_limit, gas_price } => WithdrawFee::CosmosGas { + gas_limit: take(gas_limit), + gas_price: take(gas_price), + }, + } + } +} + +impl TryFrom<&mut WithdrawFromArgs> for WithdrawFrom { + type Error = anyhow::Error; + + fn try_from(value: &mut WithdrawFromArgs) -> std::result::Result { + if let Some(derivation_path) = value.derivation_path.take() { + return Ok(WithdrawFrom::DerivationPath { derivation_path }); + }; + let account_id = value.hd_account_id.take(); + let is_change = value.hd_is_change.take(); + let address_id = value.hd_address_index.take(); + if let (Some(account), Some(is_change), Some(address_index)) = (account_id, is_change, address_id) { + return Ok(WithdrawFrom::HDWalletAddress(StandardHDCoinAddress { + account, + is_change, + address_index, + })); + }; + error_bail!( + "Failed to get withdraw_from due to params incompatibility: account_id: {:?}, chain: {:?}, address_id: {:?}", + account_id, + is_change, + address_id + ) + } +} + +#[derive(Args)] +pub(crate) struct GetRawTransactionArgs { + #[arg(long, short, help = "Coin the user desires to request for the transaction")] + pub(crate) coin: String, + #[arg( + long, + value_parser=parse_bytes, + visible_alias = "hash", + short = 'H', + help = "Hash of the transaction" + )] + pub(crate) tx_hash: BytesJson, + #[arg( + long, + short, + default_value_t = false, + visible_alias = "bare", + help = "Whether to output only tx_hex" + )] + pub(crate) bare_output: bool, +} + +impl From<&mut GetRawTransactionArgs> for GetRawTransactionRequest { + fn from(value: &mut GetRawTransactionArgs) -> Self { + GetRawTransactionRequest { + coin: take(&mut value.coin), + tx_hash: hex::encode(value.tx_hash.as_slice()), + } + } +} + +#[derive(Args)] +pub(crate) struct TxHistoryArgs { + #[arg(help = "The name of the coin for the history request")] + pub(crate) coin: String, + #[command(flatten)] + limit: TxHistoryLimitGroup, + #[command(flatten)] + from_id: FromIdGroup, + #[arg( + long, + visible_alias = "page", + short = 'p', + help = "The name of the coin for the history request" + )] + page_number: Option, +} + +#[derive(Default, Args)] +#[group(required = false, multiple = false)] +struct FromIdGroup { + #[arg( + long, + short = 'H', + value_parser = parse_bytes, + help = "Skips records until it reaches this hash, skipping the from_id as well" + )] + from_tx_hash: Option, + #[arg( + long, + short = 'i', + help = "For zcoin compatibility, skips records until it reaches this ID, skipping the from_id as well" + )] + from_tx_id: Option, +} + +#[derive(Args)] +#[group(required = true, multiple = false)] +struct TxHistoryLimitGroup { + #[arg( + group = "limit-group", + long, + short, + help = "Limits the number of returned transactions" + )] + limit: Option, + #[arg( + group = "limit-group", + long, + short, + default_value_t = false, + help = "Whether to return all available records" + )] + max: bool, +} + +impl From<&mut TxHistoryArgs> for MyTxHistoryRequest { + fn from(value: &mut TxHistoryArgs) -> Self { + MyTxHistoryRequest { + coin: take(&mut value.coin), + from_id: value.from_id.from_tx_hash.take(), + max: value.limit.max, + limit: value.limit.limit.unwrap_or(10), + page_number: value.page_number.take(), + } + } +} + +trait FromId { + fn get(self) -> Option; +} + +impl FromId for FromIdGroup { + fn get(mut self) -> Option { self.from_tx_hash.take() } +} + +impl FromId for FromIdGroup { + fn get(mut self) -> Option { self.from_tx_id.take() } +} + +impl From<&mut TxHistoryArgs> for MyTxHistoryRequestV2 +where + FromIdGroup: FromId, +{ + fn from(value: &mut TxHistoryArgs) -> Self { + let paging_options = if let Some(from_id) = take(&mut value.from_id).get() { + PagingOptionsEnum::FromId(from_id) + } else if let Some(page_number) = value.page_number.take() { + if page_number > 0 { + PagingOptionsEnum::PageNumber( + NonZeroUsize::new(page_number).expect("Page number is expected to be greater than zero"), + ) + } else { + PagingOptionsEnum::default() + } + } else { + PagingOptionsEnum::default() + }; + + MyTxHistoryRequestV2 { + coin: take(&mut value.coin), + limit: if value.limit.max { + u32::MAX as usize + } else { + value + .limit + .limit + .expect("limit option is expected to be set due to group rules") + }, + paging_options, + } + } +} + +#[derive(Args)] +pub(crate) struct ShowPrivKeyArgs { + #[arg(help = "The name of the coin of the private key to show")] + coin: String, +} + +impl From<&mut ShowPrivKeyArgs> for ShowPrivateKeyRequest { + fn from(value: &mut ShowPrivKeyArgs) -> Self { + ShowPrivateKeyRequest { + coin: take(&mut value.coin), + } + } +} + +#[derive(Args)] +pub(crate) struct ValidateAddressArgs { + #[arg(help = "The coin to validate address for")] + coin: String, + #[arg(help = "The input string to validate")] + address: String, +} + +impl From<&mut ValidateAddressArgs> for ValidateAddressRequest { + fn from(value: &mut ValidateAddressArgs) -> Self { + ValidateAddressRequest { + coin: take(&mut value.coin), + address: take(&mut value.address), + } + } +} + +#[derive(Args)] +pub(crate) struct ConvertAddressArgs { + #[arg(long, short, help = "The name of the coin address context")] + coin: String, + #[arg(long, short, help = "Input address")] + from: String, + #[arg( + long, + short = 'F', + value_enum, + help = "Address format to which the input address should be converted" + )] + format: ConvertAddressFormat, + #[arg(long, short = 'C', value_enum, help = "Network prefix for cashaddress format")] + cash_address_network: Option, +} + +#[derive(Clone, ValueEnum)] +pub(crate) enum ConvertAddressFormat { + MixedCase, + CashAddress, + Standard, +} + +#[derive(Clone, ValueEnum)] +pub(crate) enum CashAddressNetwork { + BitcoinCash, + BchTest, + BchReg, +} + +impl TryFrom<&mut ConvertAddressArgs> for ConvertAddressRequest { + type Error = Error; + fn try_from(value: &mut ConvertAddressArgs) -> Result { + let to_address_format = match value.format { + ConvertAddressFormat::Standard => RpcConvertAddressFormat::Standard, + ConvertAddressFormat::MixedCase => RpcConvertAddressFormat::MixedCase, + ConvertAddressFormat::CashAddress => match value.cash_address_network { + Some(CashAddressNetwork::BitcoinCash) => { + RpcConvertAddressFormat::CashAddress(RpcCashAddressNetwork::BitcoinCash) + }, + Some(CashAddressNetwork::BchReg) => RpcConvertAddressFormat::CashAddress(RpcCashAddressNetwork::BchReg), + Some(CashAddressNetwork::BchTest) => { + RpcConvertAddressFormat::CashAddress(RpcCashAddressNetwork::BchTest) + }, + None => error_bail!("Failed to construct request from arguments, cash_address is not set"), + }, + }; + Ok(ConvertAddressRequest { + coin: take(&mut value.coin), + from: take(&mut value.from), + to_address_format, + }) + } +} + +#[derive(Args)] +pub(crate) struct ConvertUtxoArgs { + #[arg(help = "Input UTXO address")] + address: String, + #[arg(help = "Coin to convert input given address to")] + to_coin: String, +} + +impl From<&mut ConvertUtxoArgs> for ConvertUtxoAddressRequest { + fn from(value: &mut ConvertUtxoArgs) -> Self { + ConvertUtxoAddressRequest { + address: take(&mut value.address), + to_coin: take(&mut value.to_coin), + } + } +} diff --git a/mm2src/komodefi_cli/src/cli_cmd_args/mod.rs b/mm2src/komodefi_cli/src/cli_cmd_args/mod.rs new file mode 100644 index 0000000000..92c8be21d9 --- /dev/null +++ b/mm2src/komodefi_cli/src/cli_cmd_args/mod.rs @@ -0,0 +1,50 @@ +mod cmd_sell_buy; +mod cmd_set_config; +mod cmd_set_price; +mod cmd_task; +mod cmd_update_maker_order; + +mod commands_cancel; +mod commands_coin; +mod commands_message; +mod commands_mm2; +mod commands_network; +mod commands_order; +mod commands_swap; +mod commands_utility; +mod commands_version_stat; +mod commands_wallet; + +use anyhow::Result; +use chrono::{DateTime, TimeZone, Utc}; +use std::str::FromStr; + +use mm2_number::bigdecimal::ParseBigDecimalError; +use mm2_number::{BigDecimal, MmNumber}; + +pub(crate) mod prelude { + pub(crate) use super::cmd_sell_buy::{BuyOrderArgs, SellOrderArgs}; + pub(crate) use super::cmd_set_config::SetConfigArgs; + pub(crate) use super::cmd_set_price::SetPriceArgs; + pub(crate) use super::cmd_task::{TaskSubcommand, TaskSubcommandCancel, TaskSubcommandStatus}; + pub(crate) use super::cmd_update_maker_order::UpdateMakerOrderArgs; + pub(crate) use super::commands_cancel::CancelSubcommand; + pub(crate) use super::commands_coin::CoinCommands; + pub(crate) use super::commands_message::MessageCommands; + pub(crate) use super::commands_mm2::Mm2Commands; + pub(crate) use super::commands_network::NetworkCommands; + pub(crate) use super::commands_order::OrderCommands; + pub(crate) use super::commands_swap::SwapCommands; + pub(crate) use super::commands_utility::UtilityCommands; + pub(crate) use super::commands_version_stat::VersionStatCommands; + pub(crate) use super::commands_wallet::{TxHistoryArgs, WalletCommands}; +} + +fn parse_mm_number(value: &str) -> Result { + let decimal: BigDecimal = BigDecimal::from_str(value)?; + Ok(MmNumber::from(decimal)) +} + +fn parse_datetime(value: &str) -> Result, chrono::ParseError> { + Utc.datetime_from_str(value, "%y-%m-%dT%H:%M:%S") +} diff --git a/mm2src/komodefi_cli/src/config.rs b/mm2src/komodefi_cli/src/config.rs new file mode 100644 index 0000000000..22db48f952 --- /dev/null +++ b/mm2src/komodefi_cli/src/config.rs @@ -0,0 +1,233 @@ +use crate::cli::get_cli_root; +use crate::cli_cmd_args::prelude::SetConfigArgs; +use crate::helpers::rewrite_json_file; +use crate::komodefi_proc::SmartFractPrecision; +use crate::logging::{error_anyhow, warn_bail}; + +use anyhow::{anyhow, bail, Result}; +#[cfg(unix)] use common::log::debug; +use common::log::{error, info, warn}; +use inquire::Password; +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; +use std::fs; +#[cfg(unix)] use std::os::unix::fs::PermissionsExt; +use std::path::{Path, PathBuf}; +use url::{ParseError, Url}; + +const KOMODEFI_CFG: &str = "komodefi_cfg.json"; + +const PRICE_PRECISION_MIN: usize = 8; +const PRICE_PRECISION_MAX: usize = 8; +const VOLUME_PRECISION_MIN: usize = 2; +const VOLUME_PRECISION_MAX: usize = 5; +const VOLUME_PRECISION: SmartFractPrecision = (VOLUME_PRECISION_MIN, VOLUME_PRECISION_MAX); +const PRICE_PRECISION: SmartFractPrecision = (PRICE_PRECISION_MIN, PRICE_PRECISION_MAX); +#[cfg(unix)] +const CFG_FILE_PERM_MODE: u32 = 0o660; + +pub(super) fn get_config(option: &crate::cli::GetOption) { + let Ok(komodefi_cfg) = KomodefiConfigImpl::from_config_path() else { return; }; + if option.unhide { + komodefi_cfg.print_config_with_password() + } else { + info!("{}", komodefi_cfg) + } +} + +pub(super) fn set_config(config: &mut SetConfigArgs) -> Result<()> { + let mut komodefi_cfg = KomodefiConfigImpl::from_config_path().unwrap_or_else(|_| KomodefiConfigImpl::default()); + + if config.from_path { + let (uri, password) = config.source_from_path()?; + komodefi_cfg.set_rpc_uri(uri); + komodefi_cfg.set_rpc_password(password); + } else { + config.inquire()?; + let set_password = config + .password + .ok_or_else(|| error_anyhow!("No set password option detected in config"))?; + let rpc_api_uri = config.uri.take(); + assert!(set_password || rpc_api_uri.is_some()); + + if set_password { + let rpc_password = Password::new("Enter RPC API password:") + .prompt() + .map_err(|error| error_anyhow!("Failed to get rpc_api_password: {error}"))?; + komodefi_cfg.set_rpc_password(rpc_password); + } + + if let Some(mut rpc_api_uri) = rpc_api_uri { + validate_rpc_uri_scheme(config.secure_conn.unwrap_or_default(), &mut rpc_api_uri)?; + komodefi_cfg.set_rpc_uri(rpc_api_uri); + } + } + + komodefi_cfg.write_to_config_path()?; + info!("Configuration has been set"); + + Ok(()) +} + +/// Validates an RPC URL and add base if not provided. +fn validate_rpc_uri_scheme(secure_conn: bool, input: &mut String) -> Result<(), anyhow::Error> { + match Url::parse(input) { + Ok(url) => { + if matches!(url.scheme(), "http" | "https") { + let scheme = if secure_conn { "https" } else { "http" }; + *input = format!("{scheme}://{}:{}", url.host().unwrap(), url.port().unwrap()); + return Ok(()); + } + + Ok(()) + }, + Err(err) => match err { + ParseError::RelativeUrlWithoutBase => { + let scheme = if secure_conn { "https" } else { "http" }; + *input = format!("{scheme}://{input}"); + Ok(()) + }, + _ => Err(error_anyhow!("Invalid RPC URI: {err:?}")), + }, + } +} + +pub(super) trait KomodefiConfig { + fn rpc_password(&self) -> Option; + fn rpc_uri(&self) -> Option; + fn orderbook_price_precision(&self) -> &SmartFractPrecision; + fn orderbook_volume_precision(&self) -> &SmartFractPrecision; +} + +#[derive(Debug, Default, Deserialize, Serialize)] +pub(super) struct KomodefiConfigImpl { + #[serde(skip_serializing_if = "Option::is_none")] + rpc_password: Option, + #[serde(skip_serializing_if = "Option::is_none")] + rpc_uri: Option, +} + +impl Display for KomodefiConfigImpl { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if !self.is_set() { + return writeln!(f, "komodefi configuration is not set"); + } + writeln!( + f, + "mm2 RPC URL: {}", + self.rpc_uri.as_ref().expect("Expected rpc_uri is set") + )?; + writeln!(f, "mm2 RPC password: *************")?; + Ok(()) + } +} + +impl KomodefiConfig for KomodefiConfigImpl { + fn rpc_password(&self) -> Option { self.rpc_password.clone() } + fn rpc_uri(&self) -> Option { self.rpc_uri.clone() } + fn orderbook_price_precision(&self) -> &SmartFractPrecision { &PRICE_PRECISION } + fn orderbook_volume_precision(&self) -> &SmartFractPrecision { &VOLUME_PRECISION } +} + +impl KomodefiConfigImpl { + #[cfg(test)] + pub(super) fn new(rpc_password: &str, rpc_uri: &str) -> Self { + Self { + rpc_password: Some(rpc_password.to_string()), + rpc_uri: Some(rpc_uri.to_string()), + } + } + + #[cfg(not(test))] + pub(super) fn read_config() -> Result { + let config = KomodefiConfigImpl::from_config_path()?; + if config.rpc_password.is_none() { + warn!("Configuration is not complete, no rpc_password in there"); + } + if config.rpc_uri.is_none() { + warn!("Configuration is not complete, no rpc_uri in there"); + } + Ok(config) + } + + fn is_set(&self) -> bool { self.rpc_uri.is_some() && self.rpc_password.is_some() } + + pub(crate) fn get_config_path() -> Result { + let mut config_path = get_cli_root()?; + config_path.push(KOMODEFI_CFG); + + Ok(config_path) + } + + fn from_config_path() -> Result { + let config_path = Self::get_config_path()?; + + if !config_path.exists() { + warn_bail!("Config is not set") + } + Self::read_from(&config_path) + } + + fn write_to_config_path(&self) -> Result<()> { + let config_path = Self::get_config_path()?; + self.write_to(&config_path) + } + + fn read_from(cfg_path: &Path) -> Result { + let komodefi_path_str = cfg_path.to_str().unwrap_or("Undefined"); + let komodefi_cfg_file = fs::File::open(cfg_path) + .map_err(|error| error_anyhow!("Failed to open: {komodefi_path_str}, error: {error}"))?; + + serde_json::from_reader(komodefi_cfg_file).map_err(|error| { + error_anyhow!("Failed to read komodefi_cfg to read from: {komodefi_path_str}, error: {error}") + }) + } + + fn write_to(&self, cfg_path: &Path) -> Result<()> { + let komodefi_path_str = cfg_path + .to_str() + .ok_or_else(|| error_anyhow!("Failed to get cfg_path as str"))?; + rewrite_json_file(self, komodefi_path_str)?; + #[cfg(unix)] + { + Self::warn_on_insecure_mode(komodefi_path_str)?; + } + Ok(()) + } + + #[cfg(unix)] + fn warn_on_insecure_mode(file_path: &str) -> Result<()> { + let perms = fs::metadata(file_path)?.permissions(); + let mode = perms.mode() & 0o777; + if mode != CFG_FILE_PERM_MODE { + warn!( + "Configuration file: '{}' - does not comply to the expected mode: {:o}, the actual one is: {:o}", + file_path, CFG_FILE_PERM_MODE, mode + ); + + // Update permissions to the desired mode + debug!("Updating permission..."); + let new_perms = fs::Permissions::from_mode(CFG_FILE_PERM_MODE); + fs::set_permissions(Path::new(file_path), new_perms)?; + debug!("Permission Updated!, new permission: {mode}"); + }; + + Ok(()) + } + + fn set_rpc_password(&mut self, rpc_password: String) { self.rpc_password.replace(rpc_password); } + + fn set_rpc_uri(&mut self, rpc_uri: String) { self.rpc_uri.replace(rpc_uri); } + + fn print_config_with_password(&self) { + if !self.is_set() { + return info!("komodefi configuration is not set"); + } + + info!( + "mm2 RPC URL: {}\nmm2 RPC password: {}", + self.rpc_uri.as_ref().expect("Expected rpc_uri is set"), + self.rpc_password.clone().expect("Expected rpc_password is not set") + ) + } +} diff --git a/mm2src/komodefi_cli/src/doc/CLI_REFERENCE.md b/mm2src/komodefi_cli/src/doc/CLI_REFERENCE.md new file mode 100644 index 0000000000..28b782530f --- /dev/null +++ b/mm2src/komodefi_cli/src/doc/CLI_REFERENCE.md @@ -0,0 +1,2481 @@ +`komodefi-cli` provides a CLI interface and facilitates interoperating to komodo defi platform via the `mm2` service. It's a multi-purpose utility that facilitates using komodo platform to be used as a multi-chain Wallet and DEX at the same time. + +## Build + +The `komodefi-cli` binary file can be built in the root of the project using the following commands: + +```sh +export PATH=${PATH}:$(pwd)/bin +cargo build --manifest-path=mm2src/komodefi_cli/Cargo.toml --out-dir bin -Z unstable-options +``` + +Now `komodefi-cli` is built and available in the `bin` directory and can be called, as is done in the current reference examples. + +## Manage mm2 service + +`komodefi-cli` should be configured in a proper way to be able to connect to and interract with the running `mm2` service via the Komodo RPC API. The `mm2` can be started manually or by using the `komodefi-cli` that facilitates configuring and managing it as a process. + +### Setting the Default CLI Root +You can customize the default root directory for `komodefi-cli` by setting the `KOMODEFI_CLI_ROOT` environment variable. This allows you to specify a location for saving configuration files, binaries, and other related data. + +```sh +export KOMODEFI_CLI_ROOT=$HOME/komodefi-cli +komodefi-cli mm2 download +``` + +### init + +The `init` command is aiming to facilitate creating of the `MM2.json` configuration file and getting the `coins` configuration. The `komodefi-cli` implements it in an interactive step-by-step mode and produces `MM2.json` as a result. `coins` configuration is got from the https://raw.githubusercontent.com/KomodoPlatform/coins/master/coins. The `init` also provides alternative paths setting by additional options. + +```sh +komodefi-cli init --help +Initialize a predefined coin set and configuration to start mm2 instance with + +Usage: komodefi-cli init [OPTIONS] + +Options: + --mm-coins-path Coin set file path [default: coins] [aliases: coins] + --mm-conf-path mm2 configuration file path [default: MM2.json] [aliases: conf] + -h, --help Print help +``` + +**Example**: + +```sh +RUST_LOG=error komodefi-cli init +> What is the network `mm2` is going to be a part, netid: 7777 +> What is the seed phrase: upgrade hunt engage mountain cheap hood attitude bleak flag wild feature aim +> Allow weak password: No +> What is the rpc_password: D6~$jETp +> What is dbdir None +> What is rpcip: None +> What is the rpcport: None +> What is rpc_local_only: +> Use secure connection for rpc: No +> What is i_am_a_seed: +? What is the next seednode: Tap enter to skip +[Optional. If operating on a test or private netID, the IP address of at least one seed node is required (on the main network, these are already hardcoded)] +... +``` + +resulting `MM2.json`: + +```json +{ + "gui": "komodefi-cli", + "netid": 7777, + "rpc_password": "D6~$jETp", + "passphrase": "upgrade hunt engage mountain cheap hood attitude bleak flag wild feature aim", + "allow_weak_password": false, + "rpcip": "127.0.0.1", + "rpcport": 7783, + "secure_conn": false +} +``` + +### mm2 download +Download the latest version of `mm2` binary and save to `KOMODEFI_CLI_ROOT` dir. + +Usage: +```sh +komodefi-cli mm2 download +``` + +### mm2 start + +The `start` command is used to start `mm2` instance. Options are used to be set as an environment variables of the `mm2 `: `MM_CONF_PATH`, `MM_COINS_PATH` and `MM_LOG` accordingly. + +```sh +komodefi-cli mm2 start -help +Start mm2 instance + +Usage: komodefi-cli mm2 start [OPTIONS] + +Options: + --mm-conf-path mm2 configuration file path [aliases: conf] + --mm-coins-path Coin set file path [aliases: coins] + --mm-log Log file path [aliases: log] + -h, --help Print help +``` + +**Example**: + +```sh +komodefi-cli mm2 start --mm-log mm2.log +Set env MM_LOG as: mm2.log +Started child process: "mm2", pid: 459264 +``` +### mm2 status + +The `check` command gets the state of the running `mm2` instance + +```sh +komodefi-cli mm2 status --help +Check if mm2 is running + +Usage: komodefi-cli mm2 status + +Options: + -h, --help Print help +``` + +**Example**: + +```sh +komodefi-cli mm2 status +Found mm2 is running, pid: 459264 +``` + +```sh +komodefi-cli mm2 status +Process not found: mm2 +``` +### mm2 version + +The `version` command requests the version of `mm2` using the [`version` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/version.html) + +```sh +komodefi-cli mm2 version --help +Get version of intermediary mm2 service + +Usage: komodefi-cli mm2 version + +Options: + -h, --help Print help +``` + +**Example**: + +```sh +komodefi-cli mm2 version +Version: 1.0.6-beta_9466e53c6 +Datetime: 2023-09-11T16:19:16+05:00 +``` + +### mm2 stop + +The `stop` command requests stopping of mm2 using the [`stop` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/stop.html) + +```sh +komodefi-cli mm2 stop --help +Stop mm2 using API + +Usage: komodefi-cli mm2 stop + +Options: + -h, --help Print help +``` + +**Example**: + +```sh +komodefi-cli mm2 stop +Sending stop command +Service stopped: Success +``` + +### mm2 kill + +The `kill` command kills the process of `mm2` if it is currently running + +```sh +komodefi-cli mm2 kill --help +Kill mm2 process + +Usage: komodefi-cli mm2 kill + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli mm2 kill +Process killed: mm2:505606 +``` + +## Configuring `komodefi-cli` + +Config commands are `set` and `get`. These are aimed to set and get password and uri to connect to and be +granted to request methods from the running `mm2` instance. Password is setting in an interactive mode. The certain path of the configuration depends on the operating system the `komodefi-cli` is running on and could be different. + +```sh +komodefi-cli config set --help +Set komodo komodefi cli configuration + +Usage: +- komodefi-cli config set <--password|--uri > +- komodefi-cli config set --from-path (sets config from MM2.json file automatically) + +Options: + -p, --password Set if you are going to set up a password + -u, --uri KomoDeFi RPC API Uri. http://localhost:7783 [aliases: url] + -h, --help Print help +``` + +**Examples:** + +Setting configuration: + +```sh +komodefi-cli config set -u https://localhost:7783 -p +? Enter RPC API password: +? Confirmation: +``` + +resulting `komodefi_cfg.json`: + +``` +cat ~/.config/komodefi-cli/komodefi_cfg.json +{ + "rpc_password": "D6~$jETp", + "rpc_uri": "https://localhost:7783" +} +``` + +Getting configuration: + +```sh +release/komodefi-cli config get +mm2 RPC URL: https://localhost:7783 +mm2 RPC password: ************* +``` + +```sh +release/komodefi-cli config get --unhide +mm2 RPC URL: https://localhost:7783 +mm2 RPC password: SomeVeryStrongPassword +``` + +## Setting up coin index + +To use the komodo platform as both a multi-chain wallet and DEX, there are commands to include and exclude certain coins from the wallet index. RPC API methods can mainly be requested to be used with only enabled coins. + +```sh +komodefi-cli coin +Coin commands: enable, disable etc. + +Usage: komodefi-cli coin + +Commands: + enable Put a coin to the trading index + disable Deactivates enabled coin and also cancels all active orders that use the selected coin. + get-enabled List activated coins [aliases: enabled] + set-required-conf Set the number of confirmations to wait for the selected coin [aliases: set-conf] + set-required-nota Whether to wait for a dPoW notarization of the given atomic swap transactions [aliases: set-nota] + coins-to-kick-start Return the coins that should be activated to continue the interrupted swaps [aliases: to-kick] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` +### coin enable + +The `enable` command includes the given coin in the komodo wallet index. Depending on the given coin the different RPC API method could be requested. For the [ZHTLC related method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20-dev/zhtlc_coins.html#task-enable-z-coin-init) - `--keep-progress` option is designed to request the status of the enabling task every N seconds. The `--tx-history` option overrides the predefined setting to make it able to request transactions history for the given coin. + +```sh +komodefi-cli coin enable --help +Put a coin to the trading index + +Usage: komodefi-cli coin enable [OPTIONS] + +Arguments: + Coin to be included into the trading index + +Options: + -k, --keep-progress Whether to keep progress on task based commands [default: 0] [aliases: track, keep, progress] + -H, --tx-history Whether to save tx history for the coin [aliases: history] + -h, --help Print help +``` + +*Notice: To override required conf and nota `set-required-conf` and `set-required-nota` commands could be used* + +**Examples** + +Enabling `DOC` using the ([legacy method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/coin_activation.html)) + +```sh +komodefi-cli coin enable --tx-history DOC +Enabling coin: DOC +coin: DOC +address: RNfMoWT47LWht7wwixJdx9QP51mkakLVsU +balance: 0 +unspendable_balance: 0 +required_confirmations: 1 +requires_notarization: No +mature_confirmations: 100 +``` + +Enabling `tBCH` ([v2.0 method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20/enable_bch_with_tokens.html)) + +```sh +komodefi-cli coin enable tBCH +Enabling coin: tBCH +current_block: 1570304 +bch_addresses_infos: +│ address, pubkey │ method │ balance(sp,unsp) │ tickers │ +│ bchtest:qzvnf6l254ktt97fx657mqszmrvh5znsqvs26sxf6t│ Iguana │ 0.05:0 │ none │ +│ 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d...│ │ │ │ + +slp_addresses_infos: +│ address, pubkey │ method │ balance(sp,unsp) │ tickers │ +│ slptest:qzvnf6l254ktt97fx657mqszmrvh5znsqvt7atu7gk│ Iguana │ {} │ none │ +│ 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d...│ │ │ │ +``` + +Enable ZHTLC `ZOMBIE` COIN ([`task_enable_z_coin_init` method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20-dev/zhtlc_coins.html#task-enable-z-coin-init)) + +```sh +komodefi-cli coin enable ZOMBIE --track 5 +Enabling coin: ZOMBIE +Enabling zcoin started, task_id: 0 +Getting enable zcoin task status +In progress: Activating coin +In progress: Activating coin +In progress: Activating coin +In progress: Activating coin +In progress: Updating block cache, current_scanned_block: 58980, latest_block: 77153 +In progress: Building wallet db, current_scanned_block: 56397, latest_block: 77153 +In progress: Building wallet db, current_scanned_block: 60397, latest_block: 77153 +In progress: Building wallet db, current_scanned_block: 65397, latest_block: 77153 +In progress: Building wallet db, current_scanned_block: 69397, latest_block: 77153 +In progress: Building wallet db, current_scanned_block: 73397, latest_block: 77153 +status: OK +current_block: 77154 +ticker: ZOMBIE +iguana wallet: + address: zs1r0fzx9unydgfty74z5d4qkvjyaky0n73ms4cvhttj4234s6rf0hfju5faf6a5nzlwv5qgrr0pen + balance: 50.77:0 +``` + +```sh +komodefi-cli coin enable ZOMBIE +Enabling coin: ZOMBIE +Enabling zcoin started, task_id: 1 + +komodefi-cli task status zcoin 1 +Getting enable zcoin task status +status: OK +current_block: 77154 +ticker: ZOMBIE +iguana wallet: + address: zs1r0fzx9unydgfty74z5d4qkvjyaky0n73ms4cvhttj4234s6rf0hfju5faf6a5nzlwv5qgrr0pen + balance: 50.77:0 +``` +### coin disable + +The `disable` command excludes the given coin from the komodo wallet index using the [disable_coin RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/disable_coin.html). + +```sh +komodefi-cli coin disable --help +Deactivates enabled coin and also cancels all active orders that use the selected coin. + +Usage: komodefi-cli coin disable + +Arguments: + Coin to disable + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +Disabling coin: DOC +coin: DOC +cancelled_orders: 8339efe3-ee0a-436c-ad6a-88ef8130ba2b +passivized: false +``` +### coin get-enabled (enabled) + +The `get-enabled` command lists coins that are already in the wallet index using the [get_enabled_coins RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/get_enabled_coins.html). *Alias: `enabled`* + +```sh +komodefi-cli coin get-enabled --help +List activated coins + +Usage: komodefi-cli coin get-enabled + +Options: + -h, --help Print help +``` + +**Example**: + +``` +komodefi-cli coin enabled +Getting list of enabled coins ... +Ticker Address +ZOMBIE zs1r0fzx9unydgfty74z5d4qkvjyaky0n73ms4cvhttj4234s6rf0hfju5faf6a5nzlwv5qgrr0pen +tBCH bchtest:qzvnf6l254ktt97fx657mqszmrvh5znsqvs26sxf6t +MARTY RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM +DOC RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM +``` +### coin coins-to-kick-start (to-kick) + +The `coins-to-kick-start` command lists coins that are involved in swaps that are not done and needed to be anbled to complete them. This command is proceed using the [`coins_needed_to_kick_start` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/coins_needed_for_kick_start.html) *Alias: `to-kick`* + +```sh +komodefi-cli coin to-kick --help +Return the coins that should be activated to continue the interrupted swaps + +Usage: komodefi-cli coin coins-to-kick-start + +Options: + -h, --help Print help + ``` + +**Example:** + +```sh +komodefi-cli coin to-kick +Getting coins needed for kickstart +coins: RICK, MORTY +``` +### coin set-required-conf (set-conf) + +The `set-required-conf` command temporary overrides the predefined `required-conf` setting of the given coin using the [`set_required_confirmations` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/set_required_confirmations.html) *Alias: `set-conf`* + +```sh +komodefi-cli coin set-conf --help +Set the number of confirmations to wait for the selected coin + +Usage: komodefi-cli coin set-required-conf + +Arguments: + Ticker of the selected coin + Number of confirmations to require [aliases: conf] + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli coin set-conf DOC 3 +Setting required confirmations: DOC, confirmations: 3 +coin: DOC +confirmations: 3 +``` + +### coin set-required-nota (set-nota) + +The `set-required-nota` command temporary overrides the predefined `required-nota` setting of the given coin using the [`set_required_notarization` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/set_requires_notarization.html). *Alias: `set-nota`* + +```sh +komodefi-cli coin set-nota --help +Whether to wait for a dPoW notarization of the given atomic swap transactions + +Usage: komodefi-cli coin set-required-nota [OPTIONS] + +Arguments: + Ticker of the selected coin + +Options: + -n, --requires-notarization Whether the node should wait for dPoW notarization of atomic swap transactions [aliases: requires-nota] + -h, --help Print help +``` + +**Examples:** + +Set `requried_nota`: + +```sh +komodefi-cli coin set-nota --requires-nota DOC +Setting required nota: DOC, requires_nota: true +coin: DOC +requires_notarization: true +``` + +Reset `required_nota`: + +```sh +komodefi-cli coin set-nota DOC +Setting required nota: DOC, requires_nota: false +coin: DOC +requires_notarization: false +``` + +## Wallet commands + +The further group of commands are aimed to provide base multi-chain wallet functionality. It includes commands like balance, withdraw, tx-history etc. + +```sh +komodefi-cli wallet +Wallet commands: balance, withdraw etc. + +Usage: komodefi-cli wallet + +Commands: + my-balance Get coin balance [aliases: balance] + withdraw Generates, signs, and returns a transaction that transfers the amount of coin to the address indicated in the to argument + send-raw-transaction Broadcasts the transaction to the network of the given coin [aliases: send-raw, send] + get-raw-transaction Returns the full signed raw transaction hex for any transaction that is confirmed or within the mempool [aliases: get-raw, raw-tx, get] + tx-history Returns the blockchain transactions involving the Komodo DeFi Framework node's coin address [aliases: history] + show-priv-key Returns the private key of the specified coin in a format compatible with coin wallets [aliases: private, private-key] + validate-address Checks if an input string is a valid address of the specified coin [aliases: validate] + kmd-rewards-info Informs about the active user rewards that can be claimed by an address's unspent outputs [aliases: rewards] + convert-address Converts an input address to a specified address format [aliases: convert] + convert-utxo-address Takes a UTXO address as input, and returns the equivalent address for another UTXO coin (e.g. from BTC address to RVN address) [aliases: convert-utxo] + get-public-key Returns the compressed secp256k1 pubkey corresponding to the user's seed phrase [aliases: get-public, public-key, public] + get-public-key-hash Returns the RIPEMD-160 hash version of your public key [aliases: pubkey-hash, hash, pubhash] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` + +### wallet my-balance (balace) + +The `my-balance` command gets balance of the given coin using the [`my_balance` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/my_balance.html). *Alias: `balance`*. + +```sh +komodefi-cli wallet balance --help +Get coin balance + +Usage: komodefi-cli wallet my-balance + +Arguments: + Coin to get balance + +Options: + -h, --help Print help +``` + +**Examples:** + +```sh +komodefi-cli wallet balance DOC +Getting balance, coin: DOC +coin: DOC +balance: 949832.3746621 +unspendable: 0 +address: RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM +``` + +### wallet withdraw + +The `withdraw` command return a signed transaction binary data that is supposed to be sent with `send-raw-transaction`. The withdrawal is done using the [`withdraw` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/withdraw.html). `--amount` and `--max` options can not be used together. `--fee` options provides setting up custom fee for utxo, eth, qrc and cosmos. `--derivation-path` (*currently not supported*) and (`--hd-account-id`, `--hd-is-change`, `--hd-address-index`) are parameters that allow withdrawing from the certain [bip32 compatible derived address ](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) if `enable_hd` option is enabled as a part of current `mm2` configuration. `--bare-output` option prevents an extra output to be able to call methods in sequentially. + +```sh +release/komodefi-cli wallet withdraw --help +Generates, signs, and returns a transaction that transfers the amount of coin to the address indicated in the to argument + +Usage: komodefi-cli wallet withdraw [OPTIONS] + +Arguments: + Coin the user desires to withdraw + Address the user desires to withdraw to + +Options: + -M, --max + Withdraw the maximum available amount + -a, --amount + Amount the user desires to withdraw + -f, --fee + Transaction fee [possible-values: ] + --derivation-path + Derivation path to determine the source of the derived value in more detail + --hd-account-id + Account index of the same crypto currency + --hd-is-change + Is change [possible values: true, false] + --hd-address-index + An incremental address index for the account + -b, --bare-output + Whether to output only tx_hex [aliases: bare] + -h, --help + Print help +``` + +**Example**: + +```sh +komodefi-cli wallet withdraw DOC RQvvz29iHpn4iuHeN7qFXnLbc1eh31nRKY --amount 1 +Getting withdraw tx_hex +coin: DOC +from: RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM +to: RQvvz29iHpn4iuHeN7qFXnLbc1eh31nRKY +total_amount: 949831.3746521 +spent_by_me: 949831.3746521 +received_by_me: 949830.3746421 +my_balance_change: -1.00001 +block_height: 0 +timestamp: 23-09-13 14:32:29 +fee_details: {"type":"Utxo","coin":"DOC","amount":"0.00001"} +internal_id: +transaction_type: "StandardTransfer" +tx_hash: 04bfc0470a07ab1390dffb98164543e368d100101020a4f9b20bf3f8193c2ea0 +tx_hex: 0400008085202f8901d4a7ffce25cdc0ca0cbfa23363e1159195a3fafd742bf8bfc6ba0f50a51b7160010000006a4730440220590868b953be9c655ebd97cf750fbd669171006848900c5ea9d0e151a1dfb28b02203b6ca6955d2f558c4b5fdbf5cc1a149cbe6a57685be34d9f7594b140ad +698765012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff0200e1f505000000001976a914abad17c49173537a07e39d2c94dcfe64645b1ad488ac922e35f6625600001976a9149934ebeaa56cb597c936a9ed8202d8d97a0a700388acfdc70165000000 +000000000000000000000000 +``` + +Bare output: + +```sh +RUST_LOG=error komodefi-cli wallet withdraw --amount 1 --fee utxo-fixed:0.01 DOC RQvvz29iHpn4iuHeN7qFXnLbc1eh31nRKY -b +0400008085202f8901d4a7ffce25cdc0ca0cbfa23363e1159195a3fafd742bf8bfc6ba0f50a51b7160010000006b483045022100a9bb524d413fea1a417a5d9ac1465c8995eadd642704c003ab5a2be22b533546022044d3f33582773254930f5054afa89d9ce085369a1b3c2c47302ab5dbf17cfa13 +012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff0200e1f505000000001976a914abad17c49173537a07e39d2c94dcfe64645b1ad488ac3af025f6625600001976a9149934ebeaa56cb597c936a9ed8202d8d97a0a700388ac7c850165000000000000 +000000000000000000 +``` + +### wallet send-raw-transaction (send-raw, send) + +The `send-raw-transaction` command sends a raw transaction to a given coin network using the [`send_raw_transaction` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/send_raw_transaction.html) The `--coin` and `--tx-hex` options specify the coin and transaction data, accordingly. The optional `--bare-output` (`bare`) parameter is intended to suppress additional output. *Aliases: `send-raw`, `send`* + +```sh +komodefi-cli wallet send -h +Broadcasts the transaction to the network of selected coin + +Usage: komodefi-cli wallet send-raw-transaction [OPTIONS] --coin --tx-hex + +Options: + -c, --coin Name of the coin network on which to broadcast the transaction + -t, --tx-hex Transaction bytes in hexadecimal format; + -b, --bare-output Whether to output only tx_hash [aliases: bare] + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli wallet send --coin DOC --tx-hex 0400008085202f8901d4a7ffce25cdc0ca0cbfa23363e1159195a3fafd742bf8bfc6ba0f50a51b7160010000006a47304402200ddbcda20d288d1c6075d01bb55158e18353f0e5 +e9789cddfb572ad0429e96f7022040d2c66dbab960c5746ceb16aec7d6f0f85364d939377cbfdc060ad8f24dd0be012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff0200e1f505000000001976a914abad17c49173537a07e39d2c94dcfe64645b1ad4 +88ac922e35f6625600001976a9149934ebeaa56cb597c936a9ed8202d8d97a0a700388ac3acb0165000000000000000000000000000000 +Sending raw transaction +tx_hash: 58aca0e311513974e467187026b873621c68a4ddedb16175c558d3886dbc3dc4 +``` + +Calling methods sequentially: + +```sh +komodefi-cli wallet send -b --coin DOC --tx-hex $(RUST_LOG=error komodefi-cli wallet withdraw DOC RQvvz29iHpn4iuHeN7qFXnLbc1eh31nRKY --amount 1 -b) +Sending raw transaction +26f4f904c60d5d0a410fa6e00fb540e19e983f52045b2d29f97d046c58e9ecd0 +``` + +This transaction could be found [in the DOC coin explorer](https://doc.explorer.dexstats.info/tx/26f4f904c60d5d0a410fa6e00fb540e19e983f52045b2d29f97d046c58e9ecd0) +### wallet get-raw-transaction (get-raw, raw-tx, get) + +The `get-raw-transaction` get raw transaction data from the given coin's network using the [`get_raw_transaction` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20/get_raw_transaction.html). *Aliases: `get-raw`, `raw-tx`, `get`* + +```sh +komodefi-cli wallet get -h +Returns the full signed raw transaction hex for any transaction that is confirmed or within the mempool + +Usage: komodefi-cli wallet get-raw-transaction [OPTIONS] --coin --tx-hash + +Options: + -c, --coin Coin the user desires to request for the transaction + -H, --tx-hash Hash of the transaction [aliases: hash] + -b, --bare-output Whether to output only tx_hex [aliases: bare] + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli wallet get-raw-transaction -c DOC -H 26f4f904c60d5d0a410fa6e00fb540e19e983f52045b2d29f97d046c58e9ecd0 -b +Getting raw transaction of coin: DOC, hash: 26f4f904c60d5d0a410fa6e00fb540e19e983f52045b2d29f97d046c58e9ecd0 +0400008085202f8901b3d8bfad51d9d44ba1d721180596cfe7e2f38969a582a3d358d8bc6cb80cb918010000006a47304402201fc52ae1078a0fd12b79b76170165919a8c36c6aae978e2ed8f204998f46095c02205e5b6dfe9c118b3acef2c28a7454e9d153fbdd89d0f6abc24fc2e2b966486fff01 +2102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff0200e1f505000000001976a914abad17c49173537a07e39d2c94dcfe64645b1ad488acc26449ea625600001976a9149934ebeaa56cb597c936a9ed8202d8d97a0a700388ac01d1016500000000000000 +0000000000000000 +``` + +### wallet tx-history (history) + +If the given coin was enabled with `--tx-history` it's getting possible to request history of transactions using the [`my_tx_history` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/my_tx_history.html). Requesting transaction set could be limited with `--limit` option or not if `--max` is taking place. The resulting transactions are organized into pages, there is the `--page-number` (`--page`) options that allows to select the certain one. If one of `--from-tx-hash` or `--from-tx-id` is taking place the resulting set will be restricted with the given hash or id accordingly. +*Alias: history* + +```sh +komodefi-cli wallet tx-history --help +Returns the blockchain transactions involving the Komodo DeFi Framework node's coin address + +Usage: komodefi-cli wallet tx-history [OPTIONS] <--limit |--max> + +Arguments: + The name of the coin for the history request + +Options: + -l, --limit Limits the number of returned transactions + -m, --max Whether to return all available records + -H, --from-tx-hash Skips records until it reaches this ID, skipping the from_id as well + -i, --from-tx-id For zcoin compatibility, skips records until it reaches this ID, skipping the from_id as well + -p, --page-number The name of the coin for the history request + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli wallet tx-history DOC --limit 5 --page 3 +Getting tx history, coin: DOC +limit: 5 +skipped: 10 +total: 19 +page_number: 3 +total_pages: 4 +current_block: 208242 +sync_status: NotEnabled +transactions: +│ time: 23-07-24 16:54:29 │ +│ coin: DOC │ +│ block: 132256 │ +│ confirmations: 75987 │ +│ transaction_type: StandardTransfer │ +│ total_amount: 949836.47 │ +│ spent_by_me: 949836.47 │ +│ received_by_me: 949835.47 │ +│ my_balance_change: -1.00 │ +│ fee_details: {"type":"Utxo","coin":"DOC","amount":"0.00001"} │ +│ tx_hash: 671aaee83b6d870f168c4e0be93e2d2087c8eae324e105c1dcad240cfea73c03 │ +│ from: RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM │ +│ to: RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM, RQvvz29iHpn4iuHeN7qFXnLbc1eh31nRKY │ +│ internal_id: 671aaee83b6d870f168c4e0be93e2d2087c8eae324e105c1dcad240cfea73c03 │ +│ tx_hex: 0400008085202f8901afdad3b683400c1ff4331514afdcbd1703647edcd1abaa3d93b1aecd6daaa3e0010000006a473044022022d692771b413f4b197c2dba61453b97a4df99 │ +│ 4986fafbd8e8950fcc77e1519d022012c990b48e11ee568907184236e1d5e2f03a5a18132fb322fe7522e33ddc6f39012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f7 │ +│ 93061fa5f38b2b4dffffffff0200e1f505000000001976a914abad17c49173537a07e39d2c94dcfe64645b1ad488ac54729d14635600001976a9149934ebeaa56cb597c936a9ed8202d8 │ +│ d97a0a700388acb0acbe64000000000000000000000000000000 │ +│ │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│ time: 23-07-24 16:44:09 │ +│ coin: DOC │ +... +``` + +### wallet show-private-key (private) + +The `show-private-key` command allows to get private key for the given coin that could be used to access account in a third-party wallet application. This command is proceed using the [`show_priv_key` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/show_priv_key.html). *Aliases: `private`, `private-key`* + +```sh +komodefi-cli wallet private --help +Returns the private key of the specified coin in a format compatible with coin wallets + +Usage: komodefi-cli wallet show-priv-key + +Arguments: + The name of the coin of the private key to show + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli wallet private-key DOC +Getting private key, coin: DOC +coin: DOC +priv_key: UufMCuXDsLNWga8kz7ZyJKXzH86MZ7vbEzUa21A7EEYqqbtHwYQ5 +``` + +### wallet validate-address (validate) + +The `validate-address` validated wether the given coin address could exist and valid using the [`validateaddress` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/validateaddress.html). *Alias: `validate`* + +```sh +release/komodefi-cli wallet validate-address --help +Checks if an input string is a valid address of the specified coin + +Usage: komodefi-cli wallet validate-address
+ +Arguments: + The coin to validate address for +
The input string to validate + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli wallet validate-address ZOMBIE zs1lgyuf2tudj7vgtx2pgtsqqdlcrva7a5rw4n4f7rswc6vnk8thc85dl4uvazpkv6y8yd25cjp72g +Validating address, coin: ZOMBIE, address: zs1lgyuf2tudj7vgtx2pgtsqqdlcrva7a5rw4n4f7rswc6vnk8thc85dl4uvazpkv6y8yd25cjp72g +valid: valid +``` + +### wallet kmd-rewards-info (rewards) + +The `kmd-rewards-info` returns information about the active user rewards that can be claimed by an address's unspent outputs using the [`kmd_rewards_info` RPC API methods](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/kmd_rewards_info.html). *Alias: `rewards`* + +```sh +komodefi-cli wallet rewards --help +Informs about the active user rewards that can be claimed by an address's unspent outputs + +Usage: komodefi-cli wallet kmd-rewards-info + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli wallet rewards +Getting kmd rewards info +rewards_info: +tx_hash: 498e23e1e0a3322192c4c4d7f531a562c12f44f86544013344397c3fc217963a +height: 3515481 +output_index: 1 +amount: 1599.00 +locktime: 23-07-23 14:30:46 +accrued_rewards: 23-07-23 14:30:46 +accrued_rewards: 0.01356243 +accrue_start_at: 23-07-23 15:30:46 +accrue_stop_at: 23-08-23 14:30:46 +``` + +### wallet convert-address (convert) + +The `convert-address` command converts an input address to a specified address format using the [`convert_address` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/convertaddress.html). For example, this method can be used to convert a BCH address from legacy to cash address format and vice versa. Or this can be used to convert an ETH address from single to mixed case checksum format. *Alias: `convert`* + +```sh +komodefi-cli wallet convert --help +Converts an input address to a specified address format + +Usage: komodefi-cli wallet convert-address [OPTIONS] --coin --from --format + +Options: + -c, --coin + The name of the coin address context + -f, --from + Input address + -F, --format + Address format to which the input address should be converted [possible values: mixed-case, cash-address, standard] + -C, --cash-address-network + Network prefix for cashaddress format [possible values: bitcoin-cash, bch-test, bch-reg] + -h, --help + Print help +``` + +**Examples:** + +Mixed case + +```sh +komodefi-cli wallet convert --coin ETH --from 0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359 --format mixed-case +Converting address +address: 0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359 +``` + +Standard + +```sh +komodefi-cli wallet convert --coin BCH --from bitcoincash:qzxqqt9lh4feptf0mplnk58gnajfepzwcq9f2rxk55 --format standard +Converting address +address: 1DmFp16U73RrVZtYUbo2Ectt8mAnYScpqM +``` + +Cash address + +```sh +komodefi-cli wallet convert --coin BCH --from 1DmFp16U73RrVZtYUbo2Ectt8mAnYScpqM --format cash-address -C bch-test +Converting address +address: bchtest:qzxqqt9lh4feptf0mplnk58gnajfepzwcqpmwyypng +``` + +### wallet convert-utxo-address (convert-utxo) + +The `convert-utxo` command is similar to convert-address but is aimed to convert UTXO addresses to each other + +```sh +komodefi-cli wallet convert-utxo-address
+ +Arguments: +
Input UTXO address + Input address to convert from + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli wallet convert-utxo RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM KMD +Convert utxo address +address: RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM +``` + +### wallet get-public-key (public) + +The `get-public-key` command returns the compressed secp256k1 pubkey corresponding to the configured seed phrase using the [`get_public_key` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20/get_public_key.html). *Alias: `public`, `get-public`, `public-key`* + +```sh +komodefi-cli wallet public --help +Returns the compressed secp256k1 pubkey corresponding to the user's seed phrase + +Usage: komodefi-cli wallet get-public-key + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli wallet public +Getting public key +public_key: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d +``` + +### wallet get-public-key-hash (hash) + + +The `get-public-key-hash` command returns [RIPEMD-160](https://en.bitcoin.it/wiki/RIPEMD-160)(https://en.bitcoin.it/wiki/RIPEMD-160) hash version of the configured seed phrase using the [` get_public_key_hash` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20/get_public_key_hash.html). *Aliases: `pubkey-hash`, `hash`, `pubhash`*. + +```sh +komodefi-cli wallet pubhash --help +Returns the RIPEMD-160 hash version of your public key + +Usage: komodefi-cli wallet get-public-key-hash + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli wallet pubhash +Getting public key hash +public_key_hash: 9934ebeaa56cb597c936a9ed8202d8d97a0a7003 +``` + +## DEX funtionality + +The next group of commands provides trading functionality, orders managing and trading commands are meant. General commands such as `sell`, `buy`, `set-price`, `update-maker-order` are left at the top level to make it easier to call them. + + +```sh +komodefi-cli orders --help +Order listing commands: book, history, depth etc. + +Usage: komodefi-cli order + +Commands: + orderbook Get orderbook [aliases: book] + orderbook-depth Get orderbook depth [aliases: depth] + order-status Return the data of the order with the selected uuid created by the current node [aliases: status] + best-orders Return the best priced trades available on the orderbook [aliases: best] + my-orders Get my orders [aliases: my, mine] + orders-history Return all orders whether active or inactive that match the selected filters [aliases: history, filter] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` +### orders orderbook (book) + +The `orderbook` command gets available bids and asks for the given base and rel coins using the [`orderbook` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/orderbook.html). You can specify which order should be shown and limit the output to the given ask and bids limits. *Alias: `book`* + +```sh +komodefi-cli orders orderbook DOC MARTY --help +Get orderbook + +Usage: komodefi-cli order orderbook [OPTIONS] + +Arguments: + Base currency of a pair + Related currency, also can be called "quote currency" according to exchange terms + +Options: + -u, --uuids Enable `uuid` column + -m, --min-volume Enable `min_volume` column [aliases: min] + -M, --max-volume Enable `max_volume` column [aliases: max] + -p, --publics Enable `public` column + -a, --address Enable `address` column + -A, --age Enable `age` column + -c, --conf-settings Enable order confirmation settings column + --asks-limit Orderbook asks count limitation [default: 20] + --bids-limit Orderbook bids count limitation [default: 20] + -h, --help Print help +``` + + +**Example:** + +```sh +komodefi-cli orders book DOC MARTY --uuids --asks-limit 5 --bids-limit 5 +Getting orderbook, base: DOC, rel: MARTY + Volume: DOC Price: MARTY Uuid +* 1.00 3.00000000 4d7c187b-d61f-4349-b608-a6abe0b3f0ea + 0.14 1.00000000 0e549623-fead-4645-9c6c-00877b50bac2 + 94974264.87 1.00000000 9406010e-54fd-404c-b252-1af473bf77e6 +- --------------- ---------------- ------------------------------------ + 94898462.11 1.00000000 5a14a592-feea-4eca-92d8-af79c0670a39 + 1.94 1.00000000 fbd51c38-f3a7-42c5-aa3a-c52938188086 +``` + +### sell + +The `sell` command sells a base coin for a rel one in a given volume and at a given price in rel using the [`sell` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/sell.html). That is possible to limit a new order with the `--min-volume`. If `--uuid` or `--public` options were set matching procedure would be restricted according to the given values. `--base-confs`, `--base-nota`, `--rel-confs`, `--rel-nota` are to override preset confirmation and notarization rules. + +```sh +komodefi-cli sell --help +Put a selling request + +Usage: komodefi-cli sell [OPTIONS] + +Arguments: + Base currency of a pair + Related currency of a pair + Amount of coins the user is willing to sell/buy of the base coin + Price in rel the user is willing to receive/pay per one unit of the base coin + +Options: + -t, --order-type The GoodTillCancelled order is automatically converted to a maker order if not matched in 30 seconds, and this maker order stays in the orderbook until explicitly cancelled. On the other hand, a FillOrKi +ll is cancelled if not matched within 30 seconds [default: good-till-cancelled] [aliases: type] [possible values: fill-or-kill, good-till-cancelled] + -m, --min-volume Amount of base coin that will be used as min_volume of GoodTillCancelled order after conversion to maker + -u, --uuid The created order is matched using a set of uuid + -p, --public The created order is matched using a set of publics to select specific nodes + --base-confs Number of required blockchain confirmations for base coin atomic swap transaction [aliases: bc] + --base-nota Whether dPoW notarization is required for base coin atomic swap transaction [aliases: bn] [possible values: true, false] + --rel-confs Number of required blockchain confirmations for rel coin atomic swap transaction [aliases: rc] + --rel-nota Whether dPoW notarization is required for rel coin atomic swap transaction [aliases: rn] [possible values: true, false] + -s, --save-in-history If true, each order's short record history is stored else the only order status will be temporarily stored while in progress [aliases: save] + -h, --help Print help +``` + +**Example**: + +```sh +komodefi-cli sell DOC MARTY 1 3 +Selling: 1 DOC for: 3 MARTY at the price of 3 MARTY per DOC +4d7c187b-d61f-4349-b608-a6abe0b3f0ea +``` +### set-price + +The `set-price` is almost the same as `sell` command, it's always considered as `sell`. When the `set-price` is called the maker order is created immidiatelly. It provides the `--cancel-prev` option alowing to cancel existing orders for the given pair. This command is implemented by requesting the [`setprice` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/setprice.html). + +```sh +komodefi-cli set-price --help +Place an order on the orderbook. The setprice order is always considered a sell + +Usage: komodefi-cli set-price [OPTIONS] <--max|--volume > + +Arguments: + The name of the coin the user desires to sell + The name of the coin the user desires to receive + The price in rel the user is willing to receive per one unit of the base coin + +Options: + -M, --max Use the entire coin balance for the order, taking 0.001 coins into reserve to account for fees + -v, --volume The maximum amount of base coin available for the order, ignored if max is true; the following values must be greater than or equal to the min_trading_vol of the corresponding coin + -m, --min-volume The minimum amount of base coin available for the order; it must be less or equal than volume param; the following values must be greater than or equal to the min_trading_vol of the corresponding coin + -c, --cancel-prev Cancel all existing orders for the selected pair [aliases: cancel] + --base-confs Number of required blockchain confirmations for base coin atomic swap transaction [aliases: bc] + --base-nota Whether dPoW notarization is required for base coin atomic swap transaction [aliases: bn] [possible values: true, false] + --rel-confs Number of required blockchain confirmations for rel coin atomic swap transaction [aliases: rc] + --rel-nota Whether dPoW notarization is required for rel coin atomic swap transaction [aliases: rn] [possible values: true, false] + -s, --save-in-history If true, each order's short record history is stored in a local SQLite database table, and when the order is cancelled or fully matched, it's history will be saved as a json file [aliases: save] + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli set-price DOC MARTY 3 --volume 1 -c +Setting price for pair: DOC MARTY +Maker order: + base: DOC + rel: MARTY + price: 3.00 + uuid: 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 + created at: 23-09-15 13:40:28 + updated at: 23-09-15 13:40:28 + max_base_vol: 1.00 + min_base_vol: 0.000100 + swaps: empty + conf_settings: 1,false:1,false + +komodefi-cli orders book DOC MARTY --uuids +Getting orderbook, base: DOC, rel: MARTY + Volume: DOC Price: MARTY Uuid +* 1.00 3.00000000 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 + 0.14 1.00000000 0e549623-fead-4645-9c6c-00877b50bac2 + 94974264.87 1.00000000 a29748d0-fa6a-4a7f-a402-c569c96ea92a +- -------------- ---------------- ------------------------------------ + 94898462.11 1.00000000 41b0a9cb-8416-4748-af4d-2064dd8ae617 + 1.94 1.00000000 fbd51c38-f3a7-42c5-aa3a-c52938188086 +... +``` + +### update-maker-order (update) + +The `update-maker-order` updates existing order selected by its uuid with the given values using the [`update_maker_order` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/update_maker_order.html). The order should be changed according to the given settings like it was done in the former commands. Volume is changed by the delta that is set with `--volume-delta` or is set as max. *Alias: `update`* + +```sh +komodefi-cli update-maker-order --uuid f398ffe2-9c74-4340-8702-68eca1d167e8 --help +Update order on the orderbook + +Usage: komodefi-cli update-maker-order [OPTIONS] --uuid + +Options: + -u, --uuid Uuid of the order the user desires to update + -p, --price Price in rel the user is willing to receive per one unit of the base coin + -M, --max-volume Whether to use the entire coin balance for the order, taking 0.001 coins into reserve to account for fees + -d, --volume-delta Volume added to or subtracted from the max_base_vol of the order to be updated, resulting in the new volume which is the maximum amount of base coin available for the order, ignored if max is true + -m, --min-volume Minimum amount of base coin available for the order; it must be less or equal than the new volume; the following values must be greater than or equal to the min_trading_vol of the corresponding coin + --base-confs Number of required blockchain confirmations for base coin atomic swap transaction [aliases: bc] + --base-nota Whether dPoW notarization is required for base coin atomic swap transaction [aliases: bn] [possible values: true, false] + --rel-confs Number of required blockchain confirmations for rel coin atomic swap transaction [aliases: rc] + --rel-nota Whether dPoW notarization is required for rel coin atomic swap transaction [aliases: rn] [possible values: true, false] + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli update-maker-order --uuid 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 -p 10 -d=10 --base-nota false --bc 2 --rn false --rc 2 -m 4 +Updating maker order +Maker order: + base: DOC + rel: MARTY + price: 10.00 + uuid: 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 + created at: 23-09-15 13:40:28 + updated at: 23-09-15 13:43:31 + max_base_vol: 21.00 + min_base_vol: 4.00 + swaps: empty + conf_settings: 2,false:2,false + +komodefi-cli orders book DOC MARTY --uuids -c +Getting orderbook, base: DOC, rel: MARTY + Volume: DOC Price: MARTY Uuid Order conf (bc,bn:rc,rn) +* 21.00 10.00000000 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 1,false:1,false + 0.14 1.00000000 0e549623-fead-4645-9c6c-00877b50bac2 1,false:1,false + 94974264.87 1.00000000 c4a469c7-fa69-4002-a1be-2578d329d681 1,false:1,false +- --------------- ---------------- ------------------------------------ ------------------------ + 94898462.11 1.00000000 9734d9e5-36f5-4f9f-81b9-deb79390f82b 1,false:1,false + 1.94 1.00000000 fbd51c38-f3a7-42c5-aa3a-c52938188086 1,false:1,false +... +``` + +*Notice: you can see the responded `conf-settings` for the given order 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 are wrong* +### buy + +The `buy` command buys a base coin for a rel one in a given volume and at a given price using the [`buy` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/buy.html). Like for the `sell` command that is possible to limit a new order with the `--min-volume`. If `--uuid` or `--public` options were set matching procedure would be restricted according to the given values. `--base-confs`, `--base-nota`, `--rel-confs`, `--rel-nota` are to override preset confirmation and notarization rules. + +```sh +komodefi-cli buy --help +Put a buying request + +Usage: komodefi-cli buy [OPTIONS] + +Arguments: + Base currency of a pair + Related currency of a pair + Amount of coins the user is willing to sell/buy of the base coin + Price in rel the user is willing to receive/pay per one unit of the base coin + +Options: + -t, --order-type The GoodTillCancelled order is automatically converted to a maker order if not matched in 30 seconds, and this maker order stays in the orderbook until explicitly cancelled. On the other hand, a FillOrKi +ll is cancelled if not matched within 30 seconds [default: good-till-cancelled] [aliases: type] [possible values: fill-or-kill, good-till-cancelled] + -m, --min-volume Amount of base coin that will be used as min_volume of GoodTillCancelled order after conversion to maker + -u, --uuid The created order is matched using a set of uuid + -p, --public The created order is matched using a set of publics to select specific nodes + --base-confs Number of required blockchain confirmations for base coin atomic swap transaction [aliases: bc] + --base-nota Whether dPoW notarization is required for base coin atomic swap transaction [aliases: bn] [possible values: true, false] + --rel-confs Number of required blockchain confirmations for rel coin atomic swap transaction [aliases: rc] + --rel-nota Whether dPoW notarization is required for rel coin atomic swap transaction [aliases: rn] [possible values: true, false] + -s, --save-in-history If true, each order's short record history is stored else the only order status will be temporarily stored while in progress [aliases: save] + -h, --help Print help +``` + +**Example** + +```sh +komodefi-cli buy DOC MARTY 1 0.1 +Buying: 1 DOC with: 0.1 MARTY at the price of 0.1 MARTY per DOC +2e791413-65ba-4a2a-a010-e1ef521f1f73 + +komodefi-cli orders book DOC MARTY --uuids --asks-limit 5 --bids-limit 5 +Getting orderbook, base: DOC, rel: MARTY + Volume: DOC Price: MARTY Uuid +... +- --------------- ---------------- ------------------------------------ +... +* 1.00 0.10000000 2e791413-65ba-4a2a-a010-e1ef521f1f73 +``` + +### order order-status (status) + +The `order-status` command gets the status and details of an order by the given uuid using the [`order_status` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/order_status.html). *Alias: `status`* + +```sh +komodefi-cli order status --help +Return the data of the order with the selected uuid created by the current node + +Usage: komodefi-cli order order-status + +Arguments: + + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli order status 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 +Getting order status: 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 + base: DOC + rel: MARTY + price: 10.00 + uuid: 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 + created at: 23-09-15 13:40:28 + updated at: 23-09-15 13:43:31 + max_base_vol: 21.00 + min_base_vol: 4.00 + swaps: empty + conf_settings: 2,false:2,false + cancellable: true + available_amount: 21.00 +``` + +### orders orderbook-depth (depth) + +The `orderbook-depth` command gets a common orderbooks' view on the given set of pairs using the [`orderbook_depth` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/orderbook_depth.html). Each resulting value is a count of bids and asks in the related orderbook. *Alias: `depth`* + +```sh +komodefi-cli orders depth --help +Get orderbook depth + +Usage: komodefi-cli order orderbook-depth ... + +Arguments: + ... + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli orders depth DOC/MARTY BTC/KMD ETH/BTC +Getting orderbook depth for pairs: DOC/MARTY, BTC/KMD, ETH/BTC + Bids Asks +DOC/MARTY: 3 3 + BTC/KMD: 8 5 + ETH/BTC: 2 0 +``` +### orders best-orders (best) + +The `best-orders` command scans all order books related to a given coin and lists the more profitable orders available in them that can meet a given `--volume` or limits them with a given `--number`. This command is implemented by requesting the [`best_orders` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/best_orders.html). + +```sh +komodefi-cli orders best-orders --help +Return the best priced trades available on the orderbook + +Usage: komodefi-cli order best-orders [OPTIONS] <--volume |--number > + +Arguments: + The coin to get best orders + Whether to buy or sell the selected coin [possible values: buy, sell] + +Options: + -v, --volume The returned results will show the best prices for trades that can fill the requested volume + -n, --number The returned results will show a list of the best prices + -o, --show-orig-tickets Whether to show the original tickers if they are configured for the queried coin [aliases: show-origin, original-tickers, origin] + -e, --exclude-mine Exclude orders that is mine + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli orders best-orders --volume 1 BTC sell +Getting best orders: Sell BTC +│ │ Price │ Uuid │ Base vol(min:max) │ Rel vol(min:max) │ Address │ Confirmation │ +│ ARRR │ +│ │ 129058.90 │ 2157dcfe-a352-4f72-b4b2-327b3021a013 │ 0.0080:0.0085 │ 1034.68:1104.35 │ Shielded │ 1,false:2,true │ +│ │ 144073.53 │ a56072bf-8a69-423b-a351-ed582cd43209 │ 0.0081:0.14 │ 1175.57:20787.01 │ Shielded │ 1,false:2,true │ +│ BAND-BEP20 │ +│ │ 25589.58 │ e88c6f78-46ea-462e-ad0d-ebd9b49323e7 │ 0.0081:0.01 │ 208.53:493.22 │ 0xadb681c3a1ec9bbc4105b8e8eb5fc7178125b450 │ 1,false:3,false │ +│ BCH │ +│ │ 118.06 │ 1dda5e08-e73a-4f8a-a37e-6201e84dbf74 │ 0.0081:0.0099 │ 0.96:1.17 │ bitcoincash:qp42lwm4xvgg2sjhy7nc49qwjv60dqdnu5u2h2zaya │ 1,false:1,false │ +... +``` + +### orders my-orders (my, mine) + +The `my-orders` command requests information about orders created by the current node (running `mm2` instance) using the [`my_orders` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/my_orders.html). *Aliases: `my`, `mine`. + +**Example:** + +```sh +komodefi-cli orders mine +Getting my orders + Taker orders: empty + + Maker orders: +│ base,rel │ price │ uuid │ created at, │ min base vol, │ cancellable │ available │ swaps │ conf_settings │ history changes │ +│ │ │ │ updated at │ max base vol │ │ amount │ │ │ │ +│ RICK,MORTY │ 2.00 │ 1fc0df20-9c21-461a-ad78-a4b37d4ab336 │ 23-05-08 12:07:18, │ 0.000100, │ true │ 0.50 │ empty │ 555,true:111,true │ none │ +│ │ │ │ 23-05-08 12:07:18 │ 0.50 │ │ │ │ │ │ +│ DOC,KMD │ 100.00 │ 009b9b1c-4582-4ec7-aef9-2d6729e8cde2 │ 23-09-15 15:35:48, │ 0.000100, │ true │ 1.00 │ empty │ 1,false:2,true │ none │ +│ │ │ │ 23-09-15 15:35:48 │ 1.00 │ │ │ │ │ │ +``` + +### orders orders-history (history) + +The `orders-history` command requests the hitstory of orders created by the current node (running `mm2` instance) using the [` orders_history_by_filter` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/orders_history_by_filter.html). Resulting orders are queried by the given filter. + +```sh +komodefi-cli orders history --help +Return all orders whether active or inactive that match the selected filters + +Usage: komodefi-cli order orders-history [OPTIONS] <--takers|--makers|--warnings|--all> + +Options: + -t, --takers Whether to show taker orders detailed history + -m, --makers Whether to show maker orders detailed history + -w, --warnings Whether to show warnings + -a, --all Whether to show common history data + --type Return only orders that match the type [possible values: taker, maker] + --action Return only orders that match the initial action. Note that maker order initial_action is considered "Sell" [possible values: sell, buy] + --base Return only orders that match the order.base + --rel Return only orders that match the order.rel + --from-price Return only orders whose price is more or equal the from_price + --to-price Return only orders whose price is less or equal the to_price + --from-volume Return only orders whose volume is more or equal the from_volume + --to-volume Return only orders whose volume is less or equal the to_volume + --from-dt Return only orders that match the order.created_at >= from_dt. Datetime fmt: "%y-%m-%dT%H:%M:%S" + --to-dt Return only orders that match the order.created_at <= to_dt. Datetime fmt: "%y-%m-%dT%H:%M:%S" + --was-taker Return only GoodTillCancelled orders that got converted from taker to maker + --status Return only orders that match the status [possible values: created, updated, fulfilled, insuficcient-balance, cancelled, timed-out] + -h, --help Print help +``` + +**Example:** + +Requesting all DOC based orders + +```sh +komodefi-cli orders history --base DOC --all +Getting order history +Orders history: +│uuid │Type │Action│Base│Rel │Volume│Price │Status │Created │Updated │Was taker│ +│ │ │ │ │ │ │ │ │ │ │ │ +│22532835-8d93-4484-bb6b-e01be0acbde0│Maker│Sell │DOC │MARTY│1.00 │3.00 │Cancelled│23-09-15 15:42:06│23-09-15 15:42:31│false │ +│009b9b1c-4582-4ec7-aef9-2d6729e8cde2│Maker│Sell │DOC │KMD │1.00 │100.00│Created │23-09-15 15:35:48│23-09-15 15:35:48│false │ +│72ad0098-a684-4ac1-925b-9b7155faa22a│Maker│Sell │DOC │MARTY│1.00 │3.00 │Cancelled│23-09-15 15:34:29│23-09-15 15:36:30│false │ +│077b68d9-0e71-4a35-9b3f-c3cfe5b57310│Maker│Sell │DOC │MARTY│21.00 │10.00 │Cancelled│23-09-15 13:40:28│23-09-15 15:18:18│false │ +│0fc8996a-caec-4323-8218-93882c317f88│Maker│Sell │DOC │MARTY│1.00 │3.00 │Cancelled│23-09-15 13:39:38│23-09-15 13:40:28│false │ +│26a51d94-6957-4c76-b6e6-d31f3cf5e4a6│Maker│Sell │DOC │MARTY│1.00 │0.90 │Cancelled│23-09-15 08:57:45│23-09-15 13:40:28│false │ +│f398ffe2-9c74-4340-8702-68eca1d167e8│Maker│Sell │DOC │MARTY│9.00 │10.00 │Cancelled│23-09-14 13:59:27│23-09-15 08:57:45│false │ +│96b193bf-ac34-48f4-8dc8-c59c82149753│Maker│Sell │DOC │MARTY│1.00 │3.00 │Cancelled│23-09-14 13:45:17│23-09-14 13:59:27│false │ +``` + +Requesting detailed makers + +```sh +komodefi-cli orders history --base RICK --makers +Getting order history +Maker orders history detailed: +│ base,rel │ price │ uuid │ created at, │ min base vol, │ swaps │ conf_settings │ history changes │ orderbook ticker │ │ +│ │ │ │ updated at │ max base vol │ │ │ │ base, rel │ │ +│ │ │ │ │ │ │ │ │ │ │ +│ RICK,MORTY │ 0.50 │ e2db79f4-376e-4917-be37-f383c5516e28 │ 23-07-18 16:50:31, │ 0.000100, │ empty │ 10,true:10,true │ none │ none │ │ +│ │ │ │ 23-07-18 16:58:10 │ 0.10 │ │ │ │ none │ │ +│ RICK,MORTY │ 0.90 │ 1c739304-dd83-466e-8df4-ef99dc40afb9 │ 23-07-18 09:11:48, │ 0.00011, │ empty │ 10,true:10,true │ none │ none │ │ +│ │ │ │ 23-07-18 09:12:50 │ 0.10 │ │ │ │ none │ │ +│ RICK,MORTY │ 0.95 │ f1bf7c76-806e-40cb-a489-a52056ec42e6 │ 23-06-30 09:05:34, │ 0.00010, │ fe6099a2-e29a-441b-be4f-21dd3666efad, │ 1,false:1,false │ none │ none │ │ +│ │ │ │ 23-06-30 09:05:34 │ 1.00 │ e07bcf02-788f-407c-a182-63c506280ca4, │ │ │ none │ │ +│ │ │ │ │ │ dec7fe39-16be-42cc-b3ba-2078ed5019b5, │ │ │ │ │ +│ │ │ │ │ │ c63bc058-72e7-4b1b-8866-e1e5d2a2253b, │ │ │ │ │ +│ │ │ │ │ │ 824fa32e-865f-49f1-b499-ba90b7141c2b │ │ │ │ │ +│ matches │ +│ uuid: dec7fe39-16be-42cc-b3ba-2078ed5019b5 │ +│ req.uuid: dec7fe39-16be-42cc-b3ba-2078ed5019b5 │ +│ req.(base,rel): MORTY(0.0100), RICK(0.01) │ +│ req.match_by: Any │ +│ req.action: Sell │ +│ req.conf_settings: 1,false:1,false │ +│ req.(sender, dest): 144ee16a5960c50a930c26c0e01133de603eb41ce2e2e61e744fcfa76d4ffade,0000000000000000000000000000000000000000000000000000000000000000 │ +│ reserved.(base,rel): RICK(0.01), MORTY(0.0100) │ +│ reserved.(taker, maker): dec7fe39-16be-42cc-b3ba-2078ed5019b5,f1bf7c76-806e-40cb-a489-a52056ec42e6 │ +│ reserved.(sender, dest): 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d,144ee16a5960c50a930c26c0e01133de603eb41ce2e2e61e744fcfa76d4ffade │ +│ reserved.conf_settings: 1,false:1,false │ +│ connected.(taker,maker): dec7fe39-16be-42cc-b3ba-2078ed5019b5,f1bf7c76-806e-40cb-a489-a52056ec42e6 │ +│ connected.(sender, dest): 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d,0000000000000000000000000000000000000000000000000000000000000000 │ +│ connect.(taker,maker): dec7fe39-16be-42cc-b3ba-2078ed5019b5,f1bf7c76-806e-40cb-a489-a52056ec42e6 │ +│ connect.(sender, dest): 0000000000000000000000000000000000000000000000000000000000000000,0000000000000000000000000000000000000000000000000000000000000000 │ +│ last_updated: 23-06-30 09:54:13 │ +│ │ +... +``` + +## Swaps + +Once an order is created and matched with another order, it is no longer part of the order book, the swap is created and execution begins + +```sh +komodefi-cli swaps +Swap related commands + +Usage: komodefi-cli swaps + +Commands: + active-swaps, -a Get all the swaps that are currently running [aliases: active] + my-swap-status, -s Return the data of an atomic swap [aliases: status] + my-recent-swaps, -r Return the data of the most recent atomic swaps by filter [aliases: recent] + recover-funds-of-swap, -R Reclaim the user funds from the swap-payment address, if possible [aliases: recover, recover-funds, refund] + min-trading-vol Return the minimum required volume for buy/sell/setprice methods for the selected coin + max-taker-vol Return the maximum available volume for buy/sell methods for selected coin. The result should be used as is for sell method or divided by price for buy method. + trade-preimage Return the approximate fee amounts that are paid per the whole swap [aliases: preimage] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` +### swaps active-swaps (active) + +The `active-swaps` command requests a list of swaps using the [`active_swaps` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/active_swaps.html). The output includes information about completed and active stages, hashes, and related transaction data. If detailed information was not requested using the `--include-status` option, only the uuids of active swaps would be listed. + +``` +komodefi-cli swap active --help +Get all the swaps that are currently running + +Usage: komodefi-cli swaps {active-swaps|-a} [OPTIONS] + +Options: + -s, --include-status Whether to include swap statuses in response; defaults to false + -h, --help Print help +``` + +**Example:** +Getting only uuids: + +```sh +komodefi-cli swap active +Getting active swaps +uuids: +64fced3d-a0c2-423e-9fa5-f6c470448983 +``` + +Getting detailed information +```sh +komodefi-cli swaps active -s +Getting active swaps + +TakerSwap: 64fced3d-a0c2-423e-9fa5-f6c470448983 +my_order_uuid: 64fced3d-a0c2-423e-9fa5-f6c470448983 +gui: komodefi-cli +mm_version: 1.0.7-beta_a611ca37f +taker_coin: KMD +taker_amount: 1.00 +taker_coin_usd_price: 0.22 +maker_coin: BCH +maker_amount: 0.0010 +maker_coin_usd_price: 213.30 +events: +│ Started │ uuid: 64fced3d-a0c2-423e-9fa5-f6c470448983 │ +│ 23-09-15 12:20:00 │ started_at: 23-09-15 12:20:00 │ +│ │ taker_coin: KMD │ +│ │ maker_coin: BCH │ +│ │ maker: 15d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732 │ +│ │ my_persistent_pub: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ +│ │ lock_duration: 31200 │ +│ │ maker_amount: 0.0010 │ +│ │ taker_amount: 1.00 │ +│ │ maker_payment_confirmations: 3 │ +│ │ maker_payment_requires_nota: false │ +│ │ taker_payment_confirmations: 2 │ +│ │ taker_payment_requires_nota: false │ +│ │ tacker_payment_lock: 23-09-15 21:00:00 │ +│ │ maker_payment_wait: 23-09-15 15:48:00 │ +│ │ maker_coin_start_block: 810794 │ +│ │ taker_coin_start_block: 3590943 │ +│ │ fee_to_send_taker_fee: coin: KMD, amount: 0.00001, paid_from_trading_vol: false │ +│ │ taker_payment_trade_fee: coin: KMD, amount: 0.00001, paid_from_trading_vol: false │ +│ │ maker_payment_spend_trade_fee: coin: BCH, amount: 0.00001, paid_from_trading_vol: true │ +│ │ maker_coin_htlc_pubkey: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ +│ │ taker_coin_htlc_pubkey: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ +│ │ │ +│ Negotiated │ maker_payment_locktime: 23-09-16 05:39:59 │ +│ 23-09-15 12:20:15 │ maker_pubkey: 000000000000000000000000000000000000000000000000000000000000000000 │ +│ │ secret_hash: 2846d8eb4f442286158888a2231e577f0373b750 │ +│ │ maker_coin_htlc_pubkey: 0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732 │ +│ │ taker_coin_htlc_pubkey: 0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732 │ +│ │ │ +│ TakerFeeSent │ tx_hex: 0400008085202f89013a9617c23f7c394433014465f8442fc162a531f5d7c4c4922132a3e0e1238e49010000006a4730440220552879ed │ +│ 23-09-15 12:20:15 │ 2fae025920c0d3993a1e48955f5de1a999dcb43141ec569fca3ecaf602205f176776affed034b88bd3807300ff90d09e7d05edcb9a4b2189238dab │ +│ │ e0f436012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff0276c40100000000001976a914ca1e0474 │ +│ │ 5e8ca0c60d8c5881531d51bec470743f88ac7082e33a250000001976a9149934ebeaa56cb597c936a9ed8202d8d97a0a700388ac744a0465000000 │ +│ │ 000000000000000000000000 │ +│ │ tx_hash: 2d2b4745ed039a897bf78f45d83c939b508ff8f0436281d202eb1ccf402e2e37 │ +│ │ │ +│ TakerPaymentInstructionsReceived │ none │ +│ 23-09-15 12:20:16 │ │ +│ │ │ +│ MakerPaymentReceived │ tx_hex: 0100000001e91a267e855f865dbf741a34cc4509b233c62b9ec10d53afdea29238b9412812000000006a47304402202f82ea70d750dcc7 │ +│ 23-09-15 12:20:16 │ eb003699fc052fcef4b216ef7904a5aedf90d88f1d7b0e6e02202e3b711fd950c48f5e6f8380aa0b47dd542ddd6970d997bbc21e510b2cf6d49941 │ +│ │ 210315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732ffffffff030e9201000000000017a9146a16cb1d0da650d083 │ +│ │ cf17d34fe2c01e2f4ede18870000000000000000166a142846d8eb4f442286158888a2231e577f0373b750c3530100000000001976a9141462c3dd │ +│ │ 3f936d595c9af55978003b27c250441f88ac004c0465 │ +│ │ tx_hash: 4cc4a5c89df668ac4aca619f10de74863b89374b57b18586ae113e7fe8c63e4a │ +│ │ │ +│ MakerPaymentWaitConfirmStarted │ │ +│ 23-09-15 12:20:16 │ │ +│ │ │ +│ MakerPaymentValidatedAndConfirmed │ │ +│ 23-09-15 12:59:34 │ │ +│ │ │ +│ TakerPaymentSent │ tx_hex: 0400008085202f8901372e2e40cf1ceb02d2816243f0f88f509b933cd8458ff77b899a03ed45472b2d010000006b483045022100afc98d │ +│ 23-09-15 12:59:34 │ 939ce43081eda416e8b5e63e2d36de3314f715e4555bf36c61d8654d9302207f8c33ef39be5cf93ffcb40e7ec64691f8205b5bf2b9caa1cf454051 │ +│ │ f69b89ad012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff0300e1f5050000000017a9145a2ba5f8 │ +│ │ dd563ab9dcbb74a5fe6b5a683af94520870000000000000000166a142846d8eb4f442286158888a2231e577f0373b750889ded34250000001976a9 │ +│ │ 149934ebeaa56cb597c936a9ed8202d8d97a0a700388ac384d0465000000000000000000000000000000 │ +│ │ tx_hash: 90d73c9311ff72ca4bc79b844e3db6ec3a13c37c4996e399361b9711dad81e0a │ +│ │ │ +│ TakerPaymentSpent │ tx_hex: 0400008085202f89010a1ed8da11971b3699e396497cc3133aecb63d4e849bc74bca72ff11933cd79000000000d7473044022041e02ad7 │ +│ 23-09-15 13:02:16 │ b050ef5a3fa90f7811b810c5c15b9fa52218768bed486131f44d0c450220037b2d5883dd3d56d4fa690a733aa1906e9156f926c5b6f6f9fda745be │ +│ │ 9ec4bb0120f849998cb9a3211fca8d6ba961b9aac27b4c1385597a62727e230ccfb086bae7004c6b6304d0c50465b1752102264fcd9401d797c50f │ +│ │ e2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dac6782012088a9142846d8eb4f442286158888a2231e577f0373b75088210315d9c51c65 │ +│ │ 7ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732ac68ffffffff0118ddf505000000001976a9141462c3dd3f936d595c9af55978 │ +│ │ 003b27c250441f88acd0c50465000000000000000000000000000000 │ +│ │ tx_hash: 8c3db71bafdc3bc251b3b735e7b66c4eeec6e7081d751d7e1a35fe410d532f1a │ +│ │ secret: f849998cb9a3211fca8d6ba961b9aac27b4c1385597a62727e230ccfb086bae7 │ +``` + +### swaps my-swap-status (status) + +The `my-swap-status` command requests the detailed information about completed and active stages, hashes and related transaction data using the [`my_swap_status` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/my_swap_status.html). The details provided by `my-swap-status` have the same format as the details provided by `active-swaps` command. *Alias: `status`* + +**Example:** + +```sh +komodefi-cli swap status 64fced3d-a0c2-423e-9fa5-f6c470448983 +Getting swap status: 64fced3d-a0c2-423e-9fa5-f6c470448983 +my_coin: KMD +other_coin: BCH +my_amount: 1.00 +other_amount: 0.0010 +started_at: 23-09-15 12:20:00 +recoverable: false +TakerSwap: 64fced3d-a0c2-423e-9fa5-f6c470448983 +my_order_uuid: 64fced3d-a0c2-423e-9fa5-f6c470448983 +gui: komodefi-cli +mm_version: 1.0.7-beta_a611ca37f +taker_coin: KMD +taker_amount: 1.00 +taker_coin_usd_price: 0.22 +maker_coin: BCH +maker_amount: 0.0010 +maker_coin_usd_price: 213.30 +events: +│ Started │ uuid: 64fced3d-a0c2-423e-9fa5-f6c470448983 │ +│ 23-09-15 12:20:00 │ started_at: 23-09-15 12:20:00 │ +│ │ taker_coin: KMD │ +│ │ maker_coin: BCH │ +│ │ maker: 15d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732 │ +│ │ my_persistent_pub: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ +│ │ lock_duration: 31200 │ +│ │ maker_amount: 0.0010 │ +│ │ taker_amount: 1.00 │ +│ │ maker_payment_confirmations: 3 │ +│ │ maker_payment_requires_nota: false │ +│ │ taker_payment_confirmations: 2 │ +│ │ taker_payment_requires_nota: false │ +│ │ tacker_payment_lock: 23-09-15 21:00:00 │ +│ │ maker_payment_wait: 23-09-15 15:48:00 │ +│ │ maker_coin_start_block: 810794 │ +│ │ taker_coin_start_block: 3590943 │ +│ │ fee_to_send_taker_fee: coin: KMD, amount: 0.00001, paid_from_trading_vol: false │ +│ │ taker_payment_trade_fee: coin: KMD, amount: 0.00001, paid_from_trading_vol: false │ +│ │ maker_payment_spend_trade_fee: coin: BCH, amount: 0.00001, paid_from_trading_vol: true │ +│ │ maker_coin_htlc_pubkey: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ +│ │ taker_coin_htlc_pubkey: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ +│ │ │ +... +``` + +### swaps my-recent-swaps (recent) + +The `my-recent-swaps` command requests the swap history selecte by the query of the following options: `--from-uuid`, `--my-coin`, `--other-coin`, `--from-timestamp`, `--to-timestamp`. The requesting swaps are organized into pages, there is the `--page-number` (`--page`) options that allows to select the certain one. + +```sh +komodefi-cli swaps recent --help +Return the data of the most recent atomic swaps by filter + +Usage: komodefi-cli swaps {my-recent-swaps|-r} [OPTIONS] + +Options: + -l, --limit + Limits the number of returned swaps [default: 10] + -u, --from-uuid + Skip records until this uuid, skipping the from_uuid as well + -p, --page-number + Return limit swaps from the selected page; This param will be ignored if from_uuid is set + -m, --my-coin + Return only swaps that match the swap.my_coin = request.my_coin condition [aliases: mine] + -o, --other-coin + Return only swaps that match the swap.other_coin = request.other_coin condition [aliases: other] + -t, --from-timestamp + Return only swaps that match the swap.started_at >= request.from_timestamp condition. Datetime fmt: "%y-%m-%dT%H:%M:%S" + -T, --to-timestamp + Return only swaps that match the swap.started_at < request.to_timestamp condition. Datetime fmt: "%y-%m-%dT%H:%M:%S" + -h, --help + Print help +``` + +**Example:** + +Requesting forth page of swaps happened before 2023/07/02 12:21:01 by 2 swaps per page + +```sh +komodefi-cli swaps recent --to-timestamp 23-07-01T12:21:01 --limit 2 --page 4 +Getting recent swaps +skipped: 6 +limit: 2 +total: 104 +page_number: 4 +total_pages: 52 +found_records: 2 + +TakerSwap: f3237905-43e5-42a7-8285-98e0c122b625 +my_order_uuid: f3237905-43e5-42a7-8285-98e0c122b625 +... +``` + +### swap recover-funds-of-swap (refund) + +The `recover-funds-of-swap` requests the refund of funds that stuck on the blockchain due to the interrupted swap using the [` recover_funds_of_swap` RPC API method]. The given swap should be recoverable. *Aliases: `recover`, `recover-funds`, `refund`* + +```sh +komodefi-cli swaps refund --help +Reclaim the user funds from the swap-payment address, if possible + +Usage: komodefi-cli swaps {recover-funds-of-swap|-R} + +Arguments: + Uuid of the swap to recover the funds + +Options: + -h, --help Print help +``` + +### swaps min-trading-vol + +The `min-trading-vol` command provides the minimum required volume to trade the given coin using the [`min_trading_vol` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/min_trading_vol.html). This command allows the user to have a picture of what the trading volume might be for a given coin. + +```sh +komodefi-cli swaps min-trading-vol --help +Return the minimum required volume for buy/sell/setprice methods for the selected coin + +Usage: komodefi-cli swaps min-trading-vol + +Arguments: + + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli swaps min-trading-vol BTC +Getting min trading vol: BTC +coin: BTC +volume: 0.0077 +``` + +### swaps max-taker-vol + +The `max-taker-vol` command provides the maximum volume of the given coin possible to trade. This command is implemented by requesting the [`max_taker_vol` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/max_taker_vol.html). + +```sh +komodefi-cli swaps max-taker-vol --help +Return the maximum available volume for buy/sell methods for selected coin. The result should be used as is for sell method or divided by price for buy method. + +Usage: komodefi-cli swaps max-taker-vol + +Arguments: + + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli swaps max-taker-vol KMD +Getting max taker vol, KMD +coin: KMD +result: 1596.16 +``` + +```sh +komodefi-cli swaps max-taker-vol BTC +Getting max taker vol, BTC +coin: BTC +result: 0 +``` + +### swaps trade-preimage (preimage) + +The `trade-preimage` command provides a way to estimate the fee for trading a given pair of coins at a given price and volume that could presumably take place. This command is implemented by requesting the [`trade_preimage` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/trade_preimage.html). *Alias: `preimage`* + +```sh +komodefi-cli swap trade-preimage --help +Return the approximate fee amounts that are paid per the whole swap + +Usage: komodefi-cli swaps trade-preimage <--volume |--max> + +Arguments: + Base currency of the request + Rel currency of the request + Price in rel the user is willing to pay per one unit of the base coin [possible values: set-price, buy, sell] + Price in rel the user is willing to pay per one unit of the base coin + +Options: + -v, --volume Amount the user is willing to trade; ignored if max = true and swap_method = setprice, otherwise, it must be set + -m, --max Whether to return the maximum available volume for setprice method; must not be set or false if swap_method is buy or sell + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli swap trade-preimage --volume 1590 KMD BTC sell 0.00000859 +Getting trade preimage +base_coin_fee: coin: KMD, amount: 0.00001, paid_from_trading_vol: false +rel_coin_fee: coin: BTC, amount: 0.000050, paid_from_trading_vol: true +total_fee: +│ coin │ amount │ required_balance │ +│ BTC │ 0.000050 │ 0 │ +│ KMD │ 1.84 │ 1.84 │ +``` + +## Cancelling orders + +Each of the following commands is designed to cancel one or more orders + +```sh +komodefi-cli cancel +Cancel one or many orders + +Usage: komodefi-cli cancel + +Commands: + order, -o Cancels certain order by uuid + all, -a Cancels all orders of current node + by-pair, -p Cancels all orders of specific pair [aliases: pair] + by-coin, -c Cancels all orders using the coin ticker as base or rel [aliases: coin] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` + +### cancel order + +The `cancel order` command cancels the given order using the [`cancel_order` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/cancel_order.html). + +```sh +komodefi-cli cancel order --help +Cancels certain order by uuid + +Usage: komodefi-cli cancel {order|-o} + +Arguments: + Order identifier + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli orders book DOC MARTY --uuids +Getting orderbook, base: DOC, rel: MARTY + Volume: DOC Price: MARTY Uuid +* 21.00 10.00000000 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 + 0.14 1.00000000 0e549623-fead-4645-9c6c-00877b50bac2 + 94974264.87 1.00000000 6e0b356e-abbf-46e3-8a0c-4a19e6a88199 +- -------------- ---------------- ------------------------------------ + 94898462.11 1.00000000 390c85a5-7709-4c1f-a1bc-690412832bf6 + 1.94 1.00000000 fbd51c38-f3a7-42c5-aa3a-c52938188086 +* 3.00 0.33333333 797c0456-7d99-4295-8ee6-055e784b04cf + +komodefi-cli cancel order 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 +Cancelling order: 077b68d9-0e71-4a35-9b3f-c3cfe5b57310 +Order cancelled: Success +``` + +### cancel all + +The `cancel all` command cancels all order created by the running `mm2` instance orders the [`cancel_all` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/cancel_all_orders.html). + +```sh +komodefi-cli cancel all --help +Cancels all orders of current node + +Usage: komodefi-cli cancel {all|-a} + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli cancel all +Cancelling all orders +Cancelled: d550c9d5-eedb-4069-ba0a-524d9346acda, 797c0456-7d99-4295-8ee6-055e784b04cf, 1fc0df20-9c21-461a-ad78-a4b37d4ab336 +``` + +### cancel by-pair (pair) + +The `cancel by-pair` command cancels all orders that matches by the given BASE and REL using the [`cancel_all` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/cancel_all_orders.html). Sell operation is meant. *Alias: `pair`* + +```sh +komodefi-cli cancel pair --help +Cancels all orders of specific pair + +Usage: komodefi-cli cancel {by-pair|-p} + +Arguments: + Base coin of the pair + Rel coin of the pair + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli orders book DOC MARTY --uuids +Getting orderbook, base: DOC, rel: MARTY + Volume: DOC Price: MARTY Uuid +* 1.00 3.00000000 72ad0098-a684-4ac1-925b-9b7155faa22a + 0.14 1.00000000 0e549623-fead-4645-9c6c-00877b50bac2 + 94974264.87 1.00000000 36c3456d-7f69-4818-940a-72d5465217bd +- -------------- ---------------- ------------------------------------ + 94898462.11 1.00000000 205fb457-f693-421b-ae16-48f63b996ad5 + 1.94 1.00000000 fbd51c38-f3a7-42c5-aa3a-c52938188086 +* 3.00 0.33333333 2018ece4-0210-4f07-8cff-eed811f17ded + +komodefi-cli cancel pair DOC MARTY +Cancelling by pair, base: DOC, rel: MARTY +Cancelled: 72ad0098-a684-4ac1-925b-9b7155faa22a +``` + +### cancel by-coin (coin) + +The `cancel by-coin` command cancels all orders corresponding to a given COIN in which this coin is set as a base or relative coin. This command is implemented by requesting the [`cancel_all` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/cancel_all_orders.html) *Alias: `coin`* + +```sh +komodefi-cli cancel coin --help +Cancels all orders using the coin ticker as base or rel + +Usage: komodefi-cli cancel {by-coin|-c} + +Arguments: + Order is cancelled if it uses ticker as base or rel + +Options: + -h, --help Print help +``` + + +**Example:** + +```sh +komodefi-cli orders book DOC MARTY --uuids +Getting orderbook, base: DOC, rel: MARTY + Volume: DOC Price: MARTY Uuid +* 1.00 3.00000000 22532835-8d93-4484-bb6b-e01be0acbde0 + 0.14 1.00000000 0e549623-fead-4645-9c6c-00877b50bac2 + 94974264.87 1.00000000 36c3456d-7f69-4818-940a-72d5465217bd +- -------------- ---------------- ------------------------------------ + 94898462.11 1.00000000 205fb457-f693-421b-ae16-48f63b996ad5 + 1.94 1.00000000 fbd51c38-f3a7-42c5-aa3a-c52938188086 +* 3.00 0.33333333 2018ece4-0210-4f07-8cff-eed811f17ded + +komodefi-cli cancel coin MARTY +Cancelling by coin: MARTY +Cancelled: 22532835-8d93-4484-bb6b-e01be0acbde0, 2018ece4-0210-4f07-8cff-eed811f17ded +``` + +## Utility + +The utility provides both work with pubkey blacklists and obtaining avarage MTP of electrum servers. * Aliases: `util`, `pubkeys`, `pubkey`* + +```sh +komodefi-cli util +Utility commands + +Usage: komodefi-cli utility + +Commands: + ban-pubkey Bans the given pubkey ignoring its order matching messages and preventing its orders from displaying in the orderbook. Use the secp256k1 pubkey without prefix for this method input [aliases: ban] + list-banned-pubkeys Returns a list of public keys of nodes that are banned from interacting with the node executing the method [aliases: list, ban-list, list-banned] + unban-pubkeys Remove all currently banned pubkeys from ban list, or specific pubkeys [aliases: unban] + get-current-mtp Returns the Median Time Past (MTP) from electrum servers for UTXO coins [aliases: current-mtp, mtp] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` +### pubkey ban-pubkey (ban) + +The `ban-pubkey` command bans the given pubkey ignoring its order matching messages and preventing its orders from displaying in the orderbook using the [`ban_pubkey` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/ban_pubkey.html). *Alias: `ban`* + +```sh +komodefi-cli pubkey ban --help +Bans the given pubkey ignoring its order matching messages and preventing its orders from displaying in the orderbook. Use the secp256k1 pubkey without prefix for this method input + +Usage: komodefi-cli utility ban-pubkey --pubkey --reason + +Options: + -p, --pubkey Pubkey to ban + -r, --reason Reason of banning + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli order book KMD BTC --publics +Getting orderbook, base: KMD, rel: BTC + Volume: KMD Price: BTC Public + 19458.97 0.00004000 038f5fd70e8f97942913ceae60365c9a8ad26fa28733d1885c710b9036b6bffbab + 3679.85 0.00000837 03de96cb66dcfaceaa8b3d4993ce8914cd5fe84e3fd53cefdae45add8032792a12 +... + +komodefi-cli pubkey ban -p 8f5fd70e8f97942913ceae60365c9a8ad26fa28733d1885c710b9036b6bffbab --reason "too many failed trades" +Banning pubkey: 8f5fd70e8f97942913ceae60365c9a8ad26fa28733d1885c710b9036b6bffbab +Status: Success +... + +komodefi-cli order book KMD BTC --publics +Getting orderbook, base: KMD, rel: BTC + Volume: KMD Price: BTC Public + 3695.15 0.00000835 03de96cb66dcfaceaa8b3d4993ce8914cd5fe84e3fd53cefdae45add8032792a12 +``` + +The orders for banned pubkey have disappeared from the order book. + +### pubkey list-banned-pubkeys (list) + +The `list-banned-pubkeys` lists previously banned pubkeys using the [`list_banned_pubkeys` RPC API methods](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/list_banned_pubkeys.html). *Alias: `list`, `ban-list`, `list-banned`* + +```sh +komodefi-cli pubkeys list --help +Returns a list of public keys of nodes that are banned from interacting with the node executing the method + +Usage: komodefi-cli utility list-banned-pubkeys + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli pubkeys list +Getting list of banned pubkeys +│ pubkey │ reason │ comment │ +│ 8f5fd70e8f97942913ceae60365c9a8ad26fa28733d1885c710b9036b6bffbab │ manual │ too many failed trades │ +│ 5d81c96aa4269c5946c0bd8dad7785ae0f4f595e7aea2ec4f8fe71f77ebf74a9 │ manual │ test ban │ +│ 1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8 │ manual │ one more test ban │ +``` +### pubkey unban-pubkeys (unban) + +The `unban-pubkeys` command unbans whether the all previously banned pubkey or a given one using the [`unban_pubkey` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/unban_pubkeys.html#unban-pubkeys). *Alias: `ban`* + +```sh +komodefi-cli pubkey unban --help +Remove all currently banned pubkeys from ban list, or specific pubkeys + +Usage: komodefi-cli utility unban-pubkeys <--all|--pubkey > + +Options: + -a, --all Whether to unban all pubkeys + -p, --pubkey Pubkey to unban + -h, --help Print help +``` + +**Example:** + +Unban a certain pubkey + +```sh +komodefi-cli pubkey unban -p 8f5fd70e8f97942913ceae60365c9a8ad26fa28733d1885c710b9036b6bffbab +Unbanning pubkeys +still_banned: none +unbanned: 8f5fd70e8f97942913ceae60365c9a8ad26fa28733d1885c710b9036b6bffbab(manually "too many failed trades") +were_not_banned: none +``` + +Unban all pubkeys + +```sh +komodefi-cli pubkey unban --all +Unbanning pubkeys +still_banned: none +unbanned: 5d81c96aa4269c5946c0bd8dad7785ae0f4f595e7aea2ec4f8fe71f77ebf74a9(manually "test ban"), 1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8(manually "one more test ban"), 8f5fd70e8f97942913ceae60365c9a8ad26fa28733d +1885c710b9036b6bffbab(manually "too many failed trades") +were_not_banned: none +``` + +### utild get-current-mtp (mtp) + +The `get-current-mtp` command returns the Median Time Past (MTP) from electrum servers for UTXO coins using the [`get_current_mtp` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20-dev/get_current_mtp.html). This information is useful for debugging, specifically in cases where an electrum server has been misconfigured. *Aliases: `current-mtp`, `mtp`* + +```sh +komodefi-cli utility mtp --help +Returns the Median Time Past (MTP) from electrum servers for UTXO coins + +Usage: komodefi-cli utility get-current-mtp + +Arguments: + A compatible (UTXO) coin's ticker + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli util mtp DOC +Getting current MTP, coin: DOC +Current mtp: 23-09-18 08:34:40 +``` +## Message signing + +The Komodo defi platform provides options for creating a signature on a given message or verifying a message signature. This facility could be used to prove ownership of an address. + +### message sign + +The `sign` command provides a signature on a given message using the [`sign_message` RPC API message](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20/message_signing.html#message-signing). + +```sh +komodefi-cli message sign --help +If your coins file contains the correct message prefix definitions, you can sign message to prove ownership of an address + +Usage: komodefi-cli message {sign|-s} --coin --message + +Options: + -c, --coin The coin to sign a message with + -m, --message The message you want to sign + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli message sign --coin DOC --message "test message" +Signing message +signature: INAWPG9a6vKhbcwhpb6i8Zjg/0sZ30LOcGcW1TmZBDiCfcPIVYvQ5hjWez9wUj4UT6Gc+M2Ky4aKSgIJggUNqTI= +``` + +### message verify + +The `verify` command verifies the signature of a message previously created on a given coin and address. + +```sh +komodefi-cli message verify --help +Verify message signature + +Usage: komodefi-cli message {verify|-v} --coin --message --signature --address
+ +Options: + -c, --coin The coin to sign a message with + -m, --message The message input via the sign_message method sign + -s, --signature The signature generated for the message + -a, --address
The address used to sign the message + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli message verify -c DOC --message "test message" --signature INAWPG9a6vKhbcwhpb6i8Zjg/0sZ30LOcGcW1TmZBDiCfcPIVYvQ5hjWez9wUj4UT6Gc+M2Ky4aKSgIJggUNqTI= --address RPFGrvJWjSYN4qYvcXsECW1H +oHbvQjowZM +Verifying message +is valid: valid +``` + +## Network + +The Network commands are designed to obtain komodo network characteristics that may be useful for debugging purposes. + +```sh +komodefi-cli network +Network commands + +Usage: komodefi-cli network + +Commands: + get-gossip-mesh Return an array of peerIDs added to a topics' mesh for each known gossipsub topic [aliases: gossip-mesh] + get-relay-mesh Return a list of peerIDs included in our local relay mesh [aliases: relay-mesh] + get-gossip-peer-topics Return a map of peerIDs to an array of the topics to which they are subscribed [aliases: peer-topics] + get-gossip-topic-peers Return a map of topics to an array of the PeerIDs which are subscribers [aliases: topic-peers] + get-my-peer-id Return your unique identifying Peer ID on the network [aliases: my-peer-id] + get-peers-info Return all connected peers with their multiaddresses [aliases: peers-info] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` + +### network get-gossip-mesh (gossip-mesh) + +The `get-gossip-mesh` returns an array of peerIDs added to a topics' mesh for each known gossipsub topic using the [` get_gossip_mesh` RPC API method]. *Alias: `gossip-mesh`* + +```sh +komodefi-cli network gossip-mesh --help +Return an array of peerIDs added to a topics' mesh for each known gossipsub topic + +Usage: komodefi-cli network get-gossip-mesh + +Options: + -h, --help Print help +``` + +**Example: ** + +```sh +komodefi-cli network gossip-mesh +Getting gossip mesh +gossip_mesh: +orbk/DOC:KMD: empty +orbk/BTC:KMD: empty +``` +### network get-relay-mesh (relay-mesh) + +The `get-relay-mesh` command returns a list of peerIDs included in our local relay mesh using [`get_relay_mesh` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/get_gossip_mesh.html). *Alias: `relay-mesh`* + +```sh +komodefi-cli network relay-mesh --help +Return a list of peerIDs included in our local relay mesh + +Usage: komodefi-cli network get-relay-mesh + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli network relay-mesh +Getting relay mesh +relay_mesh: +12D3KooWDsFMoRoL5A4ii3UonuQZ9Ti2hrc7PpytRrct2Fg8GRq9 +12D3KooWBXS7vcjYGQ5vy7nZj65FicpdxXsavPdLYB8gN7Ai3ruA +``` + +### network get-gossip-peer-topics (peer-topics) + +The `get-gossip-peer-topics` returns a map of peerIDs to an array of the topics to which they are subscribed using the [` get_gossip_peer_topics` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/get_gossip_peer_topics.html). *Alias: `peer-topics`* + +```sh +komodefi-cli network peer-topics --help +Return a map of peerIDs to an array of the topics to which they are subscribed + +Usage: komodefi-cli network get-gossip-peer-topics + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli network peer-topics +Getting gossip peer topics +gossip_peer_topics: +12D3KooWSmizY35qrfwX8qsuo8H8qrrvDjXBTMRBfeYsRQoybHaA: empty +12D3KooWEaZpH61H4yuQkaNG5AsyGdpBhKRppaLdAY52a774ab5u: empty +12D3KooWCSidNncnbDXrX5G6uWdFdCBrMpaCAqtNxSyfUcZgwF7t: empty +12D3KooWDgFfyAzbuYNLMzMaZT9zBJX9EHd38XLQDRbNDYAYqMzd: empty +12D3KooWDsFMoRoL5A4ii3UonuQZ9Ti2hrc7PpytRrct2Fg8GRq9: empty +12D3KooWBXS7vcjYGQ5vy7nZj65FicpdxXsavPdLYB8gN7Ai3ruA: empty +12D3KooWEsuiKcQaBaKEzuMtT6uFjs89P1E8MK3wGRZbeuCbCw6P: empty +12D3KooWMsfmq3bNNPZTr7HdhTQvxovuR1jo5qvM362VQZorTk3F: empty +``` + +### network get-gossip-topic-peers (topic-peers) + +The `get-gossip-topic-peers` command returns a map of topics to an array of the PeerIDs which are subscribers using the [` get_gossip_topic_peers` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/get_gossip_topic_peers.html). *Alias: `topic-peers`* + +```sh +omodefi-cli network get-gossip-topic-peers --help +Return a map of topics to an array of the PeerIDs which are subscribers + +Usage: komodefi-cli network get-gossip-topic-peers + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli network get-gossip-topic-peers +Getting gossip topic peers +gossip_topic_peers: empty +``` + +### network get-my-peer-id (my-peer-id) + +The `get-my-peer-id` command returns the unique identifying Peer ID corresponding to the running `mm2` node on the network. *Alias: `my-peer-id`* + +```sh +komodefi-cli network my-peer-id --help +Return your unique identifying Peer ID on the network + +Usage: komodefi-cli network get-my-peer-id + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli network my-peer-id +Getting my peer id +12D3KooWA8XJs1HocoDcb28sNYk79UX4ibuBJuHSR84wdWBQPiCr +``` +### network get-peers-info (peers-info) + +The `get-peers-info` command returns all connected peers with their multiaddresses using the [` get_peers_info`](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/get_peers_info.html). + +```sh +komodefi-cli network peers-info --help +Return all connected peers with their multiaddresses + +Usage: komodefi-cli network get-peers-info + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli network peers-info +Getting peers info +peers_info: +12D3KooWEaZpH61H4yuQkaNG5AsyGdpBhKRppaLdAY52a774ab5u: /ip4/46.4.78.11/tcp/38890/p2p/12D3KooWEaZpH61H4yuQkaNG5AsyGdpBhKRppaLdAY52a774ab5u +12D3KooWMsfmq3bNNPZTr7HdhTQvxovuR1jo5qvM362VQZorTk3F: /ip4/2.56.154.200/tcp/38890/p2p/12D3KooWMsfmq3bNNPZTr7HdhTQvxovuR1jo5qvM362VQZorTk3F +12D3KooWDsFMoRoL5A4ii3UonuQZ9Ti2hrc7PpytRrct2Fg8GRq9: /ip4/148.113.1.52/tcp/38890,/ip4/148.113.1.52/tcp/38890/p2p/12D3KooWDsFMoRoL5A4ii3UonuQZ9Ti2hrc7PpytRrct2Fg8GRq9 +... +``` + +## Tracking seed-node version statistics + +The possibility to keep version statistics is provided by tracking nodes that are set up on the `mm2` instance and controlled by version statistics commands. *Aliases: `stat`, `vstat`* + +```sh +komodefi-cli stat +Version statistic commands + +Usage: komodefi-cli version-stat + +Commands: + add-node, -a Adds a Node's name, IP address and PeerID to a local database to track which version of MM2 it is running. Note: To allow collection of version stats, added nodes must open port 38890 [aliases: add, add-node-to-ver +sion-stat] + remove-node, -r Removes a Node (by name) from the local database which tracks which version of MM2 it is running [aliases: remove, remove-node-from-version-stat] + start-collect, -s Initiates storing version statistics for nodes previously registered via the add-node command [aliases: start, start-version-stat-collection] + stop-collect, -S Stops the collection of version stats at the end of the current loop interval [aliases: stop, stop-version-stat-collection] + update-collect, -u Updates the polling interval for version stats collection. Note: the new interval will take effect after the current interval loop has completed. [aliases: update, update-version-stat-collection] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` + +### version-stat add-node-to-version-stat (add) + +The `add-node-to-version-stat` command provides setting up th version statistics node to a local database to track which version of MM2 it is running using the [` add_node_to_version_stat` RPC API method]. *Alias: `add`, `add-node`* + +``` +komodefi-cli stat add --help +Adds a Node's name, IP address and PeerID to a local database to track which version of MM2 it is running. Note: To allow collection of version stats, added nodes must open port 38890 + +Usage: komodefi-cli version-stat {add-node|-a} --name --address
--peer-id + +Options: + -n, --name The name assigned to the node, arbitrary identifying string, such as "seed_alpha" or "dragonhound_DEV" + -a, --address
The Node's IP address or domain names + -p, --peer-id The Peer ID can be found in the MM2 log file after a connection has been initiated + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli stat add --name "test" --address "25.145.122.43" --peer-id 12D3KooWEsuiKcQaBaKEzuMtT6uFjs89P1E8MK3wGRZbeuCbCw6P +Adding stat collection node +Add node to version stat: Success +``` + +### version-stat remove-node-from-version-stat (remove) + +The `remove-node-from-version-stat` command removes the given node from the list of statistics tracking nodes using the [`remove_node_from_version_stat` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20/remove_node_from_version_stat.html). *Alias: `remove`, `remove-node`* + +```sh +komodefi-cli stat remove-node --help +Removes a Node (by name) from the local database which tracks which version of MM2 it is running + +Usage: komodefi-cli version-stat {remove-node|-r} + +Arguments: + The name assigned to the node, arbitrary identifying string, such as "seed_alpha" or "dragonhound_DEV" + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli stat remove-node test +Removing stat collection node +Remove node from version stat: Success +``` + +### version-stat start-version-stat-collection (start) + +The `start-version-stat-collection` command initiates gathering of version statistics on nodes previously registered by the `add-node` command using the [` start_version_stat_collection` RPC API] method. The statistics accumulation is done at polling intervals. *Aliases: `start`, `start-collect`. + + +```sh +komodefi-cli stat start --help +Initiates storing version statistics for nodes previously registered via the add-node command + +Usage: komodefi-cli version-stat {start-collect|-s} + +Arguments: + Polling rate (in seconds) to check node versions + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli stat start 5 +Starting stat collection +Start stat collection: Success +``` + +### version-stat stop-version-stat-collection (stop) + +The `stop-version-stat-collection` stops gathering of version statistcis using the [`stop_version_stat_collection` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20/stop_version_stat_collection.html). *Aliases: `stop`, `stop-collect`* + +```sh +komodefi-cli stat stop --help +Stops the collection of version stats at the end of the current loop interval + +Usage: komodefi-cli version-stat {stop-collect|-S} + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli stat stop +Stopping stat collection +Stop stat collection: Success +``` + +### version-stat update-version-stat-collection (update) + +The `update-version-stat-collection` updates the polling interval for version stats collection using the [`update_version_stat_collection` RPC API method]. + +```sh +komodefi-cli stat update --help +Updates the polling interval for version stats collection. Note: the new interval will take effect after the current interval loop has completed. + +Usage: komodefi-cli version-stat {update-collect|-u} + +Arguments: + Polling rate (in seconds) to check node versions + +Options: + -h, --help Print help +``` + +**Example:** + +```sh +komodefi-cli stat update 10 +Updating stat collection +Update stat collection: Success +``` + +## Task managing + +The komodo defi platform protocol provides a number of asynchronous commands. The current implementation of `komodefi-cli` includes an asynchronous ZHTLC coin enable command. This command returns the `task_id` associated with the command that is running in the background. The following commands provide task management functionality. + +```sh +komodefi-cli task +Tracking the status of long-running commands + +Usage: komodefi-cli task + +Commands: + status Get status of task + cancel Cancel task + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` + +### task status zcoin + +The `status` command gets a status of the background activity by the task_id using the [`task_enable_z_coin_status` RPC API method](https://developers.komodoplatform.com/basic-docs/atomicdex-api-20-dev/zhtlc_coins.html#task-enable-z-coin-status) + +```sh +komodefi-cli task status zcoin --help +Get zcoin enabling status + +Usage: komodefi-cli task status zcoin + +Arguments: + + +Options: + -h, --help Print help +``` + +**Example** + +```sh +komodefi-cli coin enable ZOMBIE +Enabling coin: ZOMBIE +Enabling zcoin started, task_id: 2 +... +komodefi-cli task status zcoin 2 +Getting enable zcoin task status +In progress: Activating coin +... +komodefi-cli task status zcoin 2 +Getting enable zcoin task status +Error: Error on platform coin ZOMBIE creation: All the current light clients are unavailable. +``` diff --git a/mm2src/adex_cli/src/helpers.rs b/mm2src/komodefi_cli/src/helpers.rs similarity index 100% rename from mm2src/adex_cli/src/helpers.rs rename to mm2src/komodefi_cli/src/helpers.rs diff --git a/mm2src/komodefi_cli/src/komodefi_proc/command.rs b/mm2src/komodefi_cli/src/komodefi_proc/command.rs new file mode 100644 index 0000000000..2e87371f76 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/command.rs @@ -0,0 +1,119 @@ +use anyhow::{anyhow, Result}; +use derive_more::Display; +use serde::Serialize; + +use common::log::error; +use mm2_rpc::data::version2::{MmRpcRequest, MmRpcVersion}; + +use crate::error_anyhow; + +#[derive(Clone, Serialize)] +pub(crate) struct Command +where + T: Serialize + Sized, +{ + #[serde(flatten, skip_serializing_if = "Option::is_none")] + pub(crate) flatten_data: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) userpass: Option, +} + +#[derive(Clone, Display, Serialize)] +#[serde(rename_all = "snake_case")] +pub(super) enum V2Method { + BestOrders, + TradePreimage, + Withdraw, + GetPublicKey, + GetPublicKeyHash, + GetRawTransaction, + EnableBchWithTokens, + EnableSlp, + EnableTendermintWithAssets, + EnableTendermintToken, + EnableErc20, + EnableEthWithTokens, + #[serde(rename = "task::enable_z_coin::init")] + EnableZCoin, + #[serde(rename = "task::enable_z_coin::status")] + EnableZCoinStatus, + #[serde(rename = "task::enable_z_coin::cancel")] + EnableZCoinCancel, + AddNodeToVersionStat, + RemoveNodeFromVersionStat, + StartVersionStatCollection, + StopVersionStatCollection, + UpdateVersionStatCollection, + SignMessage, + VerifyMessage, + GetCurrentMtp, + MyTxHistory, + ZCoinTxHistory, +} + +impl Command +where + T: Serialize + Sized, +{ + pub(super) fn builder() -> CommandBuilder { CommandBuilder::new() } +} + +pub(super) struct CommandBuilder { + userpass: Option, + method: Option, + flatten_data: Option, +} + +impl CommandBuilder +where + T: Serialize, +{ + fn new() -> Self { + CommandBuilder { + userpass: None, + method: None, + flatten_data: None, + } + } + + pub(super) fn userpass(&mut self, userpass: String) -> &mut Self { + self.userpass = Some(userpass); + self + } + + pub(super) fn v2_method(&mut self, method: V2Method) -> &mut Self { + self.method = Some(method); + self + } + + pub(super) fn flatten_data(&mut self, flatten_data: T) -> &mut Self { + self.flatten_data = Some(flatten_data); + self + } + + pub(super) fn build(&mut self) -> Result> { + Ok(Command { + userpass: self.userpass.take(), + flatten_data: self.flatten_data.take(), + }) + } + + pub(crate) fn build_v2(&mut self) -> Result>> { + let mm2_rpc_request = MmRpcRequest { + mmrpc: MmRpcVersion::V2, + userpass: Some( + self.userpass + .take() + .ok_or_else(|| error_anyhow!("Failed to build v2 request, password is not set"))?, + ), + method: self + .method + .take() + .ok_or_else(|| error_anyhow!("Failed to build v2 request, method is not set"))?, + params: self.flatten_data.take(), + id: None, + }; + + Ok(mm2_rpc_request) + } +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/komodefi_proc_impl.rs b/mm2src/komodefi_cli/src/komodefi_proc/komodefi_proc_impl.rs new file mode 100644 index 0000000000..bdb1e6e358 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/komodefi_proc_impl.rs @@ -0,0 +1,894 @@ +use anyhow::{anyhow, bail, Result}; +use itertools::Itertools; +use rpc::v1::types::Bytes as BytesJson; +use serde::{Deserialize, Serialize}; +use serde_json::Value as Json; +use std::time::Duration; +use uuid::Uuid; + +use common::log::{debug, error, info, warn}; +use mm2_rpc::data::legacy::{BalanceRequest, BalanceResponse, BanPubkeysRequest, BuyRequest, CancelAllOrdersRequest, + CancelAllOrdersResponse, CancelBy, CancelOrderRequest, CoinInitResponse, + GetEnabledResponse, MakerOrderForRpc, MinTradingVolResponse, Mm2RpcResult, + MmVersionResponse, MyOrdersRequest, MyOrdersResponse, OrderStatusRequest, + OrderStatusResponse, OrderbookDepthRequest, OrderbookRequest, OrderbookResponse, + OrdersHistoryRequest, OrdersHistoryResponse, PairWithDepth, SellBuyResponse, SellRequest, + SetPriceRequest, SetRequiredConfRequest, SetRequiredNotaRequest, Status, StopRequest, + UpdateMakerOrderRequest, VersionRequest}; +use mm2_rpc::data::version2::{BestOrdersRequestV2, GetRawTransactionRequest, MmRpcRequest, MmRpcVersion}; + +use self::macros::{request_legacy, request_v2}; +use super::command::{Command, V2Method}; +use super::response_handler::ResponseHandler; +use super::{OrderbookSettings, OrdersHistorySettings}; +use crate::activation_scheme_db::get_activation_scheme; +use crate::cli_cmd_args::prelude::TxHistoryArgs; +use crate::config::KomodefiConfig; +use crate::rpc_data::activation::{zcoin::ZcoinActivationParams, ActivationMethod, ActivationMethodV2, + EnablePlatformCoinWithTokensReq, InitRpcTaskResponse, InitStandaloneCoinReq, + RpcTaskStatusRequest, SetTxHistory, TaskId}; +use crate::rpc_data::message_signing::{SignatureRequest, VerificationRequest}; +use crate::rpc_data::utility::GetCurrentMtpRequest; +use crate::rpc_data::version_stat::{VStatStartCollectionRequest, VStatUpdateCollectionRequest, + VersionStatAddNodeRequest, VersionStatRemoveNodeRequest}; +use crate::rpc_data::wallet::{ConvertAddressRequest, ConvertAddressResponse, ConvertUtxoAddressRequest, + ConvertUtxoAddressResponse, KmdRewardsInfoRequest, KmdRewardsInfoResponse, + MyTxHistoryRequest, MyTxHistoryRequestV2, MyTxHistoryResponse, ShowPrivateKeyRequest, + ShowPrivateKeyResponse, ValidateAddressRequest, ValidateAddressResponse}; +use crate::rpc_data::{bch, ActiveSwapsRequest, ActiveSwapsResponse, CancelRpcTaskRequest, CoinsToKickStartRequest, + CoinsToKickstartResponse, DisableCoinRequest, DisableCoinResponse, GetEnabledRequest, + GetGossipMeshRequest, GetGossipMeshResponse, GetGossipPeerTopicsRequest, + GetGossipPeerTopicsResponse, GetGossipTopicPeersRequest, GetGossipTopicPeersResponse, + GetMyPeerIdRequest, GetMyPeerIdResponse, GetPeersInfoRequest, GetPeersInfoResponse, + GetRelayMeshRequest, GetRelayMeshResponse, ListBannedPubkeysRequest, ListBannedPubkeysResponse, + MaxTakerVolRequest, MaxTakerVolResponse, MinTradingVolRequest, MmRpcErrorV2, MmRpcResponseV2, + MmRpcResultV2, MyRecentSwapResponse, MyRecentSwapsRequest, MySwapStatusRequest, + MySwapStatusResponse, Params, RecoverFundsOfSwapRequest, RecoverFundsOfSwapResponse, + SendRawTransactionRequest, SendRawTransactionResponse, SetRequiredConfResponse, + SetRequiredNotaResponse, TradePreimageRequest, UnbanPubkeysRequest, UnbanPubkeysResponse, + WithdrawRequest}; +use crate::transport::Transport; + +use crate::{error_anyhow, error_bail, warn_anyhow}; + +pub(crate) struct KomodefiProc<'trp, 'hand, 'cfg, T: Transport, H: ResponseHandler, C: KomodefiConfig + ?Sized> { + pub(crate) transport: Option<&'trp T>, + pub(crate) response_handler: &'hand H, + pub(crate) config: &'cfg C, +} + +impl KomodefiProc<'_, '_, '_, T, P, C> { + pub(in super::super) async fn enable(&self, coin: &str, keep_progress: u64, tx_history: bool) -> Result<()> { + info!("Enabling coin: {coin}"); + let activation_scheme = get_activation_scheme()?; + let mut activation_method = activation_scheme.get_activation_method(coin)?; + + activation_method.set_tx_history(tx_history); + match activation_method { + ActivationMethod::Legacy(ref mut method) => { + let enable = Command::builder() + .flatten_data(method) + .userpass(self.get_rpc_password()?) + .build()?; + + request_legacy!(enable, CoinInitResponse, self, on_enable_response) + }, + ActivationMethod::V2(ActivationMethodV2::EnableBchWithTokens(params)) => self.enable_bch(params).await, + ActivationMethod::V2(ActivationMethodV2::EnableSlp(params)) => { + let enable_slp = self.command_v2(V2Method::EnableSlp, params)?; + request_v2!(self, enable_slp, on_enable_slp ; print_response).await + }, + ActivationMethod::V2(ActivationMethodV2::EnableTendermintWithAssets(params)) => { + let enable_tendermint = self.command_v2(V2Method::EnableTendermintWithAssets, params)?; + request_v2!( + self, + enable_tendermint, + on_enable_tendermint ; print_response + ) + .await + }, + ActivationMethod::V2(ActivationMethodV2::EnableTendermintToken(params)) => { + let enable_tendermint_token = self.command_v2(V2Method::EnableTendermintToken, params)?; + request_v2!( + self, + enable_tendermint_token, + on_enable_tendermint_token ; print_response + ) + .await + }, + ActivationMethod::V2(ActivationMethodV2::EnableEthWithTokens(params)) => { + let enable_erc20 = self.command_v2(V2Method::EnableEthWithTokens, params)?; + request_v2!( + self, + enable_erc20, + on_enable_eth_with_tokens ; print_response + ) + .await + }, + ActivationMethod::V2(ActivationMethodV2::EnableErc20(params)) => { + let enable_erc20 = self.command_v2(V2Method::EnableErc20, params)?; + request_v2!(self, enable_erc20, on_enable_erc20 ; print_response).await + }, + + ActivationMethod::V2(ActivationMethodV2::EnableZCoin(params)) => { + self.enable_z_coin(params, keep_progress).await + }, + } + } + + pub(in super::super) async fn disable(&self, request: DisableCoinRequest) -> Result<()> { + info!("Disabling coin: {}", request.coin); + let disable_command = self.command_legacy(request)?; + request_legacy!(disable_command, DisableCoinResponse, self, on_disable_coin) + } + + pub(in super::super) async fn get_balance(&self, request: BalanceRequest) -> Result<()> { + info!("Getting balance, coin: {}", request.coin); + let get_balance = self.command_legacy(request)?; + request_legacy!(get_balance, BalanceResponse, self, on_balance_response) + } + + pub(in super::super) async fn get_enabled(&self) -> Result<()> { + info!("Getting list of enabled coins ..."); + let enabled = self.command_legacy(GetEnabledRequest::default())?; + request_legacy!(enabled, Mm2RpcResult, self, on_get_enabled_response) + } + + pub(in super::super) async fn get_orderbook( + &self, + request: OrderbookRequest, + settings: OrderbookSettings, + ) -> Result<()> { + info!("Getting orderbook, base: {}, rel: {}", request.base, request.rel); + let get_orderbook = Command::builder().flatten_data(request).build()?; + request_legacy!( + get_orderbook, + OrderbookResponse, + self, + on_orderbook_response, + self.config, + settings + ) + } + + pub(in super::super) async fn sell(&self, request: SellRequest) -> Result<()> { + info!( + "Selling: {} {} for: {} {} at the price of {} {} per {}", + request.delegate.volume, + request.delegate.base, + request.delegate.volume.clone() * request.delegate.price.clone(), + request.delegate.rel, + request.delegate.price, + request.delegate.rel, + request.delegate.base, + ); + let sell = self.command_legacy(request)?; + request_legacy!(sell, Mm2RpcResult, self, on_sell_response) + } + + pub(in super::super) async fn buy(&self, request: BuyRequest) -> Result<()> { + info!( + "Buying: {} {} with: {} {} at the price of {} {} per {}", + request.delegate.volume, + request.delegate.base, + request.delegate.volume.clone() * request.delegate.price.clone(), + request.delegate.rel, + request.delegate.price, + request.delegate.rel, + request.delegate.base, + ); + let buy = self.command_legacy(request)?; + request_legacy!(buy, Mm2RpcResult, self, on_buy_response) + } + + pub(in super::super) async fn send_stop(&self) -> Result<()> { + info!("Sending stop command"); + let stop_command = self.command_legacy(StopRequest::default())?; + request_legacy!(stop_command, Mm2RpcResult, self, on_stop_response) + } + + pub(in super::super) async fn get_version(&self) -> Result<()> { + info!("Requesting for mm2 version"); + let get_version = self.command_legacy(VersionRequest::default())?; + request_legacy!(get_version, MmVersionResponse, self, on_version_response) + } + + pub(in super::super) async fn cancel_order(&self, request: CancelOrderRequest) -> Result<()> { + info!("Cancelling order: {}", request.uuid); + let cancel_order = self.command_legacy(request)?; + request_legacy!(cancel_order, Mm2RpcResult, self, on_cancel_order_response) + } + + pub(in super::super) async fn cancel_all_orders(&self) -> Result<()> { + info!("Cancelling all orders"); + self.cancel_all_orders_impl(CancelAllOrdersRequest { + cancel_by: CancelBy::All, + }) + .await + } + + pub(in super::super) async fn cancel_by_pair(&self, request: CancelAllOrdersRequest) -> Result<()> { + let CancelBy::Pair { base, rel } = &request.cancel_by else {panic!("Bad cast to CancelBy::Pair")}; + info!("Cancelling by pair, base: {base}, rel: {rel}"); + self.cancel_all_orders_impl(request).await + } + + pub(in super::super) async fn cancel_by_coin(&self, request: CancelAllOrdersRequest) -> Result<()> { + let CancelBy::Coin { ticker } = &request.cancel_by else {panic!("Bad cast to CancelBy::Coin")}; + info!("Cancelling by coin: {ticker}"); + self.cancel_all_orders_impl(request).await + } + + async fn cancel_all_orders_impl(&self, request: CancelAllOrdersRequest) -> Result<()> { + let cancel_all = self.command_legacy(request)?; + request_legacy!( + cancel_all, + Mm2RpcResult, + self, + on_cancel_all_response + ) + } + + pub(in super::super) async fn order_status(&self, request: OrderStatusRequest) -> Result<()> { + info!("Getting order status: {}", request.uuid); + let order_status = self.command_legacy(request)?; + request_legacy!(order_status, OrderStatusResponse, self, on_order_status) + } + + pub(in super::super) async fn my_orders(&self) -> Result<()> { + info!("Getting my orders"); + let my_orders = self.command_legacy(MyOrdersRequest::default())?; + request_legacy!(my_orders, Mm2RpcResult, self, on_my_orders) + } + + pub(in super::super) async fn best_orders( + &self, + params: BestOrdersRequestV2, + show_orig_tickets: bool, + ) -> Result<()> { + info!("Getting best orders: {} {}", params.action, params.coin); + let best_orders_command = self.command_v2(V2Method::BestOrders, params)?; + request_v2!( + self, + best_orders_command, + on_best_orders, show_orig_tickets; + print_response + ) + .await + } + + pub(in super::super) async fn set_price(&self, request: SetPriceRequest) -> Result<()> { + info!("Setting price for pair: {} {}", request.base, request.rel); + let set_price = self.command_legacy(request)?; + request_legacy!(set_price, Mm2RpcResult, self, on_set_price) + } + + pub(in super::super) async fn orderbook_depth(&self, request: OrderbookDepthRequest) -> Result<()> { + info!( + "Getting orderbook depth for pairs: {}", + request + .pairs + .iter() + .map(|pair| format!("{}/{}", pair.0, pair.1)) + .join(", ") + ); + let ob_depth = self.command_legacy(request)?; + request_legacy!(ob_depth, Mm2RpcResult>, self, on_orderbook_depth) + } + + pub(in super::super) async fn orders_history( + &self, + request: OrdersHistoryRequest, + settings: OrdersHistorySettings, + ) -> Result<()> { + info!("Getting order history"); + let get_history = self.command_legacy(request)?; + request_legacy!( + get_history, + Mm2RpcResult, + self, + on_orders_history, + settings + ) + } + + pub(in super::super) async fn update_maker_order(&self, request: UpdateMakerOrderRequest) -> Result<()> { + info!("Updating maker order"); + let update_maker_order = self.command_legacy(request)?; + request_legacy!( + update_maker_order, + Mm2RpcResult, + self, + on_update_maker_order + ) + } + + pub(in super::super) async fn active_swaps(&self, include_status: bool) -> Result<()> { + info!("Getting active swaps"); + let active_swaps_command = self.command_legacy(ActiveSwapsRequest { include_status })?; + request_legacy!( + active_swaps_command, + ActiveSwapsResponse, + self, + on_active_swaps, + !include_status + ) + } + + pub(in super::super) async fn swap_status(&self, uuid: Uuid) -> Result<()> { + info!("Getting swap status: {}", uuid); + let my_swap_status_command = Command::builder() + .userpass(self.get_rpc_password()?) + .flatten_data(MySwapStatusRequest { + params: Params { uuid }, + }) + .build()?; + request_legacy!( + my_swap_status_command, + Mm2RpcResult, + self, + on_my_swap_status + ) + } + + pub(in super::super) async fn recent_swaps(&self, request: MyRecentSwapsRequest) -> Result<()> { + info!("Getting recent swaps"); + let recent_swaps_command = self.command_legacy(request)?; + request_legacy!( + recent_swaps_command, + Mm2RpcResult, + self, + on_my_recent_swaps + ) + } + + pub(in super::super) async fn min_trading_vol(&self, coin: String) -> Result<()> { + info!("Getting min trading vol: {}", coin); + let min_trading_vol_command = self.command_legacy(MinTradingVolRequest { coin })?; + request_legacy!( + min_trading_vol_command, + Mm2RpcResult, + self, + on_min_trading_vol + ) + } + + pub(in super::super) async fn max_taker_vol(&self, coin: String) -> Result<()> { + info!("Getting max taker vol, {}", coin); + let max_taker_vol_command = self.command_legacy(MaxTakerVolRequest { coin })?; + request_legacy!(max_taker_vol_command, MaxTakerVolResponse, self, on_max_taker_vol) + } + + pub(in super::super) async fn recover_funds_of_swap(&self, request: RecoverFundsOfSwapRequest) -> Result<()> { + info!("Recovering funds of swap: {}", request.params.uuid); + let recover_funds_command = self.command_legacy(request)?; + request_legacy!( + recover_funds_command, + RecoverFundsOfSwapResponse, + self, + on_recover_funds + ) + } + + pub(in super::super) async fn trade_preimage(&self, request: TradePreimageRequest) -> Result<()> { + info!("Getting trade preimage"); + let trade_preimage_command = self.command_v2(V2Method::TradePreimage, request)?; + request_v2!(self, trade_preimage_command, on_trade_preimage ; print_response).await + } + + pub(in super::super) async fn get_gossip_mesh(&self) -> Result<()> { + info!("Getting gossip mesh"); + let get_gossip_mesh_command = self.command_legacy(GetGossipMeshRequest::default())?; + request_legacy!( + get_gossip_mesh_command, + Mm2RpcResult, + self, + on_gossip_mesh + ) + } + + pub(in super::super) async fn get_relay_mesh(&self) -> Result<()> { + info!("Getting relay mesh"); + let get_relay_mesh_command = self.command_legacy(GetRelayMeshRequest::default())?; + request_legacy!( + get_relay_mesh_command, + Mm2RpcResult, + self, + on_relay_mesh + ) + } + + pub(in super::super) async fn get_gossip_peer_topics(&self) -> Result<()> { + info!("Getting gossip peer topics"); + let get_gossip_peer_topics_command = self.command_legacy(GetGossipPeerTopicsRequest::default())?; + request_legacy!( + get_gossip_peer_topics_command, + Mm2RpcResult, + self, + on_gossip_peer_topics + ) + } + + pub(in super::super) async fn get_gossip_topic_peers(&self) -> Result<()> { + info!("Getting gossip topic peers"); + let get_gossip_topic_peers = self.command_legacy(GetGossipTopicPeersRequest::default())?; + request_legacy!( + get_gossip_topic_peers, + Mm2RpcResult, + self, + on_gossip_topic_peers + ) + } + + pub(in super::super) async fn get_my_peer_id(&self) -> Result<()> { + info!("Getting my peer id"); + let get_my_peer_id_command = self.command_legacy(GetMyPeerIdRequest::default())?; + request_legacy!( + get_my_peer_id_command, + Mm2RpcResult, + self, + on_my_peer_id + ) + } + + pub(in super::super) async fn get_peers_info(&self) -> Result<()> { + info!("Getting peers info"); + let peers_info_command = self.command_legacy(GetPeersInfoRequest::default())?; + request_legacy!( + peers_info_command, + Mm2RpcResult, + self, + on_peers_info + ) + } + + pub(in super::super) async fn set_required_confirmations(&self, request: SetRequiredConfRequest) -> Result<()> { + info!( + "Setting required confirmations: {}, confirmations: {}", + request.coin, request.confirmations + ); + let set_required_conf_command = self.command_legacy(request)?; + request_legacy!( + set_required_conf_command, + Mm2RpcResult, + self, + on_set_confirmations + ) + } + + pub(in super::super) async fn set_required_nota(&self, request: SetRequiredNotaRequest) -> Result<()> { + info!( + "Setting required nota: {}, requires_nota: {}", + request.coin, request.requires_notarization + ); + let set_nota_command = self.command_legacy(request)?; + request_legacy!( + set_nota_command, + Mm2RpcResult, + self, + on_set_notarization + ) + } + + pub(in super::super) async fn coins_to_kick_start(&self) -> Result<()> { + info!("Getting coins needed for kickstart"); + let coins_to_kick_start_command = self.command_legacy(CoinsToKickStartRequest::default())?; + request_legacy!( + coins_to_kick_start_command, + Mm2RpcResult, + self, + on_coins_to_kickstart + ) + } + + pub(in super::super) async fn ban_pubkey(&self, request: BanPubkeysRequest) -> Result<()> { + info!("Banning pubkey: {}", request.pubkey); + let ban_pubkey_command = self.command_legacy(request)?; + request_legacy!(ban_pubkey_command, Mm2RpcResult, self, on_ban_pubkey) + } + + pub(in super::super) async fn list_banned_pubkeys(&self) -> Result<()> { + info!("Getting list of banned pubkeys"); + let list_banned_command = self.command_legacy(ListBannedPubkeysRequest::default())?; + request_legacy!( + list_banned_command, + Mm2RpcResult, + self, + on_list_banned_pubkeys + ) + } + + pub(in super::super) async fn unban_pubkeys(&self, request: UnbanPubkeysRequest) -> Result<()> { + info!("Unbanning pubkeys"); + let unban_pubkeys_command = self.command_legacy(request)?; + request_legacy!( + unban_pubkeys_command, + Mm2RpcResult, + self, + on_unban_pubkeys + ) + } + + pub(in super::super) async fn send_raw_transaction( + &self, + request: SendRawTransactionRequest, + bare_output: bool, + ) -> Result<()> { + info!("Sending raw transaction"); + let send_raw_command = self.command_legacy(request)?; + request_legacy!( + send_raw_command, + SendRawTransactionResponse, + self, + on_send_raw_transaction, + bare_output + ) + } + + pub(in super::super) async fn withdraw(&self, request: WithdrawRequest, bare_output: bool) -> Result<()> { + info!("Getting withdraw tx_hex"); + debug!("Getting withdraw request: {:?}", request); + let withdraw_command = self.command_v2(V2Method::Withdraw, request)?; + request_v2!(self, withdraw_command, on_withdraw, bare_output ; on_mm_rpc_error_v2).await + } + + pub(in super::super) async fn tx_history(&self, args: &mut TxHistoryArgs) -> Result<()> { + info!("Getting tx history, coin: {}", args.coin); + let activation_scheme = get_activation_scheme()?; + let activation_method = activation_scheme.get_activation_method(args.coin.as_str())?; + match activation_method { + ActivationMethod::Legacy(_) => self.tx_history_legacy_impl(args.into()).await, + ActivationMethod::V2(ActivationMethodV2::EnableZCoin(_)) => self.tx_history_zcash_impl(args.into()).await, + ActivationMethod::V2(_) => self.tx_history_v2_impl(args.into()).await, + } + } + + pub(in super::super) async fn show_priv_key(&self, request: ShowPrivateKeyRequest) -> Result<()> { + info!("Getting private key, coin: {}", request.coin); + let show_priv_key = self.command_legacy(request)?; + request_legacy!( + show_priv_key, + Mm2RpcResult, + self, + on_private_key + ) + } + + pub(in super::super) async fn validate_address(&self, request: ValidateAddressRequest) -> Result<()> { + info!( + "Validating address, coin: {}, address: {}", + request.coin, request.address + ); + let validate_address = self.command_legacy(request)?; + request_legacy!( + validate_address, + Mm2RpcResult, + self, + on_validate_address + ) + } + + pub(in super::super) async fn get_kmd_rewards_info(&self) -> Result<()> { + info!("Getting kmd rewards info"); + let get_kmd_rewards = self.command_legacy(KmdRewardsInfoRequest::default())?; + request_legacy!( + get_kmd_rewards, + Mm2RpcResult, + self, + on_kmd_rewards_info + ) + } + + pub(in super::super) async fn convert_address(&self, request: ConvertAddressRequest) -> Result<()> { + info!("Converting address"); + let convert_address = self.command_legacy(request)?; + request_legacy!( + convert_address, + Mm2RpcResult, + self, + on_convert_address + ) + } + + pub(in super::super) async fn convert_utxo_address(&self, request: ConvertUtxoAddressRequest) -> Result<()> { + info!("Convert utxo address"); + let convert_utxo_address = self.command_legacy(request)?; + request_legacy!( + convert_utxo_address, + Mm2RpcResult, + self, + on_convert_utxo_address + ) + } + + async fn tx_history_legacy_impl(&self, request: MyTxHistoryRequest) -> Result<()> { + debug!("Getting tx history request: {:?}", request); + let tx_history = self.command_legacy(request)?; + request_legacy!(tx_history, Mm2RpcResult, self, on_tx_history) + } + + async fn tx_history_v2_impl(&self, request: MyTxHistoryRequestV2) -> Result<()> { + debug!("Getting tx history request: {:?}", request); + let tx_history_v2 = self.command_v2(V2Method::MyTxHistory, request)?; + request_v2!(self, tx_history_v2, on_tx_history_v2 ; on_mm_rpc_error_v2).await + } + + async fn tx_history_zcash_impl(&self, request: MyTxHistoryRequestV2) -> Result<()> { + debug!("Getting tx history request: {:?}", request); + let tx_history_v2 = self.command_v2(V2Method::ZCoinTxHistory, request)?; + request_v2!(self, tx_history_v2, on_tx_history_zcoin ; on_mm_rpc_error_v2).await + } + + pub(in super::super) async fn get_public_key(&self) -> Result<()> { + info!("Getting public key"); + let pubkey_command = self.command_v2(V2Method::GetPublicKey, ())?; + request_v2!(self, pubkey_command, on_public_key ; on_mm_rpc_error_v2).await + } + + pub(in super::super) async fn get_public_key_hash(&self) -> Result<()> { + info!("Getting public key hash"); + let pubkey_hash_command = self.command_v2(V2Method::GetPublicKeyHash, ())?; + request_v2!(self, pubkey_hash_command, on_public_key_hash ; on_mm_rpc_error_v2).await + } + + pub(in super::super) async fn get_current_mtp(&self, request: GetCurrentMtpRequest) -> Result<()> { + info!("Getting current MTP, coin: {}", request.coin); + let get_current_mtp = self.command_v2(V2Method::GetCurrentMtp, request)?; + request_v2!(self, get_current_mtp, on_current_mtp ; on_get_current_mtp_error).await + } + + pub(in super::super) async fn get_raw_transaction( + &self, + request: GetRawTransactionRequest, + bare_output: bool, + ) -> Result<()> { + info!( + "Getting raw transaction of coin: {}, hash: {}", + request.coin, request.tx_hash + ); + let get_raw_tx_command = self.command_v2(V2Method::GetRawTransaction, request)?; + request_v2!( + self, + get_raw_tx_command, + on_raw_transaction, bare_output ; + on_mm_rpc_error_v2 + ) + .await + } + + async fn enable_bch( + &self, + params: EnablePlatformCoinWithTokensReq, + ) -> Result<()> { + let enable_bch = self.command_v2(V2Method::EnableBchWithTokens, params)?; + request_v2!(self, enable_bch, on_enable_bch ; on_mm_rpc_error_v2).await + } + + async fn enable_z_coin( + &self, + params: InitStandaloneCoinReq, + track_timeout_sec: u64, + ) -> Result<()> { + let enable_z_coin = self.command_v2(V2Method::EnableZCoin, params)?; + + let transport = self.transport.ok_or_else(|| { + warn_anyhow!(concat!( + "Failed to send: `", + "enable_z_coin", + "`, transport is not available" + )) + })?; + match transport + .send::<_, MmRpcResponseV2, MmRpcErrorV2>(enable_z_coin) + .await + { + Ok(Ok(MmRpcResponseV2 { + mmrpc: MmRpcVersion::V2, + result: MmRpcResultV2::Ok { result }, + id: _, + })) => { + let task_id = self.response_handler.on_enable_z_coin(result); + if track_timeout_sec != 0 { + self.enable_zcoin_status(task_id, Some(track_timeout_sec)).await?; + } + }, + Ok(Ok(MmRpcResponseV2 { + mmrpc: MmRpcVersion::V2, + result: MmRpcResultV2::Err(error), + id: _, + })) => self.response_handler.on_mm_rpc_error_v2(error), + Ok(Err(error)) => self.response_handler.on_mm_rpc_error_v2(error), + Err(error) => error_bail!( + concat!("Failed to send `", stringify!($request), "` request: {}"), + error + ), + }; + Ok(()) + } + + pub(in super::super) async fn enable_zcoin_status( + &self, + task_id: TaskId, + track_timeout_sec: Option, + ) -> Result<()> { + info!("Getting enable zcoin task status"); + let zcoint_stat = self.command_v2(V2Method::EnableZCoinStatus, RpcTaskStatusRequest { + task_id, + forget_if_finished: true, + })?; + + while request_v2!(self, zcoint_stat, on_zcoin_status ; on_mm_rpc_error_v2).await? { + if let Some(track_timeout) = track_timeout_sec { + tokio::time::sleep(Duration::from_secs(track_timeout)).await; + } else { + break; + } + } + Ok(()) + } + + pub(in super::super) async fn enable_zcoin_cancel(&self, task_id: u64) -> Result<()> { + info!("Canceling enable zcoin task"); + let zcoin_cancel = self.command_v2(V2Method::EnableZCoinCancel, CancelRpcTaskRequest { task_id })?; + request_v2!(self, + zcoin_cancel, + on_enable_zcoin_cancel ; on_enable_zcoin_cancel_error + ) + .await + } + + pub(in super::super) async fn version_stat_add_node(&self, node: VersionStatAddNodeRequest) -> Result<()> { + info!("Adding stat collection node"); + let vstat_add_node = self.command_v2(V2Method::AddNodeToVersionStat, node)?; + request_v2!(self, + vstat_add_node, + on_vstat_add_node ; on_vstat_error + ) + .await + } + + pub(in super::super) async fn version_stat_remove_node(&self, request: VersionStatRemoveNodeRequest) -> Result<()> { + info!("Removing stat collection node"); + let vstat_rem_node = self.command_v2(V2Method::RemoveNodeFromVersionStat, request)?; + request_v2!(self, vstat_rem_node, on_vstat_rem_node ; on_vstat_error ).await + } + + pub(in super::super) async fn version_stat_start_collection( + &self, + request: VStatStartCollectionRequest, + ) -> Result<()> { + info!("Starting stat collection"); + let vstat_start_collection = self.command_v2(V2Method::StartVersionStatCollection, request)?; + request_v2!( + self, + vstat_start_collection, + on_vstat_start_collection ; on_vstat_error + ) + .await + } + + pub(in super::super) async fn version_stat_stop_collection(&self) -> Result<()> { + info!("Stopping stat collection"); + let vstat_stop_collection = self.command_v2(V2Method::StopVersionStatCollection, ())?; + request_v2!( + self, + vstat_stop_collection, + on_vstat_stop_collection ; on_vstat_error + ) + .await + } + + pub(in super::super) async fn version_stat_update_collection( + &self, + request: VStatUpdateCollectionRequest, + ) -> Result<()> { + info!("Updating stat collection"); + let vstat_update_collection = self.command_v2(V2Method::UpdateVersionStatCollection, request)?; + request_v2!( + self, + vstat_update_collection, + on_vstat_update_collection ; on_vstat_error + ) + .await + } + + pub(in super::super) async fn sign_message(&self, request: SignatureRequest) -> Result<()> { + info!("Signing message"); + let sign_message = self.command_v2(V2Method::SignMessage, request)?; + request_v2!(self, sign_message, on_sign_message ; on_signature_error).await + } + + pub(in super::super) async fn verify_message(&self, request: VerificationRequest) -> Result<()> { + info!("Verifying message"); + let verify_message = self.command_v2(V2Method::VerifyMessage, request)?; + request_v2!(self, verify_message, on_verify_message ; on_verificaton_error).await + } + + fn command_legacy(&self, request: R) -> Result> { + Command::builder() + .userpass(self.get_rpc_password()?) + .flatten_data(request) + .build() + } + + fn command_v2(&self, method: V2Method, params: R) -> Result>> { + Command::builder() + .userpass(self.get_rpc_password()?) + .v2_method(method) + .flatten_data(params) + .build_v2() + } + + fn get_rpc_password(&self) -> Result { + self.config + .rpc_password() + .ok_or_else(|| error_anyhow!("Failed to get rpc_password, not set")) + } + + async fn request_v2< + Req: Serialize + Send + Sync, + Resp: for<'a> Deserialize<'a> + Send + Sync, + RespErr: for<'a> Deserialize<'a> + Send + Sync, + Res: Default, + H: FnOnce(Resp) -> Result, + EH: FnOnce(RespErr), + >( + &self, + request: &Req, + handle: H, + err_handle: EH, + ) -> Result { + let transport = self + .transport + .ok_or_else(|| warn_anyhow!("Failed to send request, transport is not available"))?; + match transport.send::<_, MmRpcResponseV2, RespErr>(request).await { + Ok(Ok(MmRpcResponseV2 { + mmrpc: MmRpcVersion::V2, + result: MmRpcResultV2::Ok { result }, + id: _, + })) => handle(result), // server responded with 200 with success + Ok(Ok(MmRpcResponseV2 { + mmrpc: MmRpcVersion::V2, + result: MmRpcResultV2::Err(error), + id: _, + })) => { + self.response_handler.on_mm_rpc_error_v2(error); // server responded with 200 with error + Ok(Res::default()) + }, + Ok(Err(error)) => { + err_handle(error); + Ok(Res::default()) + }, + Err(error) => error_bail!("Failed to send request: {}", error), + } + } +} + +mod macros { + #[macro_export] + macro_rules! request_legacy { + ($request: ident, $response_ty: ty, $self: ident, $handle_method: ident$ (, $opt:expr)*) => {{ + let transport = $self.transport.ok_or_else(|| warn_anyhow!( concat!("Failed to send: `", stringify!($request), "`, transport is not available")))?; + match transport.send::<_, $response_ty, Json>($request).await { + Ok(Ok(ok)) => $self.response_handler.$handle_method(ok, $($opt),*), + Ok(Err(error)) => $self.response_handler.print_response(error), + Err(error) => error_bail!(concat!("Failed to send: `", stringify!($request), "`: {}"), error) + } + }}; + } + #[macro_export] + macro_rules! request_v2 { + ($self: ident, $request: expr, $handle: ident$(, $opt:expr)* ; $err_handle: ident) => {{ + $self + .request_v2( + &$request, + |response| $self.response_handler.$handle(response, $($opt),*), + |error| { + let _ = $self.response_handler.$err_handle(error); + }, + ) + + }}; + } + pub(super) use {request_legacy, request_v2}; +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/mod.rs b/mm2src/komodefi_cli/src/komodefi_proc/mod.rs new file mode 100644 index 0000000000..33a876cbb7 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/mod.rs @@ -0,0 +1,7 @@ +mod command; +mod komodefi_proc_impl; +mod response_handler; + +pub(super) use komodefi_proc_impl::KomodefiProc; +pub(super) use response_handler::{OrderbookSettings, OrdersHistorySettings, ResponseHandler, ResponseHandlerImpl, + SmartFractPrecision}; diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler.rs new file mode 100644 index 0000000000..6968b6fe7a --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler.rs @@ -0,0 +1,607 @@ +#[path = "response_handler/activation.rs"] mod activation; +#[path = "response_handler/best_orders.rs"] mod best_orders; +#[path = "response_handler/formatters.rs"] mod formatters; +#[path = "response_handler/macros.rs"] mod macros; +#[path = "response_handler/message_signing.rs"] +mod message_signing; +#[path = "response_handler/my_orders.rs"] mod my_orders; +#[path = "response_handler/network.rs"] mod network; +#[path = "response_handler/order_status.rs"] mod order_status; +#[path = "response_handler/orderbook.rs"] mod orderbook; +#[path = "response_handler/orderbook_depth.rs"] +mod orderbook_depth; +#[path = "response_handler/orders_history.rs"] +mod orders_history; +#[path = "response_handler/smart_fraction_fmt.rs"] +mod smart_fraction_fmt; +#[path = "response_handler/swaps.rs"] mod swaps; +#[path = "response_handler/trading.rs"] mod trading; +#[path = "response_handler/utility.rs"] mod utility; +#[path = "response_handler/version_stat.rs"] mod version_stat; +#[path = "response_handler/wallet.rs"] mod wallet; + +pub(crate) use orderbook::OrderbookSettings; +pub(crate) use orders_history::OrdersHistorySettings; +pub(crate) use smart_fraction_fmt::SmartFractPrecision; + +use anyhow::{anyhow, Result}; +use itertools::Itertools; +use rpc::v1::types::Bytes as BytesJson; +use serde_json::Value as Json; +use std::cell::RefCell; +use std::io::Write; +use std::ops::DerefMut; + +use common::log::error; +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; +use mm2_rpc::data::legacy::{BalanceResponse, CancelAllOrdersResponse, CoinInitResponse, GetEnabledResponse, + MakerOrderForRpc, MinTradingVolResponse, Mm2RpcResult, MmVersionResponse, + MyOrdersResponse, OrderStatusResponse, OrderbookResponse, OrdersHistoryResponse, + PairWithDepth, SellBuyResponse, Status}; +use mm2_rpc::data::version2::{BestOrdersV2Response, GetPublicKeyHashResponse, GetPublicKeyResponse, + GetRawTransactionResponse}; + +use crate::config::KomodefiConfig; +use crate::komodefi_proc::response_handler::formatters::{writeln_field, ZERO_INDENT}; +use crate::logging::error_anyhow; +use crate::rpc_data::activation::{InitRpcTaskResponse, TaskId}; +use crate::rpc_data::bch::{BchWithTokensActivationResult, SlpInitResult}; +use crate::rpc_data::eth::{Erc20InitResult, EthWithTokensActivationResult}; +use crate::rpc_data::message_signing::{SignatureError, SignatureResponse, VerificationError, VerificationResponse}; +use crate::rpc_data::tendermint::{TendermintActivationResult, TendermintTokenInitResult}; +use crate::rpc_data::utility::{GetCurrentMtpError, GetCurrentMtpResponse}; +use crate::rpc_data::version_stat::NodeVersionError; +use crate::rpc_data::wallet::{ConvertAddressResponse, ConvertUtxoAddressResponse, KmdRewardsInfoResponse, + MyTxHistoryDetails, MyTxHistoryResponse, MyTxHistoryResponseV2, ShowPrivateKeyResponse, + ValidateAddressResponse, ZcoinTxDetails}; +use crate::rpc_data::zcoin::ZCoinStatus; +use crate::rpc_data::{ActiveSwapsResponse, CancelRpcTaskError, CoinsToKickstartResponse, DisableCoinResponse, + GetGossipMeshResponse, GetGossipPeerTopicsResponse, GetGossipTopicPeersResponse, + GetMyPeerIdResponse, GetPeersInfoResponse, GetRelayMeshResponse, ListBannedPubkeysResponse, + MaxTakerVolResponse, MmRpcErrorV2, MyRecentSwapResponse, MySwapStatusResponse, + RecoverFundsOfSwapResponse, SendRawTransactionResponse, SetRequiredConfResponse, + SetRequiredNotaResponse, TradePreimageResponse, UnbanPubkeysResponse, WithdrawResponse}; + +pub(crate) trait ResponseHandler { + fn print_response(&self, response: Json) -> Result<()>; + + fn on_orderbook_response( + &self, + response: OrderbookResponse, + config: &Cfg, + settings: OrderbookSettings, + ) -> Result<()>; + fn on_get_enabled_response(&self, response: Mm2RpcResult) -> Result<()>; + fn on_version_response(&self, response: MmVersionResponse) -> Result<()>; + fn on_enable_response(&self, response: CoinInitResponse) -> Result<()>; + fn on_enable_bch(&self, response: BchWithTokensActivationResult) -> Result<()>; + fn on_enable_slp(&self, response: SlpInitResult) -> Result<()>; + fn on_enable_tendermint(&self, response: TendermintActivationResult) -> Result<()>; + fn on_enable_tendermint_token(&self, response: TendermintTokenInitResult) -> Result<()>; + fn on_enable_erc20(&self, response: Erc20InitResult) -> Result<()>; + fn on_enable_eth_with_tokens(&self, response: EthWithTokensActivationResult) -> Result<()>; + fn on_enable_z_coin(&self, response: InitRpcTaskResponse) -> TaskId; + fn on_zcoin_status(&self, respone: ZCoinStatus) -> Result; + fn on_disable_coin(&self, response: DisableCoinResponse) -> Result<()>; + fn on_balance_response(&self, response: BalanceResponse) -> Result<()>; + fn on_sell_response(&self, response: Mm2RpcResult) -> Result<()>; + fn on_buy_response(&self, response: Mm2RpcResult) -> Result<()>; + fn on_stop_response(&self, response: Mm2RpcResult) -> Result<()>; + fn on_cancel_order_response(&self, response: Mm2RpcResult) -> Result<()>; + fn on_cancel_all_response(&self, response: Mm2RpcResult) -> Result<()>; + fn on_order_status(&self, response: OrderStatusResponse) -> Result<()>; + fn on_best_orders(&self, response: BestOrdersV2Response, show_orig_tickets: bool) -> Result<()>; + fn on_my_orders(&self, response: Mm2RpcResult) -> Result<()>; + fn on_set_price(&self, response: Mm2RpcResult) -> Result<()>; + fn on_orderbook_depth(&self, response: Mm2RpcResult>) -> Result<()>; + fn on_orders_history( + &self, + response: Mm2RpcResult, + settings: OrdersHistorySettings, + ) -> Result<()>; + fn on_update_maker_order(&self, response: Mm2RpcResult) -> Result<()>; + fn on_active_swaps(&self, response: ActiveSwapsResponse, uuids_only: bool) -> Result<()>; + fn on_my_swap_status(&self, response: Mm2RpcResult) -> Result<()>; + fn on_my_recent_swaps(&self, response: Mm2RpcResult) -> Result<()>; + fn on_min_trading_vol(&self, response: Mm2RpcResult) -> Result<()>; + fn on_max_taker_vol(&self, response: MaxTakerVolResponse) -> Result<()>; + fn on_recover_funds(&self, response: RecoverFundsOfSwapResponse) -> Result<()>; + fn on_trade_preimage(&self, response: TradePreimageResponse) -> Result<()>; + fn on_gossip_mesh(&self, response: Mm2RpcResult) -> Result<()>; + fn on_relay_mesh(&self, response: Mm2RpcResult) -> Result<()>; + fn on_gossip_peer_topics(&self, response: Mm2RpcResult) -> Result<()>; + fn on_gossip_topic_peers(&self, response: Mm2RpcResult) -> Result<()>; + fn on_my_peer_id(&self, response: Mm2RpcResult) -> Result<()>; + fn on_peers_info(&self, response: Mm2RpcResult) -> Result<()>; + fn on_set_confirmations(&self, resonse: Mm2RpcResult) -> Result<()>; + fn on_set_notarization(&self, response: Mm2RpcResult) -> Result<()>; + fn on_coins_to_kickstart(&self, response: Mm2RpcResult) -> Result<()>; + fn on_ban_pubkey(&self, response: Mm2RpcResult) -> Result<()>; + fn on_list_banned_pubkeys(&self, response: Mm2RpcResult) -> Result<()>; + fn on_unban_pubkeys(&self, response: Mm2RpcResult) -> Result<()>; + fn on_current_mtp(&self, response: GetCurrentMtpResponse) -> Result<()>; + fn on_get_current_mtp_error(&self, response: GetCurrentMtpError); + fn on_send_raw_transaction(&self, response: SendRawTransactionResponse, bare_output: bool) -> Result<()>; + fn on_withdraw(&self, response: WithdrawResponse, bare_output: bool) -> Result<()>; + fn on_tx_history(&self, response: Mm2RpcResult) -> Result<()>; + fn on_tx_history_v2(&self, response: MyTxHistoryResponseV2) -> Result<()>; + fn on_tx_history_zcoin(&self, response: MyTxHistoryResponseV2) -> Result<()>; + fn on_public_key(&self, response: GetPublicKeyResponse) -> Result<()>; + fn on_public_key_hash(&self, response: GetPublicKeyHashResponse) -> Result<()>; + fn on_raw_transaction(&self, response: GetRawTransactionResponse, bare_output: bool) -> Result<()>; + fn on_mm_rpc_error_v2(&self, error: MmRpcErrorV2); + fn on_enable_zcoin_cancel(&self, response: Status) -> Result<()>; + fn on_enable_zcoin_cancel_error(&self, error: CancelRpcTaskError) -> Result<()>; + fn on_vstat_add_node(&self, response: Status) -> Result<()>; + fn on_vstat_error(&self, error: NodeVersionError) -> Result<()>; + fn on_vstat_rem_node(&self, response: Status) -> Result<()>; + fn on_vstat_start_collection(&self, response: Status) -> Result<()>; + fn on_vstat_stop_collection(&self, response: Status) -> Result<()>; + fn on_vstat_update_collection(&self, response: Status) -> Result<()>; + fn on_sign_message(&self, response: SignatureResponse) -> Result<()>; + fn on_verify_message(&self, response: VerificationResponse) -> Result<()>; + fn on_signature_error(&self, error: SignatureError); + fn on_verificaton_error(&self, error: VerificationError); + fn on_private_key(&self, response: Mm2RpcResult) -> Result<()>; + fn on_validate_address(&self, response: Mm2RpcResult) -> Result<()>; + fn on_kmd_rewards_info(&self, response: Mm2RpcResult) -> Result<()>; + fn on_convert_address(&self, response: Mm2RpcResult) -> Result<()>; + fn on_convert_utxo_address(&self, response: Mm2RpcResult) -> Result<()>; +} + +pub(crate) struct ResponseHandlerImpl<'a> { + pub(crate) writer: RefCell<&'a mut dyn Write>, +} + +impl ResponseHandler for ResponseHandlerImpl<'_> { + fn print_response(&self, result: Json) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + let object = result + .as_object() + .ok_or_else(|| error_anyhow!("Failed to cast result as object"))?; + + object + .iter() + .for_each(|value| writeln_safe_io!(writer, "{}: {:?}", value.0, value.1)); + Ok(()) + } + + fn on_orderbook_response( + &self, + response: OrderbookResponse, + config: &Cfg, + settings: OrderbookSettings, + ) -> Result<()> { + orderbook::on_orderbook_response(self.writer.borrow_mut().deref_mut(), response, config, settings) + } + + fn on_get_enabled_response(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + writeln_safe_io!(writer, "{:8} {}", "Ticker", "Address"); + for row in &response.result { + writeln_safe_io!(writer, "{:8} {}", row.ticker, row.address); + } + Ok(()) + } + + fn on_version_response(&self, response: MmVersionResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + writeln_safe_io!(writer, "Version: {}", response.result); + writeln_safe_io!(writer, "Datetime: {}", response.datetime); + Ok(()) + } + + fn on_enable_response(&self, response: CoinInitResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + writeln_safe_io!( + writer, + "coin: {}\naddress: {}\nbalance: {}\nunspendable_balance: {}\nrequired_confirmations: {}\nrequires_notarization: {}", + response.coin, + response.address, + response.balance, + response.unspendable_balance, + response.required_confirmations, + if response.requires_notarization { "Yes" } else { "No" } + ); + if let Some(mature_confirmations) = response.mature_confirmations { + writeln_safe_io!(writer, "mature_confirmations: {}", mature_confirmations); + } + Ok(()) + } + + fn on_enable_bch(&self, response: BchWithTokensActivationResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_enable_bch(writer.deref_mut(), response) + } + + fn on_enable_slp(&self, response: SlpInitResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_enable_slp(writer.deref_mut(), response) + } + + fn on_enable_tendermint(&self, response: TendermintActivationResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_enable_tendermint(writer.deref_mut(), response) + } + + fn on_enable_tendermint_token(&self, response: TendermintTokenInitResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_enable_tendermint_token(writer.deref_mut(), response) + } + + fn on_enable_erc20(&self, response: Erc20InitResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_enable_erc20(writer.deref_mut(), response) + } + + fn on_enable_eth_with_tokens(&self, response: EthWithTokensActivationResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_enable_eth_with_tokens(writer.deref_mut(), response) + } + + fn on_enable_z_coin(&self, response: InitRpcTaskResponse) -> TaskId { + let mut writer = self.writer.borrow_mut(); + activation::on_enable_zcoin(writer.deref_mut(), response) + } + + fn on_zcoin_status(&self, response: ZCoinStatus) -> Result { + let mut writer = self.writer.borrow_mut(); + activation::z_coin::on_enable_zcoin_status(writer.deref_mut(), response) + } + + fn on_disable_coin(&self, response: DisableCoinResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_disable_coin(writer.deref_mut(), response); + Ok(()) + } + + fn on_balance_response(&self, response: BalanceResponse) -> Result<()> { + writeln_safe_io!( + self.writer.borrow_mut(), + "coin: {}\nbalance: {}\nunspendable: {}\naddress: {}", + response.coin, + response.balance, + response.unspendable_balance, + response.address + ); + Ok(()) + } + + fn on_sell_response(&self, response: Mm2RpcResult) -> Result<()> { + writeln_safe_io!(self.writer.borrow_mut(), "{}", response.request.uuid); + Ok(()) + } + + fn on_buy_response(&self, response: Mm2RpcResult) -> Result<()> { + writeln_safe_io!(self.writer.borrow_mut(), "{}", response.request.uuid); + Ok(()) + } + + fn on_stop_response(&self, response: Mm2RpcResult) -> Result<()> { + writeln_safe_io!(self.writer.borrow_mut(), "Service stopped: {}", response.result); + Ok(()) + } + + fn on_cancel_order_response(&self, response: Mm2RpcResult) -> Result<()> { + writeln_safe_io!(self.writer.borrow_mut(), "Order cancelled: {}", response.result); + Ok(()) + } + + fn on_cancel_all_response(&self, response: Mm2RpcResult) -> Result<()> { + let cancelled = &response.result.cancelled; + let mut writer = self.writer.borrow_mut(); + if cancelled.is_empty() { + writeln_safe_io!(writer, "No orders found to be cancelled"); + } else { + writeln_safe_io!(writer, "Cancelled: {}", cancelled.iter().join(", ")); + } + + let currently_matched = &response.result.currently_matching; + if !currently_matched.is_empty() { + writeln_safe_io!(writer, "Currently matched: {}", currently_matched.iter().join(", ")); + } + Ok(()) + } + + fn on_order_status(&self, response: OrderStatusResponse) -> Result<()> { + order_status::on_order_status(self.writer.borrow_mut().deref_mut(), response) + } + + fn on_best_orders(&self, response: BestOrdersV2Response, show_orig_tickets: bool) -> Result<()> { + best_orders::on_best_orders(self.writer.borrow_mut().deref_mut(), response, show_orig_tickets) + } + + fn on_my_orders(&self, response: Mm2RpcResult) -> Result<()> { + my_orders::on_my_orders(self.writer.borrow_mut().deref_mut(), response) + } + + fn on_set_price(&self, response: Mm2RpcResult) -> Result<()> { + formatters::on_maker_order_response(self.writer.borrow_mut().deref_mut(), response.result) + } + + fn on_orderbook_depth(&self, response: Mm2RpcResult>) -> Result<()> { + orderbook_depth::on_orderbook_depth(self.writer.borrow_mut().deref_mut(), response) + } + + fn on_orders_history( + &self, + response: Mm2RpcResult, + settings: OrdersHistorySettings, + ) -> Result<()> { + orders_history::on_orders_history(self.writer.borrow_mut().deref_mut(), response, settings) + } + + fn on_update_maker_order(&self, response: Mm2RpcResult) -> Result<()> { + formatters::on_maker_order_response(self.writer.borrow_mut().deref_mut(), response.result) + } + + fn on_active_swaps(&self, response: ActiveSwapsResponse, uuids_only: bool) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + swaps::on_active_swaps(writer.deref_mut(), response, uuids_only) + } + + fn on_my_swap_status(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + swaps::on_my_swap_status(writer.deref_mut(), response.result) + } + + fn on_my_recent_swaps(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + swaps::on_my_recent_swaps(writer.deref_mut(), response.result) + } + + fn on_min_trading_vol(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + trading::on_min_trading_vol(writer.deref_mut(), response.result) + } + + fn on_max_taker_vol(&self, response: MaxTakerVolResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + trading::on_max_taker_vol(writer.deref_mut(), response) + } + + fn on_recover_funds(&self, response: RecoverFundsOfSwapResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + swaps::on_recover_funds(writer.deref_mut(), response) + } + + fn on_trade_preimage(&self, response: TradePreimageResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + trading::on_trade_preimage(writer.deref_mut(), response) + } + + fn on_gossip_mesh(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + network::on_gossip_mesh(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_relay_mesh(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + network::on_relay_mesh(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_gossip_peer_topics(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + network::on_gossip_peer_topics(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_gossip_topic_peers(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + network::on_gossip_topic_peers(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_my_peer_id(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + network::on_my_peer_id(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_peers_info(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + network::on_peers_info(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_set_confirmations(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_set_confirmations(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_set_notarization(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_set_notarization(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_coins_to_kickstart(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::on_coins_to_kickstart(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_ban_pubkey(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + writeln_field(writer.deref_mut(), "Status", response.result, ZERO_INDENT); + Ok(()) + } + + fn on_list_banned_pubkeys(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + utility::on_list_banned_pubkeys(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_unban_pubkeys(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + utility::on_unban_pubkeys(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_current_mtp(&self, response: GetCurrentMtpResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + utility::on_current_mtp(writer.deref_mut(), response); + Ok(()) + } + + fn on_get_current_mtp_error(&self, error: GetCurrentMtpError) { + let mut writer = self.writer.borrow_mut(); + utility::on_get_current_mtp_error(writer.deref_mut(), error); + } + + fn on_send_raw_transaction(&self, response: SendRawTransactionResponse, bare_output: bool) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_send_raw_transaction(writer.deref_mut(), response, bare_output); + Ok(()) + } + + fn on_withdraw(&self, response: WithdrawResponse, bare_output: bool) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_withdraw(writer.deref_mut(), response, bare_output) + } + + fn on_tx_history(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_tx_history(writer.deref_mut(), response.result) + } + + fn on_tx_history_v2(&self, response: MyTxHistoryResponseV2) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_tx_history_v2(writer.deref_mut(), response) + } + + fn on_tx_history_zcoin(&self, response: MyTxHistoryResponseV2) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_tx_history_zcoin(writer.deref_mut(), response) + } + + fn on_public_key(&self, response: GetPublicKeyResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_public_key(writer.deref_mut(), response); + Ok(()) + } + + fn on_public_key_hash(&self, response: GetPublicKeyHashResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_public_key_hash(writer.deref_mut(), response); + Ok(()) + } + + fn on_raw_transaction(&self, response: GetRawTransactionResponse, bare_output: bool) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_raw_transaction(writer.deref_mut(), response, bare_output); + Ok(()) + } + + fn on_private_key(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_private_key(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_validate_address(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_validate_address(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_kmd_rewards_info(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_kmd_rewards_info(writer.deref_mut(), response.result) + } + + fn on_convert_address(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_convert_address(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_convert_utxo_address(&self, response: Mm2RpcResult) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + wallet::on_convert_utxo_address(writer.deref_mut(), response.result); + Ok(()) + } + + fn on_mm_rpc_error_v2(&self, error: MmRpcErrorV2) { + let mut writer = self.writer.borrow_mut(); + let writer = writer.deref_mut(); + writeln_field(writer, "error", error.error, ZERO_INDENT); + writeln_field(writer, "error_path", error.error_path, ZERO_INDENT); + writeln_field(writer, "error_trace", error.error_trace, ZERO_INDENT); + } + + fn on_enable_zcoin_cancel(&self, response: Status) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + let writer = writer.deref_mut(); + activation::z_coin::on_enable_zcoin_canceled(writer, response); + Ok(()) + } + + fn on_enable_zcoin_cancel_error(&self, error: CancelRpcTaskError) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + activation::z_coin::on_enable_zcoin_cancel_error(writer.deref_mut(), error); + Ok(()) + } + + fn on_vstat_add_node(&self, response: Status) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + version_stat::on_vstat_add_node(writer.deref_mut(), response); + Ok(()) + } + + fn on_vstat_error(&self, error: NodeVersionError) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + version_stat::on_node_version_error(writer.deref_mut(), error); + Ok(()) + } + + fn on_vstat_rem_node(&self, response: Status) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + version_stat::on_vstat_rem_node(writer.deref_mut(), response); + Ok(()) + } + + fn on_vstat_start_collection(&self, response: Status) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + version_stat::on_vstat_start_collection(writer.deref_mut(), response); + Ok(()) + } + + fn on_vstat_stop_collection(&self, response: Status) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + version_stat::on_vstat_stop_collection(writer.deref_mut(), response); + Ok(()) + } + + fn on_vstat_update_collection(&self, response: Status) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + version_stat::on_vstat_update_collection(writer.deref_mut(), response); + Ok(()) + } + + fn on_sign_message(&self, response: SignatureResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + message_signing::on_sign_message(writer.deref_mut(), response); + Ok(()) + } + + fn on_signature_error(&self, error: SignatureError) { + let mut writer = self.writer.borrow_mut(); + message_signing::on_signature_error(writer.deref_mut(), error); + } + + fn on_verify_message(&self, response: VerificationResponse) -> Result<()> { + let mut writer = self.writer.borrow_mut(); + message_signing::on_verify_message(writer.deref_mut(), response); + Ok(()) + } + + fn on_verificaton_error(&self, error: VerificationError) { + let mut writer = self.writer.borrow_mut(); + message_signing::on_verification_error(writer.deref_mut(), error); + } +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation.rs new file mode 100644 index 0000000000..14f4927cc5 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation.rs @@ -0,0 +1,115 @@ +#[path = "activation/bch.rs"] pub(crate) mod bch; +#[path = "activation/eth.rs"] pub(crate) mod eth; +#[path = "activation/tendermint.rs"] pub(crate) mod tendermint; +#[path = "activation/z_coin.rs"] pub(crate) mod z_coin; + +pub(super) use bch::{on_enable_bch, on_enable_slp}; +pub(super) use eth::{on_enable_erc20, on_enable_eth_with_tokens}; +pub(super) use tendermint::{on_enable_tendermint, on_enable_tendermint_token}; +pub(super) use z_coin::on_enable_zcoin; + +use anyhow::{anyhow, Result}; +use itertools::Itertools; +use std::collections::HashMap; +use std::io::Write; +use term_table::{row::Row, TableStyle}; + +use common::log::error; + +use super::formatters::{format_ratio, term_table_blank, write_sequence, writeln_field, COMMON_PRECISION, ZERO_INDENT}; +use crate::error_anyhow; +use crate::rpc_data::activation::{CoinAddressInfo, CoinBalance, TokenBalances}; +use crate::rpc_data::{CoinsToKickstartResponse, DisableCoinFailed, DisableCoinResponse, DisableCoinSuccess, + SetRequiredConfResponse, SetRequiredNotaResponse}; + +pub(super) fn on_disable_coin(writer: &mut dyn Write, response: DisableCoinResponse) { + match response { + DisableCoinResponse::Success(mm2_rpc_result) => write_disable_success(writer, mm2_rpc_result.result), + DisableCoinResponse::Failed(disable_failed) => write_disable_failed(writer, disable_failed), + } +} + +pub(super) fn on_set_confirmations(writer: &mut dyn Write, response: SetRequiredConfResponse) { + writeln_field(writer, "coin", response.coin, ZERO_INDENT); + writeln_field(writer, "confirmations", response.confirmations, ZERO_INDENT); +} + +pub(super) fn on_set_notarization(writer: &mut dyn Write, response: SetRequiredNotaResponse) { + writeln_field(writer, "coin", response.coin, ZERO_INDENT); + writeln_field( + writer, + "requires_notarization", + response.requires_notarization, + ZERO_INDENT, + ); +} + +pub(super) fn on_coins_to_kickstart(writer: &mut dyn Write, coins: CoinsToKickstartResponse) { + write_sequence(writer, "coins", coins.iter(), ZERO_INDENT); +} + +fn write_disable_success(writer: &mut dyn Write, disable_success: DisableCoinSuccess) { + writeln_field(writer, "coin", disable_success.coin, ZERO_INDENT); + let cancelled_orders = disable_success.cancelled_orders.iter(); + write_sequence(writer, "cancelled_orders", cancelled_orders, ZERO_INDENT); + writeln_field(writer, "passivized", disable_success.passivized, ZERO_INDENT); +} + +fn write_disable_failed(writer: &mut dyn Write, disable_failed: DisableCoinFailed) { + writeln_field(writer, "error", disable_failed.error, ZERO_INDENT); + + let active_swaps = disable_failed.active_swaps.iter(); + write_sequence(writer, "active_swaps", active_swaps, ZERO_INDENT); + let orders_matching = disable_failed.orders.matching.iter(); + write_sequence(writer, "orders_matching", orders_matching, ZERO_INDENT); + let orders_cancelled = disable_failed.orders.cancelled.iter(); + write_sequence(writer, "orders_matching", orders_cancelled, ZERO_INDENT); +} + +fn format_coin_balance(balance: CoinBalance) -> Result { + Ok(format!( + "{}:{}", + format_ratio(&balance.spendable, COMMON_PRECISION)?, + format_ratio(&balance.unspendable, COMMON_PRECISION)? + )) +} + +fn format_token_balances(balances: TokenBalances) -> Result { + if balances.is_empty() { + return Ok("{}".to_string()); + } + let mut buff: Vec = vec![]; + let writer: &mut dyn Write = &mut buff; + for (token, balance) in balances { + writeln_field(writer, token, format_coin_balance(balance)?, ZERO_INDENT); + } + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to format token_balances: {}", error)) +} + +fn format_addr_infos Result + Copy>( + addr: HashMap>, + format_balance: F, +) -> String { + let mut term_table = term_table_blank(TableStyle::thin(), false, false, false); + term_table.add_row(Row::new(["address, pubkey", "method", "balance(sp,unsp)", "tickers"])); + for (address, info) in addr { + term_table.add_row(Row::new(vec![ + format!("{}\n{}", address, info.pubkey), + info.derivation_method.to_string(), + format_option( + info.balances + .map(format_balance) + .map(|result| result.unwrap_or_else(|_| "error".to_string())), + ), + format_option(info.tickers.map(|tickers| tickers.iter().join("\n"))), + ])) + } + term_table.render() +} + +fn format_option(value: Option) -> String { + let Some(value) = value else { + return "none".to_string(); + }; + value.to_string() +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/bch.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/bch.rs new file mode 100644 index 0000000000..912efff56d --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/bch.rs @@ -0,0 +1,54 @@ +use anyhow::Result; +use std::io::Write; +use term_table::{row::Row, TableStyle}; + +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; + +use super::super::formatters::{term_table_blank, writeln_field, ZERO_INDENT}; +use super::{format_addr_infos, format_coin_balance, format_token_balances}; +use crate::rpc_data::bch::{BchWithTokensActivationResult, SlpInitResult}; + +pub(in super::super) fn on_enable_bch(writer: &mut dyn Write, response: BchWithTokensActivationResult) -> Result<()> { + writeln_field(writer, "current_block", response.current_block, ZERO_INDENT); + + if response.bch_addresses_infos.is_empty() { + writeln_field(writer, "bch_addresses_infos", "none", ZERO_INDENT); + } else { + writeln_field(writer, "bch_addresses_infos", "", ZERO_INDENT); + let addr_infos = format_addr_infos(response.bch_addresses_infos, format_coin_balance); + writeln_safe_io!(writer, "{}", addr_infos); + } + + if response.slp_addresses_infos.is_empty() { + writeln_field(writer, "slp_addresses_infos", "none", ZERO_INDENT); + } else { + writeln_field(writer, "slp_addresses_infos", "", ZERO_INDENT); + let addr_infos = format_addr_infos(response.slp_addresses_infos, format_token_balances); + writeln_safe_io!(writer, "{}", addr_infos); + } + + Ok(()) +} + +pub(in super::super) fn on_enable_slp(writer: &mut dyn Write, response: SlpInitResult) -> Result<()> { + writeln_field(writer, "platform_coin", response.platform_coin, ZERO_INDENT); + writeln_field(writer, "token_id", hex::encode(response.token_id.0), ZERO_INDENT); + writeln_field( + writer, + "required_confirmations", + response.required_confirmations, + ZERO_INDENT, + ); + + if response.balances.is_empty() { + writeln_field(writer, "balances(spend:unspend)", "none", ZERO_INDENT); + return Ok(()); + } + let mut term_table = term_table_blank(TableStyle::empty(), false, false, false); + writeln_field(writer, "balances(spend:unspend)", "", ZERO_INDENT); + for (token, balance) in response.balances { + term_table.add_row(Row::new(vec![token, format_coin_balance(balance)?])) + } + writeln_safe_io!(writer, "{}", term_table.render()); + Ok(()) +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/eth.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/eth.rs new file mode 100644 index 0000000000..827f66a775 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/eth.rs @@ -0,0 +1,63 @@ +use anyhow::Result; +use std::io::Write; +use term_table::{row::Row, TableStyle}; + +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; + +use super::super::formatters::{term_table_blank, writeln_field, ZERO_INDENT}; +use super::{format_addr_infos, format_coin_balance, format_token_balances}; +use crate::rpc_data::eth::{Erc20InitResult, EthWithTokensActivationResult}; + +pub(in super::super) fn on_enable_erc20(writer: &mut dyn Write, response: Erc20InitResult) -> Result<()> { + writeln_field(writer, "platform_coin", response.platform_coin, ZERO_INDENT); + writeln_field( + writer, + "token_contract_address", + response.token_contract_address, + ZERO_INDENT, + ); + writeln_field( + writer, + "required_confirmations", + response.required_confirmations, + ZERO_INDENT, + ); + + if response.balances.is_empty() { + writeln_field(writer, "balances(spend:unspend)", "none", ZERO_INDENT); + return Ok(()); + } + let mut term_table = term_table_blank(TableStyle::empty(), false, false, false); + writeln_field(writer, "balances(spend:unspend)", "", ZERO_INDENT); + for (token, balance) in response.balances { + term_table.add_row(Row::new(vec![token, format_coin_balance(balance)?])) + } + writeln_safe_io!(writer, "{}", term_table.render()); + + Ok(()) +} + +pub(in super::super) fn on_enable_eth_with_tokens( + writer: &mut dyn Write, + response: EthWithTokensActivationResult, +) -> Result<()> { + writeln_field(writer, "current_block", response.current_block, ZERO_INDENT); + + if response.eth_addresses_infos.is_empty() { + writeln_field(writer, "eth_addresses_infos", "none", ZERO_INDENT); + } else { + writeln_field(writer, "eth_addresses_infos", "", ZERO_INDENT); + let addr_infos = format_addr_infos(response.eth_addresses_infos, format_coin_balance); + writeln_safe_io!(writer, "{}", addr_infos); + } + + if response.erc20_addresses_infos.is_empty() { + writeln_field(writer, "erc20_addresses_infos", "none", ZERO_INDENT); + } else { + writeln_field(writer, "erc20_addresses_infos", "", ZERO_INDENT); + let addr_infos = format_addr_infos(response.erc20_addresses_infos, format_token_balances); + writeln_safe_io!(writer, "{}", addr_infos); + } + + Ok(()) +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/tendermint.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/tendermint.rs new file mode 100644 index 0000000000..c9dc912e5b --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/tendermint.rs @@ -0,0 +1,66 @@ +use anyhow::Result; +use itertools::Itertools; +use std::io::Write; +use term_table::{row::Row, TableStyle}; + +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; + +use super::super::formatters::{term_table_blank, write_field_option, writeln_field, ZERO_INDENT}; +use super::{format_coin_balance, format_token_balances}; +use crate::rpc_data::tendermint::{TendermintActivationResult, TendermintTokenInitResult}; + +pub(in super::super) fn on_enable_tendermint( + writer: &mut dyn Write, + response: TendermintActivationResult, +) -> Result<()> { + writeln_field(writer, "ticker", response.ticker, ZERO_INDENT); + writeln_field(writer, "address", response.address, ZERO_INDENT); + writeln_field(writer, "current_block", response.current_block, ZERO_INDENT); + write_field_option( + writer, + "balance", + response + .balance + .map(format_coin_balance) + .map(|result| result.unwrap_or_else(|_| "error".to_string())), + ZERO_INDENT, + ); + + write_field_option( + writer, + "token_balances", + response + .tokens_balances + .map(format_token_balances) + .map(|result| result.unwrap_or_else(|_| "error".to_string())), + ZERO_INDENT, + ); + + write_field_option( + writer, + "tokens_tickers", + response.tokens_tickers.map(|tickers| tickers.iter().join(", ")), + ZERO_INDENT, + ); + Ok(()) +} + +pub(in super::super) fn on_enable_tendermint_token( + writer: &mut dyn Write, + response: TendermintTokenInitResult, +) -> Result<()> { + writeln_field(writer, "platform_coin", response.platform_coin, ZERO_INDENT); + + if response.balances.is_empty() { + writeln_field(writer, "balances(spend:unspend)", "none", ZERO_INDENT); + return Ok(()); + } + let mut term_table = term_table_blank(TableStyle::empty(), false, false, false); + writeln_field(writer, "balances(spend:unspend)", "", ZERO_INDENT); + for (token, balance) in response.balances { + term_table.add_row(Row::new(vec![token, format_coin_balance(balance)?])) + } + writeln_safe_io!(writer, "{}", term_table.render()); + + Ok(()) +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/z_coin.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/z_coin.rs new file mode 100644 index 0000000000..e132fc1a4a --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/activation/z_coin.rs @@ -0,0 +1,226 @@ +use anyhow::{anyhow, Result}; +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; +use itertools::Itertools; +use std::io::Write; +use term_table::row::Row; +use term_table::TableStyle; + +use common::log::error; +use mm2_rpc::data::legacy::Status; + +use super::super::activation::format_coin_balance; +use super::super::formatters::{term_table_blank, writeln_field, ZERO_INDENT}; +use crate::error_anyhow; +use crate::komodefi_proc::response_handler::formatters::{format_bytes, write_field_option, COMMON_INDENT}; +use crate::rpc_data::activation::zcoin::{CheckPointBlockInfo, CoinBalanceReport, CoinProtocol, HDAddressBalance, + HDWalletBalance, IguanaWalletBalance, InitStandaloneCoinError, + RpcDerivationPath, StandardHDPathToCoin, ZCoinStatus, ZcoinActivationResult, + ZcoinAwaitingStatus, ZcoinConsensusParams, ZcoinInProgressStatus}; +use crate::rpc_data::activation::{InitRpcTaskResponse, TaskId}; +use crate::rpc_data::CancelRpcTaskError; + +pub(in super::super) fn on_enable_zcoin(writer: &mut dyn Write, response: InitRpcTaskResponse) -> TaskId { + writeln_field(writer, "Enabling zcoin started, task_id", response.task_id, ZERO_INDENT); + response.task_id +} + +pub(in super::super) fn on_enable_zcoin_status(writer: &mut dyn Write, response: ZCoinStatus) -> Result { + match response { + ZCoinStatus::Ok(ok_status) => on_enable_zcoin_status_ok(writer, ok_status), + ZCoinStatus::InProgress(progress_status) => on_enable_zcoin_status_progress(writer, progress_status), + ZCoinStatus::UserActionRequired(user_action_status) => { + on_enable_zcoin_status_user_action(writer, user_action_status) + }, + ZCoinStatus::Error(error_status) => on_enable_zcoin_status_error(writer, error_status), + } +} + +fn on_enable_zcoin_status_ok(writer: &mut dyn Write, response: ZcoinActivationResult) -> Result { + writeln_field(writer, "status", "OK", ZERO_INDENT); + writeln_field(writer, "current_block", response.current_block, ZERO_INDENT); + writeln_field(writer, "ticker", response.ticker, ZERO_INDENT); + match response.wallet_balance { + CoinBalanceReport::Iguana(balance) => write_iguana_balance(writer, balance)?, + CoinBalanceReport::HD(balance) => write_hd_balance(writer, balance)?, + } + Ok(false) +} + +fn on_enable_zcoin_status_progress(writer: &mut dyn Write, response: ZcoinInProgressStatus) -> Result { + writeln_field(writer, "In progress", response, ZERO_INDENT); + Ok(true) +} + +fn on_enable_zcoin_status_error(writer: &mut dyn Write, response: InitStandaloneCoinError) -> Result { + match response { + InitStandaloneCoinError::UnexpectedCoinProtocol { ticker, protocol } => { + writeln_field(writer, "ticker", ticker, ZERO_INDENT); + write_coin_protocol(writer, protocol) + }, + response => writeln_field(writer, "Error", response, ZERO_INDENT), + }; + + Ok(false) +} + +fn on_enable_zcoin_status_user_action(writer: &mut dyn Write, response: ZcoinAwaitingStatus) -> Result { + writeln_field(writer, "Awaiting for action", response, ZERO_INDENT); + Ok(true) +} + +pub(in super::super) fn on_enable_zcoin_canceled(writer: &mut dyn Write, response: Status) { + writeln_safe_io!(writer, "canceled: {}", response); +} + +pub(in super::super) fn on_enable_zcoin_cancel_error(writer: &mut dyn Write, error: CancelRpcTaskError) { + writeln_field(writer, "rpc task error", error, ZERO_INDENT); +} + +fn write_coin_protocol(writer: &mut dyn Write, protocol: CoinProtocol) { + let CoinProtocol::ZHTLC(protocol) = protocol; + write_consensus_params(writer, protocol.consensus_params); + if let Some(check_point_block) = protocol.check_point_block { + write_check_point_block(writer, check_point_block); + } + if let Some(z_derivation_path) = protocol.z_derivation_path { + write_z_deriavation_path(writer, z_derivation_path); + } +} + +fn write_consensus_params(writer: &mut dyn Write, params: ZcoinConsensusParams) { + writeln_field(writer, "consensus_params", "", ZERO_INDENT); + writeln_field( + writer, + "overwinter_activation_height", + params.overwinter_activation_height, + COMMON_INDENT, + ); + writeln_field( + writer, + "sapling_activation_height", + params.sapling_activation_height, + COMMON_INDENT, + ); + write_field_option( + writer, + "blossom_activation_height", + params.blossom_activation_height, + COMMON_INDENT, + ); + write_field_option( + writer, + "heartwood_activation_height", + params.heartwood_activation_height, + COMMON_INDENT, + ); + write_field_option( + writer, + "canopy_activation_height", + params.canopy_activation_height, + COMMON_INDENT, + ); + writeln_field(writer, "coin_type", params.coin_type, COMMON_INDENT); + writeln_field( + writer, + "hrp_sapling_extended_spending_key", + params.hrp_sapling_extended_spending_key, + COMMON_INDENT, + ); + writeln_field( + writer, + "hrp_sapling_extended_full_viewing_key", + params.hrp_sapling_extended_full_viewing_key, + COMMON_INDENT, + ); + writeln_field( + writer, + "hrp_sapling_payment_address", + params.hrp_sapling_payment_address, + COMMON_INDENT, + ); + writeln_field( + writer, + "b58_pubkey_address_prefix", + hex::encode(params.b58_pubkey_address_prefix), + COMMON_INDENT, + ); + writeln_field( + writer, + "b58_script_address_prefix", + hex::encode(params.b58_script_address_prefix), + COMMON_INDENT, + ); +} + +fn write_check_point_block(writer: &mut dyn Write, check_point_block: CheckPointBlockInfo) { + writeln_field(writer, "check_point_block", "", ZERO_INDENT); + writeln_field(writer, "height", check_point_block.height, COMMON_INDENT); + writeln_field(writer, "hash", check_point_block.hash, COMMON_INDENT); + writeln_field(writer, "timestamp", check_point_block.time, COMMON_INDENT); + writeln_field( + writer, + "sapling_tree", + format_bytes(check_point_block.sapling_tree), + COMMON_INDENT, + ); +} + +fn write_z_deriavation_path(writer: &mut dyn Write, z_derivation_path: StandardHDPathToCoin) { + writeln_field(writer, "z_derivation_path", "", ZERO_INDENT); + + format!( + "Bip32Child: {{Value: {}, Child: {{ Value: {} , Child: {} }} }}", + z_derivation_path.value.purpose, z_derivation_path.child.value.number, z_derivation_path.child.child + ); +} + +fn write_iguana_balance(writer: &mut dyn Write, balance: IguanaWalletBalance) -> Result<()> { + writeln_field(writer, "iguana wallet", "", ZERO_INDENT); + writeln_field(writer, "address", balance.address, COMMON_INDENT); + writeln_field(writer, "balance", format_coin_balance(balance.balance)?, COMMON_INDENT); + Ok(()) +} + +fn write_hd_balance(writer: &mut dyn Write, balance: HDWalletBalance) -> Result<()> { + writeln_field(writer, "hd wallet", "", ZERO_INDENT); + if balance.accounts.is_empty() { + writeln_field(writer, "accounts", "none", COMMON_INDENT); + } else { + writeln_field(writer, "accounts", "", COMMON_INDENT); + let mut term_table = term_table_blank(TableStyle::thin(), false, false, false); + term_table.add_row(Row::new(["index", "derivation", "balance(spend:unspend)", "adresses"])); + + for account in balance.accounts { + term_table.add_row(Row::new([ + account.account_index.to_string(), + format_derivation_path(account.derivation_path), + format_coin_balance(account.total_balance)?, + format_hd_addresses(account.addresses)?, + ])) + } + writeln_safe_io!(writer, "{}", term_table.render()); + }; + Ok(()) +} + +fn format_hd_addresses(addresses: Vec) -> Result { + let mut buff: Vec = vec![]; + let writer: &mut dyn Write = &mut buff; + for address in addresses { + writeln_field(writer, "address", address.address, ZERO_INDENT); + writeln_field(writer, "balance", format_coin_balance(address.balance)?, ZERO_INDENT); + writeln_field( + writer, + "der path", + format_derivation_path(address.derivation_path), + ZERO_INDENT, + ); + writeln_field(writer, "chain", address.chain.to_string(), ZERO_INDENT); + writeln_field(writer, "", "", ZERO_INDENT); + } + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to format hd_address: {error}")) +} + +fn format_derivation_path(derivation_path: RpcDerivationPath) -> String { + derivation_path.0.path.iter().map(|v| v.0).join(",") +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/best_orders.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/best_orders.rs new file mode 100644 index 0000000000..990be1d163 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/best_orders.rs @@ -0,0 +1,75 @@ +use anyhow::Result; +use itertools::Itertools; +use std::collections::{HashMap, HashSet}; +use std::io::Write; +use term_table::{row::Row, + table_cell::{Alignment, TableCell}, + TableStyle}; + +use common::{write_safe::io::WriteSafeIO, write_safe_io}; +use mm2_rpc::data::version2::BestOrdersV2Response; + +use super::formatters::{format_confirmation_settings, format_ratio, term_table_blank, COMMON_PRECISION}; + +pub(super) fn on_best_orders( + writer: &mut dyn Write, + response: BestOrdersV2Response, + show_orig_tickers: bool, +) -> Result<()> { + let mut term_table = term_table_blank(TableStyle::thin(), false, false, false); + term_table.add_row(best_orders_header_row()); + + for (coin, data) in response.orders.iter().sorted_by_key(|p| p.0) { + let coin = if show_orig_tickers { + get_original_ticker(coin, &response.original_tickers) + } else { + coin.clone() + }; + term_table.add_row(Row::new(vec![TableCell::new_with_alignment(coin, 7, Alignment::Left)])); + for order in data.iter().sorted_by_key(|o| o.uuid) { + term_table.add_row(Row::new(vec![ + TableCell::new(if order.is_mine { "*" } else { "" }), + TableCell::new(format_ratio(&order.price.rational, COMMON_PRECISION)?), + TableCell::new(order.uuid), + TableCell::new(format!( + "{}:{}", + format_ratio(&order.base_min_volume.rational, COMMON_PRECISION)?, + format_ratio(&order.base_max_volume.rational, COMMON_PRECISION)? + )), + TableCell::new(format!( + "{}:{}", + format_ratio(&order.rel_min_volume.rational, COMMON_PRECISION)?, + format_ratio(&order.rel_max_volume.rational, COMMON_PRECISION)? + )), + TableCell::new(&order.address), + TableCell::new( + &order + .conf_settings + .as_ref() + .map_or_else(|| "none".to_string(), format_confirmation_settings), + ), + ])); + } + } + write_safe_io!(writer, "{}", term_table.render()); + + Ok(()) +} + +fn best_orders_header_row() -> Row<'static> { + Row::new(vec![ + TableCell::new(""), + TableCell::new("Price"), + TableCell::new("Uuid"), + TableCell::new("Base vol(min:max)"), + TableCell::new("Rel vol(min:max)"), + TableCell::new("Address"), + TableCell::new("Confirmation"), + ]) +} + +fn get_original_ticker(coin: &String, original_tickers: &HashMap>) -> String { + original_tickers + .get(coin) + .map_or_else(|| coin.clone(), |set| set.iter().join(", ")) +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/formatters.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/formatters.rs new file mode 100644 index 0000000000..689888e334 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/formatters.rs @@ -0,0 +1,368 @@ +use anyhow::{anyhow, Result}; +use chrono::{TimeZone, Utc}; +use itertools::Itertools; +use std::collections::HashMap; +use std::fmt::{Debug, Display}; +use std::io::Write; +use term_table::{row::Row, table_cell::TableCell, Table as TermTable, TableStyle}; +use uuid::Uuid; + +use common::log::error; +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; +use mm2_number::bigdecimal::{ToPrimitive, Zero}; +use mm2_rpc::data::legacy::{HistoricalOrder, MakerMatchForRpc, MakerOrderForRpc, MakerReservedForRpc, MatchBy, + OrderConfirmationsSettings, TakerMatchForRpc, TakerOrderForRpc}; +use rpc::v1::types::Bytes; + +use super::super::SmartFractPrecision; +use super::macros::{write_base_rel, write_confirmation_settings, write_connected}; +use super::smart_fraction_fmt::SmartFractionFmt; +use crate::logging::error_anyhow; + +pub(super) const COMMON_INDENT: usize = 20; +pub(super) const COMMON_PRECISION: SmartFractPrecision = (2, 5); +const NESTED_INDENT: usize = 26; +pub(super) const ZERO_INDENT: usize = 0; + +pub(super) fn on_maker_order_response(writer: &mut dyn Write, order: MakerOrderForRpc) -> Result<()> { + writeln_field(writer, "Maker order", "", ZERO_INDENT); + write_maker_order(writer, &order)?; + write_maker_matches(writer, &order.matches)?; + writeln_safe_io!(writer, ""); + Ok(()) +} + +pub(super) fn write_maker_order(writer: &mut dyn Write, order: &MakerOrderForRpc) -> Result<()> { + writeln_field(writer, "base", &order.base, COMMON_INDENT); + writeln_field(writer, "rel", &order.rel, COMMON_INDENT); + writeln_field( + writer, + "price", + format_ratio(&order.price_rat, COMMON_PRECISION)?, + COMMON_INDENT, + ); + writeln_field(writer, "uuid", order.uuid, COMMON_INDENT); + writeln_field( + writer, + "created at", + format_datetime_msec(order.created_at)?, + COMMON_INDENT, + ); + if let Some(updated_at) = order.updated_at { + writeln_field(writer, "updated at", format_datetime_msec(updated_at)?, COMMON_INDENT); + } + writeln_field( + writer, + "max_base_vol", + format_ratio(&order.max_base_vol_rat, COMMON_PRECISION)?, + COMMON_INDENT, + ); + writeln_field( + writer, + "min_base_vol", + format_ratio(&order.min_base_vol_rat, COMMON_PRECISION)?, + COMMON_INDENT, + ); + writeln_field( + writer, + "swaps", + if order.started_swaps.is_empty() { + "empty".to_string() + } else { + order.started_swaps.iter().join(", ") + }, + COMMON_INDENT, + ); + if let Some(ref conf_settings) = order.conf_settings { + writeln_field( + writer, + "conf_settings", + format_confirmation_settings(conf_settings), + COMMON_INDENT, + ); + } + if let Some(ref changes_history) = order.changes_history { + writeln_field( + writer, + "changes_history", + changes_history + .iter() + .map(|val| format_historical_changes(val, ", ").unwrap_or_else(|_| "error".to_string())) + .join("; "), + COMMON_INDENT, + ); + } + Ok(()) +} + +pub(super) fn write_maker_matches(writer: &mut dyn Write, matches: &HashMap) -> Result<()> { + if matches.is_empty() { + return Ok(()); + } + writeln_field(writer, "matches", "", COMMON_INDENT); + for (uuid, m) in matches { + write_maker_match(writer, uuid, m)?; + writeln_safe_io!(writer, ""); + } + Ok(()) +} + +pub(super) fn write_maker_match(writer: &mut dyn Write, uuid: &Uuid, m: &MakerMatchForRpc) -> Result<()> { + let (req, reserved, connect, connected) = (&m.request, &m.reserved, &m.connect, &m.connected); + writeln_field(writer, "uuid", uuid, NESTED_INDENT); + writeln_field(writer, "req.uuid", req.uuid, NESTED_INDENT); + write_base_rel!(writer, req, NESTED_INDENT); + writeln_field( + writer, + "req.match_by", + format_match_by(&req.match_by, ", "), + NESTED_INDENT, + ); + writeln_field(writer, "req.action", &req.action, NESTED_INDENT); + write_confirmation_settings!(writer, req, NESTED_INDENT); + writeln_field( + writer, + "req.(sender, dest)", + format!("{},{}", req.sender_pubkey, req.dest_pub_key), + NESTED_INDENT, + ); + write_maker_reserved_for_rpc(writer, reserved)?; + if let Some(ref connected) = connected { + write_connected!(writer, connected, NESTED_INDENT); + } + if let Some(ref connect) = connect { + write_connected!(writer, connect, NESTED_INDENT); + } + writeln_field( + writer, + "last_updated", + format_datetime_msec(m.last_updated)?, + NESTED_INDENT, + ); + Ok(()) +} + +fn write_maker_reserved_for_rpc(writer: &mut dyn Write, reserved: &MakerReservedForRpc) -> Result<()> { + write_base_rel!(writer, reserved, NESTED_INDENT); + writeln_field( + writer, + "reserved.(taker, maker)", + format!("{},{}", reserved.taker_order_uuid, reserved.maker_order_uuid), + NESTED_INDENT, + ); + writeln_field( + writer, + "reserved.(sender, dest)", + format!("{},{}", reserved.sender_pubkey, reserved.dest_pub_key), + NESTED_INDENT, + ); + write_confirmation_settings!(writer, reserved, NESTED_INDENT); + Ok(()) +} + +pub(super) fn taker_order_header_row() -> Row<'static> { + Row::new(vec![ + TableCell::new("action\nbase(vol),rel(vol)"), + TableCell::new("uuid, sender, dest"), + TableCell::new("type,created_at\nconfirmation"), + TableCell::new("match_by"), + TableCell::new("base,rel\norderbook ticker"), + TableCell::new("cancellable"), + ]) +} + +pub(super) fn taker_order_rows(taker_order: &TakerOrderForRpc) -> Result>> { + let req = &taker_order.request; + let mut rows = vec![Row::new(vec![ + TableCell::new(format!( + "{}\n{}({}),{}({})", + req.action, + req.base, + format_ratio(&req.base_amount, COMMON_PRECISION)?, + req.rel, + format_ratio(&req.rel_amount, COMMON_PRECISION)? + )), + TableCell::new(format!("{}\n{}\n{}", req.uuid, req.sender_pubkey, req.dest_pub_key)), + TableCell::new(format!( + "{}\n{}\n{}", + taker_order.order_type, + format_datetime_msec(taker_order.created_at)?, + req.conf_settings + .as_ref() + .map_or_else(|| "none".to_string(), format_confirmation_settings), + )), + TableCell::new(format_match_by(&req.match_by, "\n")), + TableCell::new(format!( + "{}\n{}", + taker_order + .base_orderbook_ticker + .as_ref() + .map_or_else(|| "none".to_string(), String::clone), + taker_order + .rel_orderbook_ticker + .as_ref() + .map_or_else(|| "none".to_string(), String::clone) + )), + TableCell::new(taker_order.cancellable), + ])]; + rows.append(get_matches_rows(&taker_order.matches, 6, write_taker_match)?.as_mut()); + Ok(rows) +} + +pub(super) fn get_matches_rows Result<()>>( + matches: &HashMap, + collspan: usize, + write_match: F, +) -> Result>> { + let mut rows = vec![]; + if matches.is_empty() { + return Ok(rows); + } + rows.push(Row::new(vec![TableCell::new_with_col_span("matches", collspan)])); + for (uuid, m) in matches.iter() { + let mut buff = vec![]; + write_match(&mut buff as &mut dyn Write, uuid, m)?; + rows.push(Row::new(vec![TableCell::new_with_col_span( + String::from_utf8(buff) + .map_err(|error| error_anyhow!("Failed to get string from taker order matches_str buffer: {error}"))?, + collspan, + )])); + } + Ok(rows) +} + +pub(super) fn write_taker_match(writer: &mut dyn Write, uuid: &Uuid, m: &TakerMatchForRpc) -> Result<()> { + let (reserved, connect, connected) = (&m.reserved, &m.connect, &m.connected); + writeln_field(writer, "uuid", uuid, NESTED_INDENT); + write_maker_reserved_for_rpc(writer, reserved)?; + let last_updated = if m.last_updated.is_zero() { + "none".to_string() + } else { + format_datetime_msec(m.last_updated)? + }; + writeln_field(writer, "last_updated", last_updated, NESTED_INDENT); + write_connected!(writer, connect, NESTED_INDENT); + if let Some(ref connected) = connected { + write_connected!(writer, connected, NESTED_INDENT); + } + Ok(()) +} + +pub(super) fn format_historical_changes(historical_order: &HistoricalOrder, delimiter: &str) -> Result { + let mut result = vec![]; + + if let Some(ref min_base_vol) = historical_order.min_base_vol { + result.push(format!( + "min_base_vol: {}", + format_ratio(min_base_vol, COMMON_PRECISION)?, + )) + } + if let Some(ref max_base_vol) = historical_order.max_base_vol { + result.push(format!( + "max_base_vol: {}", + format_ratio(max_base_vol, COMMON_PRECISION)?, + )) + } + if let Some(ref price) = historical_order.price { + result.push(format!("price: {}", format_ratio(price, COMMON_PRECISION)?)); + } + if let Some(updated_at) = historical_order.updated_at { + result.push(format!("updated_at: {}", format_datetime_msec(updated_at)?)); + } + if let Some(ref conf_settings) = historical_order.conf_settings { + result.push(format!( + "conf_settings: {}", + format_confirmation_settings(conf_settings), + )); + } + Ok(result.join(delimiter)) +} + +pub(super) fn term_table_blank( + style: TableStyle, + sep_row: bool, + bottom_border: bool, + top_border: bool, +) -> TermTable<'static> { + let mut term_table = TermTable::new(); + term_table.style = style; + term_table.separate_rows = sep_row; + term_table.has_bottom_boarder = bottom_border; + term_table.has_top_boarder = top_border; + term_table +} + +pub(super) fn format_match_by(match_by: &MatchBy, delimiter: &str) -> String { + match match_by { + MatchBy::Any => "Any".to_string(), + MatchBy::Orders(orders) => orders.iter().sorted().join(delimiter), + MatchBy::Pubkeys(pubkeys) => pubkeys.iter().sorted().join(delimiter), + } +} + +pub(super) fn format_datetime_msec(datetime_msec: u64) -> Result { format_datetime_sec(datetime_msec / 1000) } + +pub(super) fn format_datetime_sec(datetime: u64) -> Result { + let datetime = Utc + .timestamp_opt(datetime as i64, 0) + .single() + .ok_or_else(|| error_anyhow!("Failed to get datetime formatted datetime"))?; + Ok(format!("{}", datetime.format("%y-%m-%d %H:%M:%S"))) +} + +pub(super) fn format_ratio(rational: &T, precision: SmartFractPrecision) -> Result { + format_f64( + rational + .to_f64() + .ok_or_else(|| error_anyhow!("Failed to cast rational to f64: {rational:?}"))?, + precision, + ) +} + +pub(super) fn format_f64(rational: f64, fract_precision: SmartFractPrecision) -> Result { + Ok(SmartFractionFmt::new(&fract_precision, rational) + .map_err(|_| error_anyhow!("Failed to create smart_fraction_fmt"))? + .to_string()) +} + +pub(super) fn format_confirmation_settings(settings: &OrderConfirmationsSettings) -> String { + format!( + "{},{}:{},{}", + settings.base_confs, settings.base_nota, settings.rel_confs, settings.rel_nota + ) +} + +pub(super) fn write_sequence + Itertools>( + writer: &mut dyn Write, + header: &str, + mut iter: I, + indent: usize, +) { + writeln_field( + writer, + header, + if iter.len() == 0 { + "none".to_string() + } else { + iter.join(", ") + }, + indent, + ) +} + +pub(super) fn writeln_field(writer: &mut dyn Write, header: H, value: T, indent: usize) { + writeln_safe_io!(writer, "{:>width$}: {}", header, value, width = indent) +} + +pub(super) fn write_field_option( + writer: &mut dyn Write, + header: H, + value: Option, + indent: usize, +) { + if let Some(ref value) = value { + writeln_safe_io!(writer, "{:>width$}: {}", header, value, width = indent) + } +} + +pub(super) fn format_bytes(bytes: Bytes) -> String { hex::encode(bytes.as_slice()) } diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/macros.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/macros.rs new file mode 100644 index 0000000000..0d58891742 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/macros.rs @@ -0,0 +1,49 @@ +#[macro_export] +macro_rules! write_confirmation_settings { + ($writer:expr, $host:ident, $width:ident) => { + if $host.conf_settings.is_some() { + let output = format_confirmation_settings($host.conf_settings.as_ref().unwrap()); + writeln_field($writer, concat!(stringify!($host), ".conf_settings"), output, $width) + } + }; +} + +#[macro_export] +macro_rules! write_base_rel { + ($writer:ident, $host:expr, $width:ident) => { + writeln_field( + $writer, + concat!(stringify!($host), ".(base,rel)"), + format!( + "{}({}), {}({})", + $host.base, + format_ratio(&$host.base_amount, COMMON_PRECISION) + .map_err(|error| error_anyhow!("Failed to format base_amount: {error}"))?, + $host.rel, + format_ratio(&$host.rel_amount, COMMON_PRECISION) + .map_err(|error| error_anyhow!("Failed to format rel_amount: {error}"))?, + ), + $width, + ); + }; +} + +#[macro_export] +macro_rules! write_connected { + ($writer:ident, $connected:expr, $width:ident) => { + writeln_field( + $writer, + concat!(stringify!($connected), ".(taker,maker)"), + format!("{},{}", $connected.taker_order_uuid, $connected.maker_order_uuid), + $width, + ); + writeln_field( + $writer, + concat!(stringify!($connected), ".(sender, dest)"), + format!("{},{}", $connected.sender_pubkey, $connected.dest_pub_key), + $width, + ); + }; +} + +pub(super) use {write_base_rel, write_confirmation_settings, write_connected}; diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/message_signing.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/message_signing.rs new file mode 100644 index 0000000000..0bfbb00f22 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/message_signing.rs @@ -0,0 +1,25 @@ +use std::io::Write; + +use super::formatters::{writeln_field, ZERO_INDENT}; +use crate::rpc_data::message_signing::{SignatureError, SignatureResponse, VerificationError, VerificationResponse}; + +pub(super) fn on_sign_message(writer: &mut dyn Write, response: SignatureResponse) { + writeln_field(writer, "signature", response.signature, ZERO_INDENT); +} + +pub(super) fn on_signature_error(writer: &mut dyn Write, error: SignatureError) { + writeln_field(writer, "signature error", error, ZERO_INDENT); +} + +pub(super) fn on_verify_message(writer: &mut dyn Write, response: VerificationResponse) { + writeln_field( + writer, + "is valid", + if response.is_valid { "valid" } else { "invalid" }, + ZERO_INDENT, + ); +} + +pub(super) fn on_verification_error(writer: &mut dyn Write, error: VerificationError) { + writeln_field(writer, "verification error", error, ZERO_INDENT); +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/my_orders.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/my_orders.rs new file mode 100644 index 0000000000..27bb2eccf4 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/my_orders.rs @@ -0,0 +1,269 @@ +use anyhow::{anyhow, Result}; +use itertools::Itertools; +use std::collections::HashMap; +use std::io::Write; +use term_table::{row::Row, table_cell::TableCell, TableStyle}; +use uuid::Uuid; + +use common::log::error; +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; +use mm2_rpc::data::legacy::{MakerOrderForMyOrdersRpc, Mm2RpcResult, MyOrdersResponse, TakerOrderForRpc}; + +use super::formatters::{format_confirmation_settings, format_datetime_msec, format_historical_changes, format_ratio, + get_matches_rows, taker_order_header_row, taker_order_rows, term_table_blank, + write_maker_match, writeln_field, COMMON_INDENT, COMMON_PRECISION}; +use crate::logging::error_anyhow; + +pub(super) fn on_my_orders(writer: &mut dyn Write, response: Mm2RpcResult) -> Result<()> { + let result = response.result; + writeln_safe_io!(writer, "{}", format_taker_orders_table(&result.taker_orders)?); + writeln_safe_io!(writer, "{}", format_maker_orders_table(&result.maker_orders)?); + Ok(()) +} + +fn format_taker_orders_table(taker_orders: &HashMap) -> Result { + let mut buff: Vec = vec![]; + let writer: &mut dyn Write = &mut buff; + if taker_orders.is_empty() { + writeln_field(writer, "Taker orders", "empty", COMMON_INDENT); + } else { + writeln_field(writer, "Taker orders", "", COMMON_INDENT); + let mut table = term_table_blank(TableStyle::thin(), false, false, false); + table.add_row(taker_order_header_row()); + for (_, taker_order) in taker_orders.iter().sorted_by_key(|(_, o)| o.created_at) { + table.rows.append(taker_order_rows(taker_order)?.as_mut()); + } + write_safe_io!(writer, "{}", table.render()); + } + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to format maker orders table: {error}")) +} + +fn format_maker_orders_table(maker_orders: &HashMap) -> Result { + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + + if maker_orders.is_empty() { + writeln_field(writer, "Maker orders", "empty", COMMON_INDENT); + } else { + writeln_field(writer, "Maker orders", "", COMMON_INDENT); + let mut table = term_table_blank(TableStyle::thin(), false, false, false); + table.add_row(maker_order_for_my_orders_header_row()); + + for (_, maker_order) in maker_orders.iter().sorted_by_key(|(_, o)| o.order.created_at) { + table.rows.append(maker_order_for_my_orders_row(maker_order)?.as_mut()); + } + write_safe_io!(writer, "{}", table.render()); + } + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to format maker orders table: {error}")) +} + +fn maker_order_for_my_orders_header_row() -> Row<'static> { + Row::new(vec![ + TableCell::new("base,rel"), + TableCell::new("price"), + TableCell::new("uuid"), + TableCell::new("created at,\nupdated at"), + TableCell::new("min base vol,\nmax base vol"), + TableCell::new("cancellable"), + TableCell::new("available\namount"), + TableCell::new("swaps"), + TableCell::new("conf_settings"), + TableCell::new("history changes"), + ]) +} + +fn maker_order_for_my_orders_row(maker_order: &MakerOrderForMyOrdersRpc) -> Result> { + let order = &maker_order.order; + let mut rows = vec![Row::new(vec![ + TableCell::new(format!("{},{}", order.base, order.rel)), + TableCell::new(format_ratio(&order.price_rat, COMMON_PRECISION)?), + TableCell::new(order.uuid), + TableCell::new(format!( + "{},\n{}", + format_datetime_msec(order.created_at)?, + order.updated_at.map_or(Ok("".to_string()), format_datetime_msec)? + )), + TableCell::new(format!( + "{},\n{}", + format_ratio(&order.min_base_vol_rat, COMMON_PRECISION)?, + format_ratio(&order.max_base_vol_rat, COMMON_PRECISION)? + )), + TableCell::new(maker_order.cancellable), + TableCell::new(format_ratio(&maker_order.available_amount, COMMON_PRECISION)?), + TableCell::new(if order.started_swaps.is_empty() { + "empty".to_string() + } else { + order.started_swaps.iter().join(",\n") + }), + TableCell::new( + order + .conf_settings + .as_ref() + .map_or_else(|| "none".to_string(), format_confirmation_settings), + ), + TableCell::new(order.changes_history.as_ref().map_or_else( + || "none".to_string(), + |val| { + val.iter() + .map(|val| format_historical_changes(val, "\n").unwrap_or_else(|_| "error".to_string())) + .join("\n") + }, + )), + ])]; + rows.append(get_matches_rows(&order.matches, 10, write_maker_match)?.as_mut()); + Ok(rows) +} + +#[cfg(test)] +mod test { + use rpc::v1::types::H256 as H256Json; + use std::collections::{HashMap, HashSet}; + use std::str::FromStr; + use uuid::Uuid; + + use mm2_number::bigdecimal::FromPrimitive; + use mm2_number::{BigDecimal, BigRational}; + use mm2_rpc::data::legacy::{HistoricalOrder, MakerMatchForRpc, MakerOrderForMyOrdersRpc, MakerOrderForRpc, + MakerReservedForRpc, MatchBy, OrderConfirmationsSettings, TakerAction, + TakerRequestForRpc}; + + use super::format_maker_orders_table; + + #[test] + fn test_print_maker_orders_with_matches() { + let taker_request = TakerRequestForRpc { + uuid: Uuid::from_str("d9e1aaf6-eb5c-4550-a1d3-15bf4dc8727c").unwrap(), + base: "DFG".to_string(), + rel: "GGG".to_string(), + base_amount: BigDecimal::from_f64(0.0023).unwrap(), + base_amount_rat: BigRational::from_f64(0.0023).unwrap(), + rel_amount: BigDecimal::from_f64(0.11).unwrap(), + rel_amount_rat: BigRational::from_f64(0.11).unwrap(), + action: TakerAction::Sell, + method: "deprecated".to_string(), + sender_pubkey: H256Json::from_str("15d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732") + .unwrap(), + dest_pub_key: H256Json::from_str("0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d0447") + .unwrap(), + match_by: MatchBy::Orders(HashSet::from([ + Uuid::from_str("d9e1aaf6-eb5c-4550-a1d3-15bf4dc8727e").unwrap(), + Uuid::from_str("d9e1aaf6-eb5c-4550-a1d3-15bf4dc8727d").unwrap(), + ])), + conf_settings: Some(OrderConfirmationsSettings { + base_confs: 1, + base_nota: true, + rel_confs: 11, + rel_nota: false, + }), + }; + + let reserve = MakerReservedForRpc { + base: "TTT".to_string(), + rel: "GGG".to_string(), + base_amount: BigDecimal::from_f64(888.1).unwrap(), + base_amount_rat: BigRational::from_f64(888.1).unwrap(), + rel_amount: BigDecimal::from_f64(9921.1).unwrap(), + rel_amount_rat: BigRational::from_f64(9912.1).unwrap(), + taker_order_uuid: Uuid::from_str("a0e1aaf6-eb5c-4550-a1d3-15bf4dc8727d").unwrap(), + maker_order_uuid: Uuid::from_str("b1e1aaf6-eb5c-4550-a1d3-15bf4dc8727d").unwrap(), + sender_pubkey: H256Json::from_str("022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c8508346") + .unwrap(), + dest_pub_key: H256Json::from_str("022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c8508348") + .unwrap(), + conf_settings: Some(OrderConfirmationsSettings { + base_confs: 1, + base_nota: true, + rel_confs: 11, + rel_nota: false, + }), + method: "deprecated".to_string(), + }; + + let maker_match_for_rpc = MakerMatchForRpc { + request: taker_request, + reserved: reserve, + connect: None, + connected: None, + last_updated: 1223112311114, + }; + + let mut maker_order_matches = HashMap::new(); + maker_order_matches.insert( + Uuid::from_str("99e1aaf6-eb5c-4550-a1d3-15bf4dc8727d").unwrap(), + maker_match_for_rpc, + ); + + let hisorical_order = HistoricalOrder { + max_base_vol: BigRational::from_f64(775.123).take(), + min_base_vol: BigRational::from_f64(0.0004).take(), + price: BigRational::from_f64(0.12).take(), + updated_at: Some(22222222222), + conf_settings: None, + }; + + let maker_order_for_rpc = MakerOrderForRpc { + uuid: Uuid::from_str("99777af6-eb5c-4550-a1d3-15bf4dc8727d").unwrap(), + base: "AAA".to_string(), + rel: "BBB".to_string(), + price: BigDecimal::from_f64(11.22).unwrap(), + price_rat: BigRational::from_f64(11.22).unwrap(), + max_base_vol: BigDecimal::from_f64(10000.000003).unwrap(), + max_base_vol_rat: BigRational::from_f64(10000.000003).unwrap(), + min_base_vol: BigDecimal::from_f64(0.5).unwrap(), + min_base_vol_rat: BigRational::from_f64(0.5).unwrap(), + created_at: 1223112311114, + updated_at: Some(1223112311114), + matches: maker_order_matches, + started_swaps: vec![ + Uuid::from_str("8f4ebdec-4d86-467f-ba8e-94256783eb17").unwrap(), + Uuid::from_str("1efb18ab-2e0e-4511-9b9d-ea8fb9ec19ef").unwrap(), + ], + conf_settings: Some(OrderConfirmationsSettings { + base_confs: 87, + base_nota: true, + rel_confs: 78, + rel_nota: false, + }), + changes_history: Some(vec![hisorical_order]), + base_orderbook_ticker: Some("CCC".to_string()), + rel_orderbook_ticker: Some("DDD".to_string()), + }; + + let maker_order_for_rpc = MakerOrderForMyOrdersRpc { + order: maker_order_for_rpc, + cancellable: true, + available_amount: BigDecimal::from_f64(18828.12333).unwrap(), + }; + assert_eq!( + MAKER_WITH_MATCHES_OUT, + format_maker_orders_table(&HashMap::from([( + Uuid::from_str("1e94c6ca-a766-4c4f-b819-858ff1e4f107").unwrap(), + maker_order_for_rpc + )])) + .unwrap() + ); + } + + const MAKER_WITH_MATCHES_OUT: &str = " Maker orders: +│ base,rel │ price │ uuid │ created at, │ min base vol, │ cancellable │ available │ swaps │ conf_settings │ history changes │ +│ │ │ │ updated at │ max base vol │ │ amount │ │ │ │ +│ AAA,BBB │ 11.22 │ 99777af6-eb5c-4550-a1d3-15bf4dc8727d │ 08-10-04 09:25:11, │ 0.50, │ true │ 18828.12 │ 8f4ebdec-4d86-467f-ba8e-94256783eb17, │ 87,true:78,false │ min_base_vol: 0.00040 │ +│ │ │ │ 08-10-04 09:25:11 │ 10000.00 │ │ │ 1efb18ab-2e0e-4511-9b9d-ea8fb9ec19ef │ │ max_base_vol: 775.12 │ +│ │ │ │ │ │ │ │ │ │ price: 0.12 │ +│ │ │ │ │ │ │ │ │ │ updated_at: 70-09-15 04:50:22 │ +│ matches │ +│ uuid: 99e1aaf6-eb5c-4550-a1d3-15bf4dc8727d │ +│ req.uuid: d9e1aaf6-eb5c-4550-a1d3-15bf4dc8727c │ +│ req.(base,rel): DFG(0.0023), GGG(0.11) │ +│ req.match_by: d9e1aaf6-eb5c-4550-a1d3-15bf4dc8727d, d9e1aaf6-eb5c-4550-a1d3-15bf4dc8727e │ +│ req.action: Sell │ +│ req.conf_settings: 1,true:11,false │ +│ req.(sender, dest): 15d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732,0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d0447 │ +│ reserved.(base,rel): TTT(888.10), GGG(9921.10) │ +│ reserved.(taker, maker): a0e1aaf6-eb5c-4550-a1d3-15bf4dc8727d,b1e1aaf6-eb5c-4550-a1d3-15bf4dc8727d │ +│ reserved.(sender, dest): 022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c8508346,022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c8508348 │ +│ reserved.conf_settings: 1,true:11,false │ +│ last_updated: 08-10-04 09:25:11 │ +│ │ +"; +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/network.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/network.rs new file mode 100644 index 0000000000..fe263c9787 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/network.rs @@ -0,0 +1,99 @@ +use std::io::Write; + +use common::write_safe::io::WriteSafeIO; +use common::{write_safe_io, writeln_safe_io}; + +use super::formatters::{writeln_field, ZERO_INDENT}; +use crate::rpc_data::{GetGossipMeshResponse, GetGossipPeerTopicsResponse, GetGossipTopicPeersResponse, + GetMyPeerIdResponse, GetPeersInfoResponse, GetRelayMeshResponse}; + +pub(super) fn on_gossip_mesh(writer: &mut dyn Write, response: GetGossipMeshResponse) { + if response.is_empty() { + writeln_field(writer, "gossip_mesh", "empty", ZERO_INDENT); + return; + } + writeln_field(writer, "gossip_mesh", "", ZERO_INDENT); + for (k, v) in response { + writeln_field( + writer, + k, + if v.is_empty() { "empty".to_string() } else { v.join(",") }, + ZERO_INDENT, + ); + } +} + +pub(super) fn on_gossip_peer_topics(writer: &mut dyn Write, response: GetGossipPeerTopicsResponse) { + if response.is_empty() { + writeln_field(writer, "gossip_peer_topics", "empty", ZERO_INDENT); + return; + } + writeln_field(writer, "gossip_peer_topics", "", ZERO_INDENT); + for (key, value) in response { + writeln_field( + writer, + key, + if value.is_empty() { + "empty".to_string() + } else { + value.join(",") + }, + ZERO_INDENT, + ); + } +} + +pub(super) fn on_gossip_topic_peers(writer: &mut dyn Write, response: GetGossipTopicPeersResponse) { + if response.is_empty() { + writeln_field(writer, "gossip_topic_peers", "empty", ZERO_INDENT); + return; + } + writeln_field(writer, "gossip_topic_peers", "", ZERO_INDENT); + for (key, value) in response { + writeln_field( + writer, + key, + if value.is_empty() { + "empty".to_string() + } else { + value.join(",") + }, + ZERO_INDENT, + ); + } +} + +pub(super) fn on_relay_mesh(writer: &mut dyn Write, response: GetRelayMeshResponse) { + if response.is_empty() { + writeln_field(writer, "relay_mesh", "empty", ZERO_INDENT); + return; + } + writeln_field(writer, "relay_mesh", "", ZERO_INDENT); + for value in response { + writeln_safe_io!(writer, "{}", value); + } +} + +pub(super) fn on_my_peer_id(writer: &mut dyn Write, response: GetMyPeerIdResponse) { + writeln_safe_io!(writer, "{}", response) +} + +pub(super) fn on_peers_info(writer: &mut dyn Write, response: GetPeersInfoResponse) { + if response.is_empty() { + writeln_field(writer, "peers_info", "empty", ZERO_INDENT); + return; + } + writeln_field(writer, "peers_info", "", ZERO_INDENT); + for (key, value) in response { + writeln_field( + writer, + key, + if value.is_empty() { + "empty".to_string() + } else { + value.join(",") + }, + ZERO_INDENT, + ); + } +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/order_status.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/order_status.rs new file mode 100644 index 0000000000..cad3798667 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/order_status.rs @@ -0,0 +1,90 @@ +use anyhow::{anyhow, Result}; +use std::collections::HashMap; +use std::io::Write; +use uuid::Uuid; + +use common::log::error; +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; + +use mm2_rpc::data::legacy::{MakerOrderForMyOrdersRpc, OrderStatusResponse, TakerMatchForRpc, TakerOrderForRpc}; + +use super::formatters::{format_confirmation_settings, format_datetime_msec, format_match_by, format_ratio, + write_field_option, write_maker_matches, write_maker_order, write_taker_match, writeln_field, + COMMON_INDENT, COMMON_PRECISION}; +use super::macros::{write_base_rel, write_confirmation_settings}; +use crate::error_anyhow; + +pub(super) fn on_order_status(writer: &mut dyn Write, response: OrderStatusResponse) -> Result<()> { + match response { + OrderStatusResponse::Maker(maker_status) => write_maker_order_for_my_orders(writer, &maker_status), + OrderStatusResponse::Taker(taker_status) => write_taker_order(writer, &taker_status), + } +} + +fn write_maker_order_for_my_orders(writer: &mut dyn Write, maker_status: &MakerOrderForMyOrdersRpc) -> Result<()> { + let order = &maker_status.order; + write_maker_order(writer, order)?; + writeln_field(writer, "cancellable", maker_status.cancellable, COMMON_INDENT); + writeln_field( + writer, + "available_amount", + format_ratio(&maker_status.available_amount, COMMON_PRECISION)?, + COMMON_INDENT, + ); + write_maker_matches(writer, &order.matches)?; + writeln_safe_io!(writer, ""); + Ok(()) +} + +fn write_taker_order(writer: &mut dyn Write, taker_status: &TakerOrderForRpc) -> Result<()> { + let req = &taker_status.request; + writeln_field(writer, "uuid", req.uuid, COMMON_INDENT); + write_base_rel!(writer, req, COMMON_INDENT); + writeln_field(writer, "req.action", &req.action, COMMON_INDENT); + writeln_field( + writer, + "req.(sender, dest)", + format!("{}, {}", req.sender_pubkey, req.dest_pub_key), + COMMON_INDENT, + ); + writeln_field( + writer, + "req.match_by", + format_match_by(&req.match_by, ", "), + COMMON_INDENT, + ); + write_confirmation_settings!(writer, req, COMMON_INDENT); + writeln_field( + writer, + "created_at", + format_datetime_msec(taker_status.created_at)?, + COMMON_INDENT, + ); + writeln_field(writer, "order_type", &taker_status.order_type, COMMON_INDENT); + writeln_field(writer, "cancellable", taker_status.cancellable, COMMON_INDENT); + write_field_option( + writer, + "base_ob_ticker", + taker_status.base_orderbook_ticker.as_ref(), + COMMON_INDENT, + ); + write_field_option( + writer, + "rel_ob_ticker", + taker_status.rel_orderbook_ticker.as_ref(), + COMMON_INDENT, + ); + write_taker_matches(writer, &taker_status.matches)?; + Ok(()) +} + +fn write_taker_matches(writer: &mut dyn Write, matches: &HashMap) -> Result<()> { + if matches.is_empty() { + return Ok(()); + } + writeln_field(writer, "matches", "", COMMON_INDENT); + for (uuid, m) in matches { + write_taker_match(writer, uuid, m)?; + } + Ok(()) +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/orderbook.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/orderbook.rs new file mode 100644 index 0000000000..ff155c7d17 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/orderbook.rs @@ -0,0 +1,239 @@ +use anyhow::Result; +use itertools::Itertools; +use std::cmp::Ordering; +use std::fmt::{Display, Formatter}; +use std::io::Write; + +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; +use mm2_rpc::data::legacy::{AggregatedOrderbookEntry, OrderbookResponse}; + +use super::formatters::{format_confirmation_settings, format_ratio}; +use super::smart_fraction_fmt::SmartFractPrecision; +use crate::config::KomodefiConfig; + +pub(crate) struct OrderbookSettings { + pub(crate) uuids: bool, + pub(crate) min_volume: bool, + pub(crate) max_volume: bool, + pub(crate) publics: bool, + pub(crate) address: bool, + pub(crate) age: bool, + pub(crate) conf_settings: bool, + pub(crate) asks_limit: Option, + pub(crate) bids_limit: Option, +} + +pub(super) fn on_orderbook_response( + writer: &mut dyn Write, + response: OrderbookResponse, + config: &Cfg, + settings: OrderbookSettings, +) -> Result<()> { + let base_vol_head = format!("Volume: {}", response.base); + let rel_price_head = format!("Price: {}", response.rel); + writeln_safe_io!( + writer, + "{}", + AskBidRow::new( + base_vol_head.as_str(), + rel_price_head.as_str(), + "Uuid", + "Min volume", + "Max volume", + "Age(sec.)", + "Public", + "Address", + "Order conf (bc,bn:rc,rn)", + &settings + ) + ); + + let price_prec = config.orderbook_price_precision(); + let vol_prec = config.orderbook_volume_precision(); + + if response.asks.is_empty() { + writeln_safe_io!( + writer, + "{}", + AskBidRow::new("", "No asks found", "", "", "", "", "", "", "", &settings) + ); + } else { + let skip = response + .asks + .len() + .checked_sub(settings.asks_limit.unwrap_or(usize::MAX)) + .unwrap_or_default(); + + response.asks.iter().sorted_by(cmp_asks).skip(skip).for_each(|entry| { + let Ok(row) = AskBidRow::from_orderbook_entry(entry, vol_prec, price_prec, &settings) else {return}; + writeln_safe_io!(writer, "{}", row) + }); + } + writeln_safe_io!(writer, "{}", AskBidRow::new_delimiter(&settings)); + + if response.bids.is_empty() { + writeln_safe_io!( + writer, + "{}", + AskBidRow::new("", "No bids found", "", "", "", "", "", "", "", &settings) + ); + } else { + response + .bids + .iter() + .sorted_by(cmp_bids) + .take(settings.bids_limit.unwrap_or(usize::MAX)) + .for_each(|entry| { + let Ok(row) = AskBidRow::from_orderbook_entry(entry, vol_prec, price_prec, &settings) else {return}; + writeln_safe_io!(writer, "{}", row) + }); + } + Ok(()) +} + +fn cmp_bids(left: &&AggregatedOrderbookEntry, right: &&AggregatedOrderbookEntry) -> Ordering { + let cmp = left.entry.price.cmp(&right.entry.price).reverse(); + if cmp.is_eq() { + return left + .entry + .base_max_volume + .base_max_volume + .cmp(&right.entry.base_max_volume.base_max_volume) + .reverse(); + } + cmp +} + +fn cmp_asks(left: &&AggregatedOrderbookEntry, right: &&AggregatedOrderbookEntry) -> Ordering { + let cmp = left.entry.price.cmp(&right.entry.price).reverse(); + if cmp.is_eq() { + return left + .entry + .base_max_volume + .base_max_volume + .cmp(&right.entry.base_max_volume.base_max_volume); + } + cmp +} + +enum AskBidRowVal { + Value(String), + Delim, +} + +struct AskBidRow<'a> { + volume: AskBidRowVal, + price: AskBidRowVal, + uuid: AskBidRowVal, + min_volume: AskBidRowVal, + max_volume: AskBidRowVal, + age: AskBidRowVal, + public: AskBidRowVal, + address: AskBidRowVal, + is_mine: AskBidRowVal, + conf_settings: AskBidRowVal, + settings: &'a OrderbookSettings, +} + +impl<'a> AskBidRow<'a> { + #[allow(clippy::too_many_arguments)] + fn new( + volume: &str, + price: &str, + uuid: &str, + min_volume: &str, + max_volume: &str, + age: &str, + public: &str, + address: &str, + conf_settings: &str, + settings: &'a OrderbookSettings, + ) -> Self { + Self { + is_mine: AskBidRowVal::Value(String::new()), + volume: AskBidRowVal::Value(volume.to_string()), + price: AskBidRowVal::Value(price.to_string()), + uuid: AskBidRowVal::Value(uuid.to_string()), + min_volume: AskBidRowVal::Value(min_volume.to_string()), + max_volume: AskBidRowVal::Value(max_volume.to_string()), + age: AskBidRowVal::Value(age.to_string()), + public: AskBidRowVal::Value(public.to_string()), + address: AskBidRowVal::Value(address.to_string()), + conf_settings: AskBidRowVal::Value(conf_settings.to_string()), + settings, + } + } + + fn new_delimiter(settings: &'a OrderbookSettings) -> Self { + Self { + is_mine: AskBidRowVal::Delim, + volume: AskBidRowVal::Delim, + price: AskBidRowVal::Delim, + uuid: AskBidRowVal::Delim, + min_volume: AskBidRowVal::Delim, + max_volume: AskBidRowVal::Delim, + age: AskBidRowVal::Delim, + public: AskBidRowVal::Delim, + address: AskBidRowVal::Delim, + conf_settings: AskBidRowVal::Delim, + settings, + } + } + + fn from_orderbook_entry( + entry: &AggregatedOrderbookEntry, + vol_prec: &SmartFractPrecision, + price_prec: &SmartFractPrecision, + settings: &'a OrderbookSettings, + ) -> Result { + Ok(AskBidRow { + is_mine: AskBidRowVal::Value((if entry.entry.is_mine { "*" } else { "" }).to_string()), + volume: AskBidRowVal::Value(format_ratio(&entry.entry.base_max_volume.base_max_volume, *vol_prec)?), + price: AskBidRowVal::Value(format_ratio(&entry.entry.price, *price_prec)?), + uuid: AskBidRowVal::Value(entry.entry.uuid.to_string()), + min_volume: AskBidRowVal::Value(format_ratio(&entry.entry.min_volume, *vol_prec)?), + max_volume: AskBidRowVal::Value(format_ratio(&entry.entry.max_volume, *vol_prec)?), + age: AskBidRowVal::Value(entry.entry.age.to_string()), + public: AskBidRowVal::Value(entry.entry.pubkey.clone()), + address: AskBidRowVal::Value(entry.entry.address.clone()), + conf_settings: AskBidRowVal::Value( + entry + .entry + .conf_settings + .as_ref() + .map_or("none".to_string(), format_confirmation_settings), + ), + settings, + }) + } +} + +impl Display for AskBidRow<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + macro_rules! write_ask_bid_row { + ($value: expr, $width: expr, $alignment: literal) => { + if let AskBidRowVal::Value(value) = &$value { + write!(f, concat!("{:", $alignment, "width$} "), value, width = $width)?; + } else { + write!(f, "{:- { + if $settings { + write_ask_bid_row!($value, $width, $alignment); + } + }; + } + write_ask_bid_row!(self.is_mine, 1, "<"); + write_ask_bid_row!(self.volume, 15, ">"); + write_ask_bid_row!(self.price, 16, "<"); + write_ask_bid_row!(self.settings.uuids, self.uuid, 36, "<"); + write_ask_bid_row!(self.settings.min_volume, self.min_volume, 12, "<"); + write_ask_bid_row!(self.settings.max_volume, self.max_volume, 12, "<"); + write_ask_bid_row!(self.settings.age, self.age, 10, "<"); + write_ask_bid_row!(self.settings.publics, self.public, 66, "<"); + write_ask_bid_row!(self.settings.address, self.address, 34, "<"); + write_ask_bid_row!(self.settings.conf_settings, self.conf_settings, 24, "<"); + Ok(()) + } +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/orderbook_depth.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/orderbook_depth.rs new file mode 100644 index 0000000000..151cfe2bfa --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/orderbook_depth.rs @@ -0,0 +1,43 @@ +use std::io::Write; +use term_table::{row::Row, + table_cell::{Alignment, TableCell}, + TableStyle}; + +use common::{write_safe::io::WriteSafeIO, write_safe_io}; +use mm2_rpc::data::legacy::{Mm2RpcResult, PairWithDepth}; + +use super::formatters::term_table_blank; + +pub(super) fn on_orderbook_depth( + writer: &mut dyn Write, + response: Mm2RpcResult>, +) -> anyhow::Result<()> { + let mut term_table = term_table_blank(TableStyle::empty(), false, false, false); + term_table.add_row(orderbook_depth_header_row()); + for data in response.result { + term_table.add_row(orderbook_depth_row(data)) + } + write_safe_io!(writer, "{}", term_table.render().replace('\0', "")); + Ok(()) +} + +fn orderbook_depth_header_row() -> Row<'static> { + Row::new(vec![ + TableCell::new(""), + TableCell::new_with_alignment_and_padding("Bids", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Asks", 1, Alignment::Left, false), + ]) +} + +fn orderbook_depth_row(data: PairWithDepth) -> Row<'static> { + Row::new(vec![ + TableCell::new_with_alignment_and_padding( + format!("{}/{}:", data.pair.0, data.pair.1), + 1, + Alignment::Right, + false, + ), + TableCell::new_with_alignment_and_padding(data.depth.bids, 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding(data.depth.asks, 1, Alignment::Left, false), + ]) +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/orders_history.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/orders_history.rs new file mode 100644 index 0000000000..06fe9f0e3c --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/orders_history.rs @@ -0,0 +1,210 @@ +use anyhow::Result; +use itertools::Itertools; +use std::io::Write; +use term_table::{row::Row, + table_cell::{Alignment, TableCell}, + TableStyle}; + +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; +use mm2_rpc::data::legacy::{FilteringOrder, MakerOrderForRpc, Mm2RpcResult, OrderForRpc, OrdersHistoryResponse, + UuidParseError}; + +use super::formatters::{term_table_blank, write_maker_match}; +use crate::komodefi_proc::response_handler::formatters::{format_confirmation_settings, format_datetime_msec, + format_f64, format_historical_changes, format_ratio, + get_matches_rows, taker_order_header_row, taker_order_rows, + COMMON_PRECISION}; + +pub(crate) struct OrdersHistorySettings { + pub(crate) takers_detailed: bool, + pub(crate) makers_detailed: bool, + pub(crate) warnings: bool, + pub(crate) all: bool, +} + +pub(super) fn on_orders_history( + writer: &mut dyn Write, + mut response: Mm2RpcResult, + settings: OrdersHistorySettings, +) -> Result<()> { + macro_rules! write_history_filtered_result { + ($writer: expr, $rows: ident, $header_fn: ident, $legend: literal) => { + if $rows.is_empty() { + writeln_safe_io!($writer, concat!($legend, " not found")); + } else { + let mut table = term_table_blank(TableStyle::thin(), false, false, false); + table.add_row($header_fn()); + table.add_row(Row::new(vec![TableCell::new("")])); + table.rows.extend($rows.drain(..)); + write_safe_io!( + $writer, + concat!($legend, "\n{}"), + table.render().replace('\0', "") + ) + } + }; + } + + if settings.all { + let mut rows: Vec = response.result.orders.drain(..).map(order_row).try_collect()?; + write_history_filtered_result!(writer, rows, order_header_row, "Orders history:"); + } + + let mut maker_rows = vec![]; + let mut taker_rows = vec![]; + if settings.makers_detailed || settings.takers_detailed { + for order in response.result.details.drain(..) { + match order { + OrderForRpc::Maker(order) => maker_rows.append(maker_order_rows(&order)?.as_mut()), + OrderForRpc::Taker(order) => taker_rows.append(taker_order_rows(&order)?.as_mut()), + } + } + } + if settings.takers_detailed { + write_history_filtered_result!( + writer, + taker_rows, + taker_order_header_row, + "Taker orders history detailed:" + ); + } + if settings.makers_detailed { + write_history_filtered_result!( + writer, + maker_rows, + maker_order_header_row, + "Maker orders history detailed:" + ); + } + if settings.warnings { + let warnings = response.result.warnings.drain(..); + let mut rows: Vec = warnings.map(uuid_parse_error_row).collect(); + write_history_filtered_result!(writer, rows, uuid_parse_error_header_row, "Uuid parse errors:"); + } + Ok(()) +} + +fn order_header_row() -> Row<'static> { + Row::new(vec![ + TableCell::new_with_alignment_and_padding("uuid", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Type", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Action", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Base", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Rel", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Volume", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Price", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Status", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Created", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Updated", 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding("Was taker", 1, Alignment::Left, false), + ]) +} + +fn order_row(order: FilteringOrder) -> Result> { + Ok(Row::new(vec![ + TableCell::new_with_alignment_and_padding(&order.uuid, 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding(&order.order_type, 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding(&order.initial_action, 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding(&order.base, 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding(&order.rel, 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding( + format_f64(order.volume, COMMON_PRECISION)?, + 1, + Alignment::Left, + false, + ), + TableCell::new_with_alignment_and_padding( + format_f64(order.price, COMMON_PRECISION)?, + 1, + Alignment::Left, + false, + ), + TableCell::new_with_alignment_and_padding(&order.status, 1, Alignment::Left, false), + TableCell::new_with_alignment_and_padding( + format_datetime_msec(order.created_at as u64)?, + 1, + Alignment::Left, + false, + ), + TableCell::new_with_alignment_and_padding( + format_datetime_msec(order.last_updated as u64)?, + 1, + Alignment::Left, + false, + ), + TableCell::new_with_alignment_and_padding(order.was_taker != 0, 1, Alignment::Left, false), + ])) +} + +fn uuid_parse_error_header_row() -> Row<'static> { Row::new(vec![TableCell::new("uuid"), TableCell::new("error")]) } + +fn uuid_parse_error_row(uuid_parse_error: UuidParseError) -> Row<'static> { + Row::new(vec![ + TableCell::new(uuid_parse_error.uuid), + TableCell::new(uuid_parse_error.warning), + ]) +} + +fn maker_order_header_row() -> Row<'static> { + Row::new(vec![ + TableCell::new("base,rel"), + TableCell::new("price"), + TableCell::new("uuid"), + TableCell::new("created at,\nupdated at"), + TableCell::new("min base vol,\nmax base vol"), + TableCell::new("swaps"), + TableCell::new("conf_settings"), + TableCell::new("history changes"), + TableCell::new("orderbook ticker\nbase, rel"), + ]) +} + +fn maker_order_rows(order: &MakerOrderForRpc) -> Result>> { + let mut rows = vec![Row::new(vec![ + TableCell::new(format!("{},{}", order.base, order.rel)), + TableCell::new(format_ratio(&order.price_rat, COMMON_PRECISION)?), + TableCell::new(order.uuid), + TableCell::new(format!( + "{},\n{}", + format_datetime_msec(order.created_at)?, + order.updated_at.map_or(Ok("".to_string()), format_datetime_msec)? + )), + TableCell::new(format!( + "{},\n{}", + format_ratio(&order.min_base_vol_rat, COMMON_PRECISION)?, + format_ratio(&order.max_base_vol_rat, COMMON_PRECISION)? + )), + TableCell::new(if order.started_swaps.is_empty() { + "empty".to_string() + } else { + order.started_swaps.iter().join(",\n") + }), + TableCell::new( + order + .conf_settings + .as_ref() + .map_or_else(|| "none".to_string(), format_confirmation_settings), + ), + TableCell::new(order.changes_history.as_ref().map_or_else( + || "none".to_string(), + |val| { + val.iter() + .map(|val| format_historical_changes(val, "\n").unwrap_or_else(|_| "error".to_string())) + .join(",\n") + }, + )), + TableCell::new(format!( + "{}\n{}", + order + .base_orderbook_ticker + .as_ref() + .map_or_else(|| "none".to_string(), String::clone), + order + .rel_orderbook_ticker + .as_ref() + .map_or_else(|| "none".to_string(), String::clone) + )), + ])]; + rows.append(get_matches_rows(&order.matches, 10, write_maker_match)?.as_mut()); + Ok(rows) +} diff --git a/mm2src/adex_cli/src/adex_proc/response_handler/smart_fraction_fmt.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/smart_fraction_fmt.rs similarity index 52% rename from mm2src/adex_cli/src/adex_proc/response_handler/smart_fraction_fmt.rs rename to mm2src/komodefi_cli/src/komodefi_proc/response_handler/smart_fraction_fmt.rs index 9c7b9de415..6e6f1fd1bb 100644 --- a/mm2src/adex_cli/src/adex_proc/response_handler/smart_fraction_fmt.rs +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/smart_fraction_fmt.rs @@ -2,25 +2,25 @@ use std::cell::Cell; pub(crate) type SmartFractPrecision = (usize, usize); -pub(super) struct SmartFractionFmt { +pub(in super::super) struct SmartFractionFmt { precision_min: i32, precision_max: i32, num: Cell, } #[derive(Debug)] -pub(super) enum SmartFractionTrimErr { +pub(in super::super) enum SmartFractionTrimErr { WrongParams, } impl SmartFractionFmt { - pub(super) fn new(precision_min: usize, precision_max: usize, num: f64) -> Result { - if precision_min == 0 || precision_min > precision_max { + pub(in super::super) fn new(precision: &SmartFractPrecision, num: f64) -> Result { + if precision.0 == 0 || precision.0 > precision.1 { return Err(SmartFractionTrimErr::WrongParams); } Ok(Self { - precision_min: precision_min as i32, - precision_max: precision_max as i32, + precision_min: precision.0 as i32, + precision_max: precision.1 as i32, num: Cell::new(num), }) } @@ -51,46 +51,48 @@ impl ToString for SmartFractionFmt { #[test] fn test_construct_smart_fraction_fmt() { - assert!(SmartFractionFmt::new(0, 5, 0.0).is_err()); - assert!(SmartFractionFmt::new(5, 2, 0.0).is_err()); + assert!(SmartFractionFmt::new(&(0, 5), 0.0).is_err()); + assert!(SmartFractionFmt::new(&(5, 2), 0.0).is_err()); } #[test] fn test_smart_fraction_fmt() { - let num = SmartFractionFmt::new(2, 5, 0.0).unwrap(); + use crate::komodefi_proc::response_handler::formatters::COMMON_PRECISION; + + let num = SmartFractionFmt::new(&COMMON_PRECISION, 0.0).unwrap(); assert_eq!(num.to_string(), "0"); - let num = SmartFractionFmt::new(2, 5, 0.1).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 0.1).unwrap(); assert_eq!(num.to_string(), "0.10"); - let num = SmartFractionFmt::new(2, 5, 0.19909).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 0.19909).unwrap(); assert_eq!(num.to_string(), "0.19"); - let num = SmartFractionFmt::new(2, 5, 0.10001).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 0.10001).unwrap(); assert_eq!(num.to_string(), "0.10"); - let num = SmartFractionFmt::new(2, 5, 0.10991).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 0.10991).unwrap(); assert_eq!(num.to_string(), "0.10"); - let num = SmartFractionFmt::new(2, 5, 0.0011991).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 0.0011991).unwrap(); assert_eq!(num.to_string(), "0.0011"); - let num = SmartFractionFmt::new(2, 5, 0.001110000001).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 0.001110000001).unwrap(); assert_eq!(num.to_string(), "0.0011"); - let num = SmartFractionFmt::new(2, 5, 0.00001700445).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 0.00001700445).unwrap(); assert_eq!(num.to_string(), "0.000017"); - let num = SmartFractionFmt::new(2, 5, 0.00000199).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 0.00000199).unwrap(); assert_eq!(num.to_string(), "0.00000"); - let num = SmartFractionFmt::new(2, 5, 1.0).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 1.0).unwrap(); assert_eq!(num.to_string(), "1.00"); - let num = SmartFractionFmt::new(2, 5, 1.00001).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 1.00001).unwrap(); assert_eq!(num.to_string(), "1.00"); - let num = SmartFractionFmt::new(2, 5, 1.00000000001).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 1.00000000001).unwrap(); assert_eq!(num.to_string(), "1.00"); - let num = SmartFractionFmt::new(2, 5, 1.99001).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 1.99001).unwrap(); assert_eq!(num.to_string(), "1.99"); - let num = SmartFractionFmt::new(2, 5, 5000.0).unwrap(); + let num = SmartFractionFmt::new(&COMMON_PRECISION, 5000.0).unwrap(); assert_eq!(num.to_string(), "5000.00"); - let num = SmartFractionFmt::new(1, 5, 0.10991).unwrap(); + let num = SmartFractionFmt::new(&(1, 5), 0.10991).unwrap(); assert_eq!(num.to_string(), "0.1"); - let num = SmartFractionFmt::new(2, 2, 0.001110000001).unwrap(); + let num = SmartFractionFmt::new(&(2, 2), 0.001110000001).unwrap(); assert_eq!(num.to_string(), "0.00"); - let num = SmartFractionFmt::new(2, 2, 0.101110000001).unwrap(); + let num = SmartFractionFmt::new(&(2, 2), 0.101110000001).unwrap(); assert_eq!(num.to_string(), "0.10"); } diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/swaps.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/swaps.rs new file mode 100644 index 0000000000..17a548737f --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/swaps.rs @@ -0,0 +1,759 @@ +use anyhow::{anyhow, Result}; +use itertools::Itertools; +use rpc::v1::types::H264; +use std::io::Write; +use term_table::{row::Row, TableStyle}; + +use common::log::error; +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; + +use super::formatters::{format_bytes, format_datetime_msec, format_ratio, term_table_blank, write_field_option, + writeln_field, COMMON_PRECISION, ZERO_INDENT}; +use crate::error_anyhow; +use crate::komodefi_proc::response_handler::formatters::format_datetime_sec; +use crate::rpc_data::{ActiveSwapsResponse, MakerNegotiationData, MakerSavedEvent, MakerSavedSwap, MakerSwapData, + MakerSwapEvent, MyRecentSwapResponse, MySwapStatusResponse, PaymentInstructions, + RecoverFundsOfSwapResponse, SavedSwap, SavedTradeFee, SwapError, TakerNegotiationData, + TakerPaymentSpentData, TakerSavedEvent, TakerSavedSwap, TakerSwapData, TakerSwapEvent, + TransactionIdentifier}; + +const DATA_COLUMN_WIDTH: usize = 120; + +pub(super) fn on_active_swaps(writer: &mut dyn Write, response: ActiveSwapsResponse, uuids_only: bool) -> Result<()> { + if uuids_only && response.uuids.is_empty() { + writeln_safe_io!(writer, "No swaps found"); + } else if uuids_only && !response.uuids.is_empty() { + writeln_field(writer, "uuids", "", ZERO_INDENT); + writeln_safe_io!(writer, "{}", response.uuids.iter().join("\n")); + } else if let Some(statuses) = response.statuses { + for (_uuid, swap) in statuses { + writeln_safe_io!(writer, ""); + match swap { + SavedSwap::Taker(taker_swap) => write_taker_swap(writer, taker_swap)?, + SavedSwap::Maker(maker_swap) => write_maker_swap(writer, maker_swap)?, + } + } + } else { + writeln_safe_io!(writer, "No swaps found"); + }; + Ok(()) +} + +pub(super) fn on_my_swap_status(writer: &mut dyn Write, response: MySwapStatusResponse) -> Result<()> { + if let Some(my_info) = response.my_info { + writeln_field(writer, "my_coin", my_info.my_coin, ZERO_INDENT); + writeln_field(writer, "other_coin", my_info.other_coin, ZERO_INDENT); + writeln_field( + writer, + "my_amount", + format_ratio(&my_info.my_amount, COMMON_PRECISION)?, + 0, + ); + writeln_field( + writer, + "other_amount", + format_ratio(&my_info.other_amount, COMMON_PRECISION)?, + 0, + ); + writeln_field( + writer, + "started_at", + format_datetime_sec(my_info.started_at)?, + ZERO_INDENT, + ); + } + writeln_field(writer, "recoverable", response.recoverable, ZERO_INDENT); + match response.swap { + SavedSwap::Taker(taker_swap) => write_taker_swap(writer, taker_swap)?, + SavedSwap::Maker(maker_swap) => write_maker_swap(writer, maker_swap)?, + } + Ok(()) +} + +pub(super) fn on_my_recent_swaps(writer: &mut dyn Write, response: MyRecentSwapResponse) -> Result<()> { + write_field_option(writer, "from_uuid", response.from_uuid, ZERO_INDENT); + + writeln_field(writer, "skipped", response.skipped, ZERO_INDENT); + writeln_field(writer, "limit", response.limit, ZERO_INDENT); + writeln_field(writer, "total", response.total, ZERO_INDENT); + writeln_field(writer, "page_number", response.page_number, ZERO_INDENT); + writeln_field(writer, "total_pages", response.total_pages, ZERO_INDENT); + writeln_field(writer, "found_records", response.found_records, ZERO_INDENT); + + for swap in response.swaps { + writeln_safe_io!(writer, ""); + match swap { + SavedSwap::Taker(taker_swap) => write_taker_swap(writer, taker_swap)?, + SavedSwap::Maker(maker_swap) => write_maker_swap(writer, maker_swap)?, + } + } + + Ok(()) +} + +pub(super) fn on_recover_funds(writer: &mut dyn Write, response: RecoverFundsOfSwapResponse) -> Result<()> { + writeln_field(writer, "action", response.action, ZERO_INDENT); + writeln_field(writer, "coin", response.coin, ZERO_INDENT); + writeln_field(writer, "tx_hash", format_bytes(response.tx_hash), ZERO_INDENT); + writeln_field(writer, "tx_hash", format_bytes(response.tx_hex), ZERO_INDENT); + Ok(()) +} + +fn write_taker_swap(writer: &mut dyn Write, taker_swap: TakerSavedSwap) -> Result<()> { + writeln_field(writer, "TakerSwap", taker_swap.uuid, ZERO_INDENT); + write_field_option(writer, "my_order_uuid", taker_swap.my_order_uuid, ZERO_INDENT); + write_field_option(writer, "gui", taker_swap.gui, ZERO_INDENT); + write_field_option(writer, "mm_version", taker_swap.mm_version, ZERO_INDENT); + write_field_option(writer, "taker_coin", taker_swap.taker_coin, ZERO_INDENT); + + let taker_amount = taker_swap + .taker_amount + .map(|value| format_ratio(&value, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "taker_amount", taker_amount, ZERO_INDENT); + let taker_coin_usd_price = taker_swap + .taker_coin_usd_price + .map(|value| format_ratio(&value, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "taker_coin_usd_price", taker_coin_usd_price, ZERO_INDENT); + write_field_option(writer, "maker_coin", taker_swap.maker_coin, ZERO_INDENT); + let maker_amount = taker_swap + .maker_amount + .map(|value| format_ratio(&value, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "maker_amount", maker_amount, ZERO_INDENT); + let maker_coin_usd_price = taker_swap + .maker_coin_usd_price + .map(|value| format_ratio(&value, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "maker_coin_usd_price", maker_coin_usd_price, ZERO_INDENT); + write_taker_swap_events(writer, taker_swap.events) +} + +fn write_taker_swap_events(writer: &mut dyn Write, taker_swap_events: Vec) -> Result<()> { + let mut term_table = term_table_blank(TableStyle::thin(), false, false, false); + term_table.set_max_width_for_column(1, DATA_COLUMN_WIDTH); + if taker_swap_events.is_empty() { + writeln_field(writer, "events", "empty", ZERO_INDENT); + return Ok(()); + } + for event in taker_swap_events { + let row = match event.event { + TakerSwapEvent::Started(taker_swap_data) => taker_swap_started_row(event.timestamp, taker_swap_data)?, + TakerSwapEvent::StartFailed(error) => swap_error_row("StartFailed", event.timestamp, error)?, + TakerSwapEvent::Negotiated(maker_neg_data) => maker_negotiated_data_row(event.timestamp, maker_neg_data)?, + TakerSwapEvent::NegotiateFailed(error) => swap_error_row("NegotiateFailed", event.timestamp, error)?, + TakerSwapEvent::TakerFeeSent(tx_id) => tx_id_row("TakerFeeSent", event.timestamp, tx_id)?, + TakerSwapEvent::TakerFeeSendFailed(error) => swap_error_row("TakerFeeSendFailed", event.timestamp, error)?, + TakerSwapEvent::TakerPaymentInstructionsReceived(payment_instrs) => get_opt_value_row( + "TakerPaymentInstructionsReceived", + event.timestamp, + payment_instrs, + payment_instructions_row, + )?, + TakerSwapEvent::MakerPaymentReceived(tx_id) => tx_id_row("MakerPaymentReceived", event.timestamp, tx_id)?, + TakerSwapEvent::MakerPaymentWaitConfirmStarted => { + named_event_row("MakerPaymentWaitConfirmStarted", event.timestamp)? + }, + TakerSwapEvent::MakerPaymentValidatedAndConfirmed => { + named_event_row("MakerPaymentValidatedAndConfirmed", event.timestamp)? + }, + TakerSwapEvent::MakerPaymentValidateFailed(error) => { + swap_error_row("MakerPaymentValidateFailed", event.timestamp, error)? + }, + TakerSwapEvent::MakerPaymentWaitConfirmFailed(error) => { + swap_error_row("MakerPaymentWaitConfirmFailed", event.timestamp, error)? + }, + TakerSwapEvent::TakerPaymentSent(tx_id) => tx_id_row("TakerPaymentSent", event.timestamp, tx_id)?, + TakerSwapEvent::WatcherMessageSent(maker_spend_preimage, taker_refund_preimage) => { + watcher_message_row(event.timestamp, maker_spend_preimage, taker_refund_preimage)? + }, + TakerSwapEvent::TakerPaymentTransactionFailed(error) => { + swap_error_row("TakerPaymentTransactionFailed", event.timestamp, error)? + }, + TakerSwapEvent::TakerPaymentDataSendFailed(error) => { + swap_error_row("TakerPaymentDataSendFailed", event.timestamp, error)? + }, + TakerSwapEvent::TakerPaymentWaitConfirmFailed(error) => { + swap_error_row("TakerPaymentWaitConfirmFailed", event.timestamp, error)? + }, + TakerSwapEvent::TakerPaymentSpent(taker_spent_data) => { + taker_spent_data_row(event.timestamp, taker_spent_data)? + }, + TakerSwapEvent::TakerPaymentWaitForSpendFailed(error) => { + swap_error_row("TakerPaymentWaitForSpendFailed", event.timestamp, error)? + }, + TakerSwapEvent::MakerPaymentSpent(tx_id) => tx_id_row("MakerPaymentSpent", event.timestamp, tx_id)?, + TakerSwapEvent::MakerPaymentSpendFailed(error) => { + swap_error_row("MakerPaymentSpendFailed", event.timestamp, error)? + }, + TakerSwapEvent::TakerPaymentWaitRefundStarted { wait_until } => { + wait_refund_row("TakerPaymentWaitRefundStarted", event.timestamp, wait_until)? + }, + TakerSwapEvent::TakerPaymentRefundStarted => named_event_row("TakerPaymentRefundStarted", event.timestamp)?, + TakerSwapEvent::TakerPaymentRefunded(opt_tx_id) => { + get_opt_value_row("TakerPaymentRefunded", event.timestamp, opt_tx_id, tx_id_row)? + }, + TakerSwapEvent::TakerPaymentRefundFailed(error) => { + swap_error_row("TakerPaymentRefundFailed", event.timestamp, error)? + }, + TakerSwapEvent::TakerPaymentRefundFinished => { + named_event_row("TakerPaymentRefundFinished", event.timestamp)? + }, + TakerSwapEvent::Finished => named_event_row("Finished", event.timestamp)?, + }; + term_table.add_row(row); + } + writeln_field(writer, "events", "", ZERO_INDENT); + write_safe_io!(writer, "{}", term_table.render().replace('\0', "")); + Ok(()) +} + +fn taker_swap_started_row(timestamp: u64, swap_data: TakerSwapData) -> Result> { + let caption = format!("Started\n{}\n", format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + writeln_field(writer, "uuid", swap_data.uuid, ZERO_INDENT); + writeln_field( + writer, + "started_at", + format_datetime_sec(swap_data.started_at)?, + ZERO_INDENT, + ); + writeln_field(writer, "taker_coin", swap_data.taker_coin, ZERO_INDENT); + writeln_field(writer, "maker_coin", swap_data.maker_coin, ZERO_INDENT); + writeln_field(writer, "maker", hex::encode(swap_data.maker.0), ZERO_INDENT); + writeln_field( + writer, + "my_persistent_pub", + hex::encode(swap_data.my_persistent_pub.0), + 0, + ); + writeln_field(writer, "lock_duration", swap_data.lock_duration, ZERO_INDENT); + let maker_amount = format_ratio(&swap_data.maker_amount, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string()); + writeln_field(writer, "maker_amount", maker_amount, ZERO_INDENT); + let taker_amount = format_ratio(&swap_data.taker_amount, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string()); + writeln_field(writer, "taker_amount", taker_amount, ZERO_INDENT); + + writeln_field( + writer, + "maker_payment_confirmations", + swap_data.maker_payment_confirmations, + 0, + ); + write_field_option( + writer, + "maker_payment_requires_nota", + swap_data.maker_payment_requires_nota, + 0, + ); + writeln_field( + writer, + "taker_payment_confirmations", + swap_data.taker_payment_confirmations, + 0, + ); + write_field_option( + writer, + "taker_payment_requires_nota", + swap_data.taker_payment_requires_nota, + 0, + ); + writeln_field( + writer, + "tacker_payment_lock", + format_datetime_sec(swap_data.taker_payment_lock)?, + 0, + ); + writeln_field( + writer, + "maker_payment_wait", + format_datetime_sec(swap_data.maker_payment_wait)?, + 0, + ); + writeln_field( + writer, + "maker_coin_start_block", + swap_data.maker_coin_start_block, + ZERO_INDENT, + ); + writeln_field( + writer, + "taker_coin_start_block", + swap_data.taker_coin_start_block, + ZERO_INDENT, + ); + let fee_to_send_taker_fee = swap_data + .fee_to_send_taker_fee + .map(|value| format_saved_trade_fee(value).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "fee_to_send_taker_fee", fee_to_send_taker_fee, ZERO_INDENT); + let taker_payment_trade_fee = swap_data + .taker_payment_trade_fee + .map(|value| format_saved_trade_fee(value).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "taker_payment_trade_fee", taker_payment_trade_fee, ZERO_INDENT); + let maker_spend_trade_fee = swap_data + .maker_payment_spend_trade_fee + .map(|value| format_saved_trade_fee(value).unwrap_or_else(|_| "error".to_string())); + write_field_option( + writer, + "maker_payment_spend_trade_fee", + maker_spend_trade_fee, + ZERO_INDENT, + ); + let maker_contract = swap_data + .maker_coin_swap_contract_address + .map(|v| hex::encode(v.as_slice())); + write_field_option(writer, "maker_coin_swap_contract", maker_contract, ZERO_INDENT); + let taker_contract = swap_data + .taker_coin_swap_contract_address + .map(|v| hex::encode(v.as_slice())); + write_field_option(writer, "taker_coin_swap_contract", taker_contract, ZERO_INDENT); + let maker_coin_htlc_pubkey = swap_data.maker_coin_htlc_pubkey.map(|v| hex::encode(v.0)); + write_field_option(writer, "maker_coin_htlc_pubkey", maker_coin_htlc_pubkey, ZERO_INDENT); + let taker_coin_htlc_pubkey = swap_data.taker_coin_htlc_pubkey.map(|v| hex::encode(v.0)); + write_field_option(writer, "taker_coin_htlc_pubkey", taker_coin_htlc_pubkey, ZERO_INDENT); + let p2p_pkey = swap_data.p2p_privkey.map(|v| v.inner); + write_field_option(writer, "p2p_privkey", p2p_pkey, ZERO_INDENT); + + let data = + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to get taker_swap_data from buffer: {error}"))?; + + Ok(Row::new([caption, data])) +} + +fn swap_error_row(caption: &str, timestamp: u64, swap_error: SwapError) -> Result> { + let caption = format!("{}\n{}\n", caption, format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + writeln_field(writer, "swap_error", swap_error.error, ZERO_INDENT); + let data = + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to get swap_error from buffer: {error}"))?; + Ok(Row::new([caption, data])) +} + +fn maker_negotiated_data_row(timestamp: u64, neg_data: MakerNegotiationData) -> Result> { + let caption = format!("Negotiated\n{}\n", format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + writeln_field( + writer, + "maker_payment_locktime", + format_datetime_sec(neg_data.maker_payment_locktime)?, + 0, + ); + writeln_field(writer, "maker_pubkey", format_h264(neg_data.maker_pubkey), ZERO_INDENT); + writeln_field(writer, "secret_hash", format_bytes(neg_data.secret_hash), ZERO_INDENT); + write_field_option( + writer, + "maker_swap_contract", + neg_data.maker_coin_swap_contract_addr.map(format_bytes), + 0, + ); + write_field_option( + writer, + "taker_swap_contract", + neg_data.taker_coin_swap_contract_addr.map(format_bytes), + 0, + ); + write_field_option( + writer, + "maker_coin_htlc_pubkey", + neg_data.maker_coin_htlc_pubkey.map(format_h264), + 0, + ); + write_field_option( + writer, + "taker_coin_htlc_pubkey", + neg_data.taker_coin_htlc_pubkey.map(format_h264), + 0, + ); + + let data = String::from_utf8(buff) + .map_err(|error| error_anyhow!("Failed to get maker_negotiated_data from buffer: {error}"))?; + Ok(Row::new([caption, data])) +} + +fn tx_id_row(caption: &str, timestamp: u64, tx_id: TransactionIdentifier) -> Result> { + let caption = format!("{}\n{}\n", caption, format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + writeln_field(writer, "tx_hex", format_bytes(tx_id.tx_hex), ZERO_INDENT); + writeln_field(writer, "tx_hash", format_bytes(tx_id.tx_hash), ZERO_INDENT); + let data = String::from_utf8(buff) + .map_err(|error| error_anyhow!("Failed to get transaction_identifier from buffer: {error}"))?; + Ok(Row::new([caption, data])) +} + +fn payment_instructions_row( + caption: &str, + timestamp: u64, + payment_instrs: PaymentInstructions, +) -> Result> { + let caption = format!("{}\n{}\n", caption, format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + match payment_instrs { + PaymentInstructions::Lightning(invoice) => { + writeln_field(writer, "Lightning: {}", invoice.to_string(), ZERO_INDENT) + }, + PaymentInstructions::WatcherReward(reward) => writeln_field( + writer, + "WatcherReward: {}", + format_ratio(&reward, COMMON_PRECISION)?, + ZERO_INDENT, + ), + } + let data = String::from_utf8(buff) + .map_err(|error| error_anyhow!("Failed to get payment_instructions from buffer: {error}"))?; + Ok(Row::new([caption, data])) +} + +fn named_event_row(caption: &str, timestamp: u64) -> Result> { + let caption = format!("{}\n{}\n", caption, format_datetime_msec(timestamp)?); + Ok(Row::new([caption, "".to_string()])) +} + +fn watcher_message_row( + timestamp: u64, + maker_spend_preimage: Option>, + taker_refund_preimage: Option>, +) -> Result> { + let caption = format!("WatcherMessageSent\n{}\n", format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + write_field_option( + writer, + "maker_spend_preimage", + maker_spend_preimage.map(hex::encode), + ZERO_INDENT, + ); + write_field_option( + writer, + "taker_refund_preimage", + taker_refund_preimage.map(hex::encode), + 0, + ); + let data = + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to get watcher_message from buffer: {error}"))?; + Ok(Row::new([caption, data])) +} + +fn taker_spent_data_row(timestamp: u64, taker_spent_data: TakerPaymentSpentData) -> Result> { + let caption = format!("TakerPaymentSpent\n{}\n", format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + writeln_field( + writer, + "tx_hex", + format_bytes(taker_spent_data.transaction.tx_hex), + ZERO_INDENT, + ); + writeln_field( + writer, + "tx_hash", + format_bytes(taker_spent_data.transaction.tx_hash), + ZERO_INDENT, + ); + writeln_field(writer, "secret", hex::encode(taker_spent_data.secret.0), ZERO_INDENT); + let data = String::from_utf8(buff) + .map_err(|error| error_anyhow!("Failed to get taker_spent_data from buffer: {error}"))?; + Ok(Row::new([caption, data])) +} + +fn wait_refund_row(caption: &str, timestamp: u64, wait_until: u64) -> Result> { + let caption = format!("{}\n{}\n", caption, format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + writeln_field(writer, "wait_until", format_datetime_msec(wait_until)?, ZERO_INDENT); + let data = String::from_utf8(buff) + .map_err(|error| error_anyhow!("Failed to get taker_spent_data from buffer: {error}"))?; + Ok(Row::new([caption, data])) +} + +fn format_saved_trade_fee(trade_fee: SavedTradeFee) -> Result { + let saved_trade_fee = format!( + "coin: {}, amount: {}, paid_from_trading_vol: {}", + trade_fee.coin, + format_ratio(&trade_fee.amount, COMMON_PRECISION)?, + trade_fee.paid_from_trading_vol + ); + Ok(saved_trade_fee) +} + +fn write_maker_swap(writer: &mut dyn Write, maker_swap: MakerSavedSwap) -> Result<()> { + writeln_field(writer, "MakerSwap", maker_swap.uuid, ZERO_INDENT); + write_field_option(writer, "my_order_uuid", maker_swap.my_order_uuid, ZERO_INDENT); + write_field_option(writer, "gui", maker_swap.gui, ZERO_INDENT); + write_field_option(writer, "mm_version", maker_swap.mm_version, ZERO_INDENT); + write_field_option(writer, "taker_coin", maker_swap.taker_coin, ZERO_INDENT); + + let taker_amount = maker_swap + .taker_amount + .map(|value| format_ratio(&value, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "taker_amount", taker_amount, ZERO_INDENT); + let taker_coin_usd_price = maker_swap + .taker_coin_usd_price + .map(|value| format_ratio(&value, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "taker_coin_usd_price", taker_coin_usd_price, ZERO_INDENT); + + write_field_option(writer, "maker_coin", maker_swap.maker_coin, ZERO_INDENT); + let maker_amount = maker_swap + .maker_amount + .map(|value| format_ratio(&value, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "maker_amount", maker_amount, ZERO_INDENT); + let maker_coin_usd_price = maker_swap + .maker_coin_usd_price + .map(|value| format_ratio(&value, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string())); + write_field_option(writer, "maker_coin_usd_price", maker_coin_usd_price, ZERO_INDENT); + write_maker_swap_events(writer, maker_swap.events) +} + +fn write_maker_swap_events(writer: &mut dyn Write, maker_swap_event: Vec) -> Result<()> { + let mut term_table = term_table_blank(TableStyle::thin(), false, false, false); + term_table.set_max_width_for_column(1, DATA_COLUMN_WIDTH); + if maker_swap_event.is_empty() { + writeln_field(writer, "events", "empty", ZERO_INDENT); + return Ok(()); + } + for event in maker_swap_event { + let row = match event.event { + MakerSwapEvent::Started(maker_swap_data) => maker_swap_started_row(event.timestamp, maker_swap_data)?, + MakerSwapEvent::StartFailed(error) => swap_error_row("StartFailed", event.timestamp, error)?, + MakerSwapEvent::Negotiated(taker_neg_data) => taker_negotiated_data_row(event.timestamp, taker_neg_data)?, + MakerSwapEvent::NegotiateFailed(error) => swap_error_row("NegotiateFailed", event.timestamp, error)?, + MakerSwapEvent::MakerPaymentInstructionsReceived(opt_payment_instr) => get_opt_value_row( + "MakerPaymentInstructionsReceived", + event.timestamp, + opt_payment_instr, + payment_instructions_row, + )?, + MakerSwapEvent::TakerFeeValidated(tx_id) => tx_id_row("TakerFeeValidated", event.timestamp, tx_id)?, + MakerSwapEvent::TakerFeeValidateFailed(error) => { + swap_error_row("TakerFeeValidateFailed", event.timestamp, error)? + }, + MakerSwapEvent::MakerPaymentSent(tx_id) => tx_id_row("MakerPaymentSent", event.timestamp, tx_id)?, + MakerSwapEvent::MakerPaymentTransactionFailed(error) => { + swap_error_row("MakerPaymentTransactionFailed", event.timestamp, error)? + }, + MakerSwapEvent::MakerPaymentDataSendFailed(error) => { + swap_error_row("MakerPaymentDataSendFailed", event.timestamp, error)? + }, + MakerSwapEvent::MakerPaymentWaitConfirmFailed(error) => { + swap_error_row("MakerPaymentWaitConfirmFailed", event.timestamp, error)? + }, + MakerSwapEvent::TakerPaymentReceived(tx_id) => tx_id_row("TakerPaymentReceived", event.timestamp, tx_id)?, + MakerSwapEvent::TakerPaymentWaitConfirmStarted => { + named_event_row("TakerPaymentWaitConfirmStarted", event.timestamp)? + }, + MakerSwapEvent::TakerPaymentValidatedAndConfirmed => { + named_event_row("TakerPaymentValidatedAndConfirmed", event.timestamp)? + }, + MakerSwapEvent::TakerPaymentValidateFailed(error) => { + swap_error_row("TakerPaymentValidateFailed", event.timestamp, error)? + }, + MakerSwapEvent::TakerPaymentWaitConfirmFailed(error) => { + swap_error_row("TakerPaymentWaitConfirmFailed", event.timestamp, error)? + }, + MakerSwapEvent::TakerPaymentSpent(tx_id) => tx_id_row("TakerPaymentSpent", event.timestamp, tx_id)?, + MakerSwapEvent::TakerPaymentSpendFailed(error) => { + swap_error_row("TakerPaymentSpendFailed", event.timestamp, error)? + }, + MakerSwapEvent::TakerPaymentSpendConfirmStarted => { + named_event_row("TakerPaymentSpendConfirmStarted", event.timestamp)? + }, + MakerSwapEvent::TakerPaymentSpendConfirmed => { + named_event_row("TakerPaymentSpendConfirmed", event.timestamp)? + }, + MakerSwapEvent::TakerPaymentSpendConfirmFailed(error) => { + swap_error_row("TakerPaymentSpendConfirmFailed", event.timestamp, error)? + }, + MakerSwapEvent::MakerPaymentWaitRefundStarted { wait_until } => { + wait_refund_row("MakerPaymentWaitRefundStarted", event.timestamp, wait_until)? + }, + MakerSwapEvent::MakerPaymentRefundStarted => named_event_row("MakerPaymentRefundStarted", event.timestamp)?, + MakerSwapEvent::MakerPaymentRefunded(opt_tx_id) => { + get_opt_value_row("MakerPaymentRefunded", event.timestamp, opt_tx_id, tx_id_row)? + }, + MakerSwapEvent::MakerPaymentRefundFailed(error) => { + swap_error_row("MakerPaymentRefundFailed", event.timestamp, error)? + }, + MakerSwapEvent::MakerPaymentRefundFinished => { + named_event_row("MakerPaymentRefundFinished", event.timestamp)? + }, + MakerSwapEvent::Finished => named_event_row("Finished", event.timestamp)?, + }; + term_table.add_row(row); + } + writeln_field(writer, "events", "", ZERO_INDENT); + write_safe_io!(writer, "{}", term_table.render()); + Ok(()) +} + +fn maker_swap_started_row(timestamp: u64, swap_data: MakerSwapData) -> Result> { + let caption = format!("Started\n{}\n", format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + writeln_field(writer, "uuid", swap_data.uuid, ZERO_INDENT); + writeln_field( + writer, + "started_at", + format_datetime_msec(swap_data.started_at)?, + ZERO_INDENT, + ); + writeln_field(writer, "taker_coin", swap_data.taker_coin, ZERO_INDENT); + writeln_field(writer, "maker_coin", swap_data.maker_coin, ZERO_INDENT); + writeln_field(writer, "taker", hex::encode(swap_data.taker.0), ZERO_INDENT); + writeln_field(writer, "secret", hex::encode(swap_data.secret.0), ZERO_INDENT); + write_field_option( + writer, + "secret_hash", + swap_data.secret_hash.map(format_bytes), + ZERO_INDENT, + ); + + writeln_field( + writer, + "my_persistent_pub", + hex::encode(swap_data.my_persistent_pub.0), + 0, + ); + writeln_field(writer, "lock_duration", swap_data.lock_duration, ZERO_INDENT); + let maker_amount = format_ratio(&swap_data.maker_amount, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string()); + writeln_field(writer, "maker_amount", maker_amount, ZERO_INDENT); + let taker_amount = format_ratio(&swap_data.taker_amount, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string()); + writeln_field(writer, "taker_amount", taker_amount, ZERO_INDENT); + + writeln_field( + writer, + "maker_payment_confirmations", + swap_data.maker_payment_confirmations, + 0, + ); + write_field_option( + writer, + "maker_payment_requires_nota", + swap_data.maker_payment_requires_nota, + 0, + ); + writeln_field( + writer, + "taker_payment_confirmations", + swap_data.taker_payment_confirmations, + 0, + ); + write_field_option( + writer, + "taker_payment_requires_nota", + swap_data.taker_payment_requires_nota, + 0, + ); + writeln_field( + writer, + "macker_payment_lock", + format_datetime_msec(swap_data.maker_payment_lock)?, + 0, + ); + + writeln_field( + writer, + "maker_coin_start_block", + swap_data.maker_coin_start_block, + ZERO_INDENT, + ); + writeln_field( + writer, + "taker_coin_start_block", + swap_data.taker_coin_start_block, + ZERO_INDENT, + ); + write_field_option( + writer, + "maker_payment_trade_fee", + swap_data + .maker_payment_trade_fee + .map(|v| format_saved_trade_fee(v).unwrap_or_else(|_| "error".to_string())), + 0, + ); + write_field_option( + writer, + "taker_payment_spend_trade_fee", + swap_data + .taker_payment_spend_trade_fee + .map(|v| format_saved_trade_fee(v).unwrap_or_else(|_| "error".to_string())), + 0, + ); + let maker_contract = swap_data + .maker_coin_swap_contract_address + .map(|v| hex::encode(v.as_slice())); + write_field_option(writer, "maker_coin_swap_contract", maker_contract, ZERO_INDENT); + let taker_contract = swap_data + .taker_coin_swap_contract_address + .map(|v| hex::encode(v.as_slice())); + write_field_option(writer, "taker_coin_swap_contract", taker_contract, ZERO_INDENT); + let maker_coin_htlc_pubkey = swap_data.maker_coin_htlc_pubkey.map(|v| hex::encode(v.0)); + write_field_option(writer, "maker_coin_htlc_pubkey", maker_coin_htlc_pubkey, ZERO_INDENT); + let taker_coin_htlc_pubkey = swap_data.taker_coin_htlc_pubkey.map(|v| hex::encode(v.0)); + write_field_option(writer, "taker_coin_htlc_pubkey", taker_coin_htlc_pubkey, ZERO_INDENT); + let p2p_pkey = swap_data.p2p_privkey.map(|v| v.inner); + write_field_option(writer, "p2p_privkey", p2p_pkey, ZERO_INDENT); + let data = + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to get maker_swap_data from buffer: {error}"))?; + Ok(Row::new([caption, data])) +} + +fn taker_negotiated_data_row(timestamp: u64, neg_data: TakerNegotiationData) -> Result> { + let caption = format!("Negotiated\n{}\n", format_datetime_msec(timestamp)?); + let mut buff = vec![]; + let writer: &mut dyn Write = &mut buff; + writeln_field( + writer, + "taker_payment_locktime", + format_datetime_msec(neg_data.taker_payment_locktime)?, + 0, + ); + writeln_field(writer, "taker_pubkey", format_h264(neg_data.taker_pubkey), ZERO_INDENT); + write_field_option( + writer, + "maker_swap_contract", + neg_data.maker_coin_swap_contract_addr.map(format_bytes), + 0, + ); + write_field_option( + writer, + "taker_swap_contract", + neg_data.taker_coin_swap_contract_addr.map(format_bytes), + 0, + ); + write_field_option( + writer, + "maker_coin_htlc_pubkey", + neg_data.maker_coin_htlc_pubkey.map(format_h264), + 0, + ); + write_field_option( + writer, + "taker_coin_htlc_pubkey", + neg_data.taker_coin_htlc_pubkey.map(format_h264), + 0, + ); + + let data = String::from_utf8(buff) + .map_err(|error| error_anyhow!("Failed to get taker_negotiated_data from buffer: {error}"))?; + Ok(Row::new([caption, data])) +} + +fn get_opt_value_row Result>>( + caption: &str, + timestamp: u64, + value: Option, + delegate: F, +) -> Result> { + if let Some(value) = value { + delegate(caption, timestamp, value) + } else { + get_none_value_row(caption, timestamp) + } +} + +fn get_none_value_row(caption: &str, timestamp: u64) -> Result> { + let caption = format!("{}\n{}\n", caption, format_datetime_msec(timestamp)?); + Ok(Row::new([caption, "none".to_string()])) +} + +fn format_h264(bytes: H264) -> String { hex::encode(bytes.0) } diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/trading.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/trading.rs new file mode 100644 index 0000000000..d278387254 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/trading.rs @@ -0,0 +1,124 @@ +use anyhow::Result; +use mm2_rpc::data::legacy::MinTradingVolResponse; +use std::io::Write; + +use crate::komodefi_proc::response_handler::formatters::{term_table_blank, writeln_field, ZERO_INDENT}; +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; +use mm2_number::BigRational; +use term_table::row::Row; +use term_table::TableStyle; + +use super::formatters::{format_ratio, write_field_option, COMMON_PRECISION}; +use crate::rpc_data::{MakerPreimage, MaxTakerVolResponse, TakerPreimage, TotalTradeFeeResponse, TradeFeeResponse, + TradePreimageResponse}; + +pub(super) fn on_min_trading_vol(writer: &mut dyn Write, response: MinTradingVolResponse) -> Result<()> { + writeln_field(writer, "coin", response.coin, 0); + writeln_field( + writer, + "volume", + format_ratio(&response.volume.min_trading_vol, COMMON_PRECISION)?, + 0, + ); + Ok(()) +} + +pub(super) fn on_max_taker_vol(writer: &mut dyn Write, response: MaxTakerVolResponse) -> Result<()> { + writeln_field(writer, "coin", response.coin, 0); + writeln_field( + writer, + "result", + format_ratio(&BigRational::from(response.result), COMMON_PRECISION)?, + 0, + ); + Ok(()) +} + +pub(super) fn on_trade_preimage(writer: &mut dyn Write, response: TradePreimageResponse) -> Result<()> { + match response { + TradePreimageResponse::TakerPreimage(taker_preimage) => write_taker_preimage(writer, taker_preimage), + TradePreimageResponse::MakerPreimage(maker_preimage) => write_maker_preimage(writer, maker_preimage), + } +} + +fn write_taker_preimage(writer: &mut dyn Write, preimage: TakerPreimage) -> Result<()> { + writeln_field( + writer, + "base_coin_fee", + format_trade_coin_fee(preimage.base_coin_fee)?, + ZERO_INDENT, + ); + writeln_field( + writer, + "rel_coin_fee", + format_trade_coin_fee(preimage.rel_coin_fee)?, + ZERO_INDENT, + ); + writeln_field( + writer, + "taker_fee", + format_trade_coin_fee(preimage.taker_fee)?, + ZERO_INDENT, + ); + writeln_field( + writer, + "fee_to_send_taker_fee", + format_trade_coin_fee(preimage.fee_to_send_taker_fee)?, + ZERO_INDENT, + ); + writeln_field(writer, "total_fee", "", ZERO_INDENT); + write_total_trade_fee(writer, preimage.total_fees) +} + +fn write_maker_preimage(writer: &mut dyn Write, preimage: MakerPreimage) -> Result<()> { + writeln_field( + writer, + "base_coin_fee", + format_trade_coin_fee(preimage.base_coin_fee)?, + ZERO_INDENT, + ); + writeln_field( + writer, + "rel_coin_fee", + format_trade_coin_fee(preimage.rel_coin_fee)?, + ZERO_INDENT, + ); + write_field_option( + writer, + "volume", + preimage + .volume + .map(|v| format_ratio(&v.volume, COMMON_PRECISION).unwrap_or_else(|_| "error".to_string())), + ZERO_INDENT, + ); + writeln_field(writer, "total_fee", "", ZERO_INDENT); + write_total_trade_fee(writer, preimage.total_fees) +} + +fn format_trade_coin_fee(trade_coin_fee: TradeFeeResponse) -> Result { + Ok(format!( + "coin: {}, amount: {}, paid_from_trading_vol: {}", + trade_coin_fee.coin, + format_ratio(&trade_coin_fee.amount.amount, COMMON_PRECISION)?, + trade_coin_fee.paid_from_trading_vol + )) +} + +fn write_total_trade_fee(writer: &mut dyn Write, total_fee: Vec) -> Result<()> { + if total_fee.is_empty() { + writeln_field(writer, "total_fee", "empty", ZERO_INDENT); + return Ok(()); + } + + let mut term_table = term_table_blank(TableStyle::thin(), false, false, false); + term_table.add_row(Row::new(vec!["coin", "amount", "required_balance"])); + for fee in total_fee { + term_table.add_row(Row::new(vec![ + fee.coin, + format_ratio(&fee.amount.amount, COMMON_PRECISION)?, + format_ratio(&fee.required_balance.required_balance, COMMON_PRECISION)?, + ])); + } + writeln_safe_io!(writer, "{}", term_table.render()); + Ok(()) +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/utility.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/utility.rs new file mode 100644 index 0000000000..7851b0a6d6 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/utility.rs @@ -0,0 +1,67 @@ +use rpc::v1::types::H256 as H256Json; +use std::io::Write; +use term_table::{row::Row, TableStyle}; + +use crate::komodefi_proc::response_handler::formatters::{format_datetime_msec, term_table_blank, write_sequence, + writeln_field}; +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io}; + +use super::ZERO_INDENT; +use crate::rpc_data::utility::{GetCurrentMtpError, GetCurrentMtpResponse}; +use crate::rpc_data::{BanReason, ListBannedPubkeysResponse, UnbanPubkeysResponse}; + +pub(super) fn on_list_banned_pubkeys(writer: &mut dyn Write, response: ListBannedPubkeysResponse) { + if response.is_empty() { + writeln_field(writer, "banned_pubkeys", "empty", ZERO_INDENT); + return; + } + + let mut term_table = term_table_blank(TableStyle::thin(), false, false, false); + term_table.add_row(Row::new(["pubkey", "reason", "comment"])); + for (pubkey, ban_reason) in response { + match ban_reason { + BanReason::Manual { reason } => { + term_table.add_row(Row::new([hex::encode(pubkey.0), "manual".to_string(), reason])) + }, + BanReason::FailedSwap { caused_by_swap } => term_table.add_row(Row::new([ + hex::encode(pubkey.0), + "swap".to_string(), + caused_by_swap.to_string(), + ])), + } + } + writeln_safe_io!(writer, "{}", term_table.render()); +} + +pub(super) fn on_unban_pubkeys(writer: &mut dyn Write, response: UnbanPubkeysResponse) { + let still_banned = response.still_banned.iter().map(format_ban_reason); + write_sequence(writer, "still_banned", still_banned, ZERO_INDENT); + let unbanned = response.unbanned.iter().map(format_ban_reason); + write_sequence(writer, "unbanned", unbanned, ZERO_INDENT); + let were_not_banned = response.were_not_banned.iter(); + write_sequence(writer, "were_not_banned", were_not_banned, ZERO_INDENT); +} + +pub(super) fn on_current_mtp(writer: &mut dyn Write, response: GetCurrentMtpResponse) { + writeln_field( + writer, + "Current mtp", + format_datetime_msec(response.mtp as u64 * 1000).unwrap(), + ZERO_INDENT, + ); +} + +pub(super) fn on_get_current_mtp_error(writer: &mut dyn Write, error: GetCurrentMtpError) { + writeln_field(writer, "Failed to get current mtp", error, ZERO_INDENT); +} + +fn format_ban_reason((pubkey, ban_reason): (&H256Json, &BanReason)) -> String { + match ban_reason { + BanReason::Manual { reason } => { + format!("{}(manually \"{}\")", hex::encode(pubkey.0), reason) + }, + BanReason::FailedSwap { caused_by_swap } => { + format!("{}(caused_by_swap {})", hex::encode(pubkey.0), caused_by_swap) + }, + } +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/version_stat.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/version_stat.rs new file mode 100644 index 0000000000..eee874ece2 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/version_stat.rs @@ -0,0 +1,30 @@ +use std::io::Write; + +use mm2_rpc::data::legacy::Status; + +use super::formatters::{writeln_field, ZERO_INDENT}; +use crate::rpc_data::version_stat::NodeVersionError; + +pub(super) fn on_vstat_add_node(writer: &mut dyn Write, response: Status) { + writeln_field(writer, "Add node to version stat", response, ZERO_INDENT); +} + +pub(super) fn on_vstat_rem_node(writer: &mut dyn Write, response: Status) { + writeln_field(writer, "Remove node from version stat", response, ZERO_INDENT); +} + +pub(super) fn on_node_version_error(writer: &mut dyn Write, error: NodeVersionError) { + writeln_field(writer, "Failed to add node", error, ZERO_INDENT); +} + +pub(super) fn on_vstat_start_collection(writer: &mut dyn Write, response: Status) { + writeln_field(writer, "Start stat collection", response, ZERO_INDENT); +} + +pub(super) fn on_vstat_stop_collection(writer: &mut dyn Write, response: Status) { + writeln_field(writer, "Stop stat collection", response, ZERO_INDENT); +} + +pub(super) fn on_vstat_update_collection(writer: &mut dyn Write, response: Status) { + writeln_field(writer, "Update stat collection", response, ZERO_INDENT); +} diff --git a/mm2src/komodefi_cli/src/komodefi_proc/response_handler/wallet.rs b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/wallet.rs new file mode 100644 index 0000000000..6526c53f49 --- /dev/null +++ b/mm2src/komodefi_cli/src/komodefi_proc/response_handler/wallet.rs @@ -0,0 +1,350 @@ +use anyhow::{anyhow, Result}; +use itertools::Itertools; +use rpc::v1::types::Bytes as BytesJson; +use std::io::Write; +use term_table::{row::Row, TableStyle}; + +use common::log::error; +use common::{write_safe::io::WriteSafeIO, write_safe_io, writeln_safe_io, PagingOptionsEnum}; +use mm2_rpc::data::version2::{GetPublicKeyHashResponse, GetPublicKeyResponse, GetRawTransactionResponse}; + +use super::formatters::{format_bytes, format_datetime_sec, format_ratio, term_table_blank, write_field_option, + write_sequence, writeln_field, COMMON_INDENT, COMMON_PRECISION, ZERO_INDENT}; +use crate::error_anyhow; +use crate::rpc_data::wallet::{ConvertAddressResponse, ConvertUtxoAddressResponse, KmdRewardsInfoResponse, + MyTxHistoryDetails, MyTxHistoryResponse, MyTxHistoryResponseV2, ShowPrivateKeyResponse, + ValidateAddressResponse, ZcoinTxDetails}; +use crate::rpc_data::{KmdRewardsDetails, SendRawTransactionResponse, WithdrawResponse}; + +pub(super) fn on_send_raw_transaction(writer: &mut dyn Write, response: SendRawTransactionResponse, bare_output: bool) { + let bytes_to_show = hex::encode(response.tx_hash.as_slice()); + if bare_output { + writeln_safe_io!(writer, "{}", bytes_to_show) + } else { + writeln_field(writer, "tx_hash", bytes_to_show, ZERO_INDENT); + } +} + +pub(super) fn on_withdraw(writer: &mut dyn Write, response: WithdrawResponse, bare_output: bool) -> Result<()> { + if bare_output { + writeln_safe_io!(writer, "{}", format_bytes(response.tx_hex)); + return Ok(()); + } + writeln_field(writer, "coin", response.coin, ZERO_INDENT); + write_sequence(writer, "from", response.from.iter(), ZERO_INDENT); + write_sequence(writer, "to", response.to.iter(), ZERO_INDENT); + writeln_field(writer, "total_amount", response.total_amount, ZERO_INDENT); + writeln_field(writer, "spent_by_me", response.spent_by_me, ZERO_INDENT); + writeln_field(writer, "received_by_me", response.received_by_me, ZERO_INDENT); + writeln_field(writer, "my_balance_change", response.my_balance_change, ZERO_INDENT); + writeln_field(writer, "block_height", response.block_height, ZERO_INDENT); + writeln_field( + writer, + "timestamp", + format_datetime_sec(response.timestamp)?, + ZERO_INDENT, + ); + write_field_option(writer, "fee_details", response.fee_details, ZERO_INDENT); + writeln_field(writer, "internal_id", format_bytes(response.internal_id), ZERO_INDENT); + write_field_option( + writer, + "kmd_rewards", + response.kmd_rewards.map(format_kmd_rewards), + ZERO_INDENT, + ); + write_field_option(writer, "transaction_type", response.transaction_type, ZERO_INDENT); + write_field_option(writer, "memo", response.memo, ZERO_INDENT); + + writeln_field(writer, "tx_hash", response.tx_hash, ZERO_INDENT); + writeln_field(writer, "tx_hex", format_bytes(response.tx_hex), ZERO_INDENT); + + Ok(()) +} + +pub(super) fn on_tx_history(writer: &mut dyn Write, response: MyTxHistoryResponse) -> Result<()> { + write_field_option(writer, "from_id", response.from_id.map(format_bytes), ZERO_INDENT); + writeln_field(writer, "limit", response.limit, ZERO_INDENT); + writeln_field(writer, "skipped", response.skipped, ZERO_INDENT); + writeln_field(writer, "total", response.total, ZERO_INDENT); + write_field_option(writer, "page_number", response.page_number, ZERO_INDENT); + write_field_option(writer, "total_pages", response.total_pages, ZERO_INDENT); + writeln_field(writer, "current_block", response.current_block, ZERO_INDENT); + writeln_field(writer, "sync_status", response.sync_status, ZERO_INDENT); + write_transactions(writer, response.transactions)?; + Ok(()) +} + +pub(super) fn on_tx_history_v2( + writer: &mut dyn Write, + response: MyTxHistoryResponseV2, +) -> Result<()> { + writeln_field(writer, "coin", response.coin, ZERO_INDENT); + writeln_field(writer, "target", format!("{:?}", response.target), ZERO_INDENT); + writeln_field(writer, "current_block", response.current_block, ZERO_INDENT); + writeln_field(writer, "sync_status", response.sync_status, ZERO_INDENT); + writeln_field(writer, "limit", response.limit, ZERO_INDENT); + writeln_field(writer, "skipped", response.skipped, ZERO_INDENT); + writeln_field(writer, "total", response.total, ZERO_INDENT); + writeln_field(writer, "total_pages", response.total_pages, ZERO_INDENT); + match response.paging_options { + PagingOptionsEnum::FromId(bytes) => { + writeln_field(writer, "from_id", hex::encode(bytes.as_slice()), ZERO_INDENT) + }, + PagingOptionsEnum::PageNumber(page_number) => writeln_field(writer, "page_number", page_number, ZERO_INDENT), + } + write_transactions(writer, response.transactions) +} + +fn write_transactions(writer: &mut dyn Write, transactions: Vec) -> Result<()> { + if transactions.is_empty() { + writeln_field(writer, "transactions", "not found", ZERO_INDENT); + } else { + writeln_field(writer, "transactions", "", ZERO_INDENT); + let mut term_table = term_table_blank(TableStyle::thin(), true, false, false); + term_table.max_column_width = 150; + for tx in transactions { + let mut buff: Vec = vec![]; + let row_writer: &mut dyn Write = &mut buff; + writeln_field( + row_writer, + "time", + format_datetime_sec(tx.details.timestamp)?, + ZERO_INDENT, + ); + writeln_field(row_writer, "coin", tx.details.coin, ZERO_INDENT); + writeln_field(row_writer, "block", tx.details.block_height, ZERO_INDENT); + writeln_field(row_writer, "confirmations", tx.confirmations, ZERO_INDENT); + writeln_field(row_writer, "transaction_type", tx.details.transaction_type, ZERO_INDENT); + writeln_field( + row_writer, + "total_amount", + format_ratio(&tx.details.total_amount, COMMON_PRECISION)?, + ZERO_INDENT, + ); + writeln_field( + row_writer, + "spent_by_me", + format_ratio(&tx.details.spent_by_me, COMMON_PRECISION)?, + ZERO_INDENT, + ); + writeln_field( + row_writer, + "received_by_me", + format_ratio(&tx.details.received_by_me, COMMON_PRECISION)?, + ZERO_INDENT, + ); + writeln_field( + row_writer, + "my_balance_change", + format_ratio(&tx.details.my_balance_change, COMMON_PRECISION)?, + ZERO_INDENT, + ); + + write_field_option(row_writer, "fee_details", tx.details.fee_details, ZERO_INDENT); + if let Some(kmd_rewards) = tx.details.kmd_rewards { + writeln_field(row_writer, "kmd_rewards", "", ZERO_INDENT); + writeln_field( + row_writer, + "amount", + format_ratio(&kmd_rewards.amount, COMMON_PRECISION)?, + COMMON_INDENT, + ); + writeln_field(row_writer, "claimed_by_me", kmd_rewards.claimed_by_me, COMMON_INDENT); + } + writeln_field(row_writer, "tx_hash", tx.details.tx_hash, ZERO_INDENT); + writeln_field(row_writer, "from", tx.details.from.iter().join(", "), ZERO_INDENT); + writeln_field(row_writer, "to", tx.details.to.iter().join(", "), ZERO_INDENT); + writeln_field( + row_writer, + "internal_id", + format_bytes(tx.details.internal_id), + ZERO_INDENT, + ); + write_field_option(row_writer, "memo", tx.details.memo, ZERO_INDENT); + writeln_field( + row_writer, + "tx_hex", + hex::encode(tx.details.tx_hex.as_slice()), + ZERO_INDENT, + ); + + let data = + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to format tx_history row: {error}"))?; + term_table.add_row(Row::new([data])) + } + writeln_safe_io!(writer, "{}", term_table.render()) + } + Ok(()) +} + +pub(super) fn on_tx_history_zcoin( + writer: &mut dyn Write, + response: MyTxHistoryResponseV2, +) -> Result<()> { + writeln_field(writer, "coin", response.coin, ZERO_INDENT); + writeln_field(writer, "target", format!("{:?}", response.target), ZERO_INDENT); + writeln_field(writer, "current_block", response.current_block, ZERO_INDENT); + writeln_field(writer, "sync_status", response.sync_status, ZERO_INDENT); + writeln_field(writer, "limit", response.limit, ZERO_INDENT); + writeln_field(writer, "skipped", response.skipped, ZERO_INDENT); + writeln_field(writer, "total", response.total, ZERO_INDENT); + writeln_field(writer, "total_pages", response.total_pages, ZERO_INDENT); + match response.paging_options { + PagingOptionsEnum::FromId(id) => writeln_field(writer, "from_id", id, ZERO_INDENT), + PagingOptionsEnum::PageNumber(page_number) => writeln_field(writer, "page_number", page_number, ZERO_INDENT), + } + write_zcoin_transactions(writer, response.transactions) +} + +fn write_zcoin_transactions(writer: &mut dyn Write, transactions: Vec) -> Result<()> { + if transactions.is_empty() { + writeln_field(writer, "transactions", "not found", ZERO_INDENT); + } else { + writeln_field(writer, "transactions", "", ZERO_INDENT); + let mut term_table = term_table_blank(TableStyle::thin(), true, false, false); + term_table.max_column_width = 150; + for tx in transactions { + let mut buff: Vec = vec![]; + let row_writer: &mut dyn Write = &mut buff; + let timestamp = tx + .timestamp + .try_into() + .map_err(|err| error_anyhow!("Failed to cast timestamp to u64: {}", err))?; + writeln_field(row_writer, "coin", tx.coin, ZERO_INDENT); + writeln_field(row_writer, "timestamp", format_datetime_sec(timestamp)?, ZERO_INDENT); + writeln_field(row_writer, "tx_hash", tx.tx_hash, ZERO_INDENT); + writeln_field(row_writer, "from", tx.from.iter().join(", "), ZERO_INDENT); + writeln_field(row_writer, "to", tx.to.iter().join(", "), ZERO_INDENT); + writeln_field( + row_writer, + "spent_by_me", + format_ratio(&tx.spent_by_me, COMMON_PRECISION)?, + ZERO_INDENT, + ); + writeln_field( + row_writer, + "received_by_me", + format_ratio(&tx.received_by_me, COMMON_PRECISION)?, + ZERO_INDENT, + ); + writeln_field( + row_writer, + "my_balance_change", + format_ratio(&tx.my_balance_change, COMMON_PRECISION)?, + ZERO_INDENT, + ); + writeln_field(row_writer, "block_height", tx.block_height, ZERO_INDENT); + writeln_field(row_writer, "confirmations", tx.confirmations, ZERO_INDENT); + writeln_field( + row_writer, + "transaction_fee", + format_ratio(&tx.transaction_fee, COMMON_PRECISION)?, + ZERO_INDENT, + ); + writeln_field(row_writer, "internal_id", tx.internal_id, ZERO_INDENT); + + let data = + String::from_utf8(buff).map_err(|error| error_anyhow!("Failed to format tx_history row: {error}"))?; + + term_table.add_row(Row::new([data])) + } + writeln_safe_io!(writer, "{}", term_table.render()) + } + Ok(()) +} + +fn format_kmd_rewards(kmd_rewards: KmdRewardsDetails) -> String { + format!( + "amount: {}, claimed_by_me: {}", + kmd_rewards.amount, kmd_rewards.claimed_by_me + ) +} + +pub(super) fn on_public_key(writer: &mut dyn Write, response: GetPublicKeyResponse) { + writeln_field(writer, "public_key", response.public_key, ZERO_INDENT) +} + +pub(super) fn on_public_key_hash(writer: &mut dyn Write, response: GetPublicKeyHashResponse) { + writeln_field( + writer, + "public_key_hash", + hex::encode(response.public_key_hash.0), + ZERO_INDENT, + ) +} + +pub(super) fn on_raw_transaction(writer: &mut dyn Write, response: GetRawTransactionResponse, bare_output: bool) { + if bare_output { + writeln_safe_io!(writer, "{}", format_bytes(response.tx_hex)) + } else { + writeln_field(writer, "tx_hex", format_bytes(response.tx_hex), ZERO_INDENT); + } +} + +pub(super) fn on_private_key(writer: &mut dyn Write, response: ShowPrivateKeyResponse) { + writeln_field(writer, "coin", response.coin, ZERO_INDENT); + writeln_field(writer, "priv_key", response.priv_key, ZERO_INDENT); +} + +pub(super) fn on_validate_address(writer: &mut dyn Write, response: ValidateAddressResponse) { + writeln_field( + writer, + "valid", + if response.is_valid { "valid" } else { "invalid" }, + ZERO_INDENT, + ); + write_field_option(writer, "reason", response.reason, ZERO_INDENT); +} + +pub(super) fn on_kmd_rewards_info(writer: &mut dyn Write, response: KmdRewardsInfoResponse) -> Result<()> { + if response.is_empty() { + writeln_field(writer, "rewards_info", "not found", ZERO_INDENT); + } else { + writeln_field(writer, "rewards_info", "", ZERO_INDENT); + for info in response { + writeln_field(writer, "tx_hash", hex::encode(info.tx_hash.0), ZERO_INDENT); + write_field_option(writer, "height", info.height, ZERO_INDENT); + writeln_field(writer, "output_index", info.output_index, ZERO_INDENT); + writeln_field( + writer, + "amount", + format_ratio(&info.amount, COMMON_PRECISION)?, + ZERO_INDENT, + ); + writeln_field(writer, "locktime", format_datetime_sec(info.locktime)?, ZERO_INDENT); + writeln_field( + writer, + "accrued_rewards", + format_datetime_sec(info.locktime)?, + ZERO_INDENT, + ); + writeln_field(writer, "accrued_rewards", info.accrued_rewards, ZERO_INDENT); + if let Some(accrue_start_at) = info.accrue_start_at { + writeln_field( + writer, + "accrue_start_at", + format_datetime_sec(accrue_start_at)?, + ZERO_INDENT, + ); + } + if let Some(accrue_stop_at) = info.accrue_stop_at { + writeln_field( + writer, + "accrue_stop_at", + format_datetime_sec(accrue_stop_at)?, + ZERO_INDENT, + ); + } + writeln_safe_io!(writer, ""); + } + } + Ok(()) +} + +pub(super) fn on_convert_address(writer: &mut dyn Write, response: ConvertAddressResponse) { + writeln_field(writer, "address", response.address, ZERO_INDENT); +} + +pub(super) fn on_convert_utxo_address(writer: &mut dyn Write, response: ConvertUtxoAddressResponse) { + writeln_field(writer, "address", response, ZERO_INDENT); +} diff --git a/mm2src/adex_cli/src/logging.rs b/mm2src/komodefi_cli/src/logging.rs similarity index 97% rename from mm2src/adex_cli/src/logging.rs rename to mm2src/komodefi_cli/src/logging.rs index d02c6d6a31..4f49f64286 100644 --- a/mm2src/adex_cli/src/logging.rs +++ b/mm2src/komodefi_cli/src/logging.rs @@ -1,5 +1,5 @@ #[cfg(not(any(test, target_arch = "wasm32")))] -use log::LevelFilter; +use common::log::LevelFilter; #[cfg(not(any(test, target_arch = "wasm32")))] use std::io::Write; diff --git a/mm2src/adex_cli/src/main.rs b/mm2src/komodefi_cli/src/main.rs similarity index 70% rename from mm2src/adex_cli/src/main.rs rename to mm2src/komodefi_cli/src/main.rs index 4a6da7bacf..c7cf4e647f 100644 --- a/mm2src/adex_cli/src/main.rs +++ b/mm2src/komodefi_cli/src/main.rs @@ -1,9 +1,10 @@ #[cfg(not(target_arch = "wasm32"))] mod activation_scheme_db; -#[cfg(not(any(test, target_arch = "wasm32")))] mod adex_app; -#[cfg(not(target_arch = "wasm32"))] mod adex_config; -#[cfg(not(target_arch = "wasm32"))] mod adex_proc; +#[cfg(not(any(test, target_arch = "wasm32")))] mod app; #[cfg(not(target_arch = "wasm32"))] mod cli; +#[cfg(not(target_arch = "wasm32"))] mod cli_cmd_args; +#[cfg(not(target_arch = "wasm32"))] mod config; #[cfg(not(target_arch = "wasm32"))] mod helpers; +#[cfg(not(target_arch = "wasm32"))] mod komodefi_proc; mod logging; #[cfg(not(target_arch = "wasm32"))] mod rpc_data; #[cfg(not(target_arch = "wasm32"))] mod scenarios; @@ -17,6 +18,6 @@ fn main() {} #[tokio::main(flavor = "current_thread")] async fn main() { logging::init_logging(); - let app = adex_app::AdexApp::new(); + let app = app::KomodefiApp::new(); app.execute().await; } diff --git a/mm2src/komodefi_cli/src/rpc_data/activation.rs b/mm2src/komodefi_cli/src/rpc_data/activation.rs new file mode 100644 index 0000000000..537bb28780 --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/activation.rs @@ -0,0 +1,245 @@ +//! Contains rpc data layer structures that are not ready to become a part of the mm2_rpc::data module +//! +//! *Note: it's expected that the following data types will be moved to mm2_rpc::data when mm2 is refactored to be able to handle them* +//! + +#[path = "activation/bch.rs"] pub(crate) mod bch; +#[path = "activation/electrum.rs"] mod electrum; +#[path = "activation/enable.rs"] mod enable; +#[path = "activation/eth.rs"] pub(crate) mod eth; +#[path = "activation/tendermint.rs"] pub(crate) mod tendermint; +#[path = "activation/zcoin.rs"] pub(crate) mod zcoin; + +use serde::{Deserialize, Serialize}; +use std::collections::{HashMap, HashSet}; +use uuid::Uuid; + +use common::true_f; +use derive_more::Display; +use mm2_number::BigDecimal; +use mm2_rpc::data::legacy::{ElectrumProtocol, Mm2RpcResult}; + +use crate::rpc_data::eth::EthWithTokensActivationRequest; + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "get_enabled_coins")] +pub(crate) struct GetEnabledRequest {} + +pub(crate) enum ActivationMethod { + Legacy(ActivationMethodLegacy), + V2(ActivationMethodV2), +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(tag = "method", rename_all = "snake_case")] +pub(crate) enum ActivationMethodLegacy { + Enable(enable::EnableRequest), + Electrum(electrum::ElectrumRequest), +} + +#[derive(Deserialize, Serialize)] +#[serde(tag = "method", content = "params", rename_all = "snake_case")] +pub(crate) enum ActivationMethodV2 { + EnableBchWithTokens(EnablePlatformCoinWithTokensReq), + EnableSlp(EnableTokenRequest), + EnableTendermintWithAssets(EnablePlatformCoinWithTokensReq), + EnableTendermintToken(EnableTokenRequest), + EnableEthWithTokens(EnablePlatformCoinWithTokensReq), + EnableErc20(EnableTokenRequest), + #[serde(rename = "task::enable_z_coin::init")] + EnableZCoin(InitStandaloneCoinReq), +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct EnablePlatformCoinWithTokensReq { + ticker: String, + #[serde(flatten)] + request: T, +} + +impl SetTxHistory for EnablePlatformCoinWithTokensReq { + fn set_tx_history_impl(&mut self) { self.request.set_tx_history_impl() } +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct TokenActivationRequest { + ticker: String, + #[serde(flatten)] + request: Req, +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct EnableTokenRequest { + ticker: String, + activation_params: T, +} + +#[derive(Debug, Default, Deserialize, Serialize)] +#[serde(tag = "method", rename = "disable_coin")] +pub(crate) struct DisableCoinRequest { + pub(crate) coin: String, +} + +#[derive(Deserialize)] +#[serde(untagged)] +pub(crate) enum DisableCoinResponse { + Success(Mm2RpcResult), + Failed(DisableCoinFailed), +} + +#[derive(Debug, Deserialize)] +pub(crate) struct DisableCoinSuccess { + pub(crate) coin: String, + pub(crate) cancelled_orders: Vec, + pub(crate) passivized: bool, +} + +#[derive(Deserialize)] +pub(crate) struct DisableCoinFailed { + pub(crate) error: String, + pub(crate) orders: DisableFailedOrders, + pub(crate) active_swaps: Vec, +} + +#[derive(Deserialize)] +pub(crate) struct DisableFailedOrders { + pub(crate) matching: Vec, + pub(crate) cancelled: Vec, +} + +#[derive(Deserialize)] +pub(crate) struct SetRequiredConfResponse { + pub(crate) coin: String, + pub(crate) confirmations: u64, +} + +#[derive(Deserialize)] +pub(crate) struct SetRequiredNotaResponse { + pub(crate) coin: String, + pub(crate) requires_notarization: bool, +} + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "coins_needed_for_kick_start")] +pub(crate) struct CoinsToKickStartRequest {} + +pub(crate) type CoinsToKickstartResponse = Vec; + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct CoinBalance { + pub(crate) spendable: BigDecimal, + pub(crate) unspendable: BigDecimal, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct CoinAddressInfo { + pub(crate) derivation_method: DerivationMethod, + pub(crate) pubkey: String, + pub(crate) balances: Option, + pub(crate) tickers: Option>, +} + +pub(crate) type TokenBalances = HashMap; + +#[derive(Display, Debug, Deserialize)] +#[serde(tag = "type", content = "data")] +pub(crate) enum DerivationMethod { + Iguana, + #[allow(dead_code)] + HDWallet(String), +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct ElectrumRpcRequest { + pub(crate) url: String, + #[serde(default)] + pub(crate) protocol: ElectrumProtocol, + #[serde(default)] + pub(crate) disable_cert_verification: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct InitStandaloneCoinReq { + ticker: String, + activation_params: T, +} + +#[derive(Deserialize)] +pub(crate) struct InitRpcTaskResponse { + pub(crate) task_id: TaskId, +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct RpcTaskStatusRequest { + pub(crate) task_id: TaskId, + #[serde(default = "true_f")] + pub(crate) forget_if_finished: bool, +} + +pub(crate) type TaskId = u64; + +#[derive(Debug, Deserialize)] +#[serde(tag = "status", content = "details")] +pub(crate) enum RpcTaskStatus { + Ok(Item), + Error(Error), + InProgress(InProgressStatus), + UserActionRequired(AwaitingStatus), +} + +#[derive(Serialize)] +pub(crate) struct CancelRpcTaskRequest { + pub(crate) task_id: TaskId, +} + +#[derive(Display, Deserialize)] +#[serde(tag = "error_type", content = "error_data")] +pub(crate) enum CancelRpcTaskError { + #[display(fmt = "No such task '{}'", _0)] + NoSuchTask(TaskId), + #[display(fmt = "Task is finished already")] + TaskFinished(TaskId), + #[display(fmt = "Internal error: {}", _0)] + Internal(String), +} + +pub(crate) trait SetTxHistory { + fn set_tx_history_impl(&mut self); + fn set_tx_history(&mut self, tx_history: bool) { + if tx_history { + self.set_tx_history_impl(); + } + } +} + +impl SetTxHistory for ActivationMethod { + fn set_tx_history_impl(&mut self) { + match self { + Self::Legacy(method) => method.set_tx_history_impl(), + Self::V2(method) => method.set_tx_history_impl(), + } + } +} + +impl SetTxHistory for ActivationMethodLegacy { + fn set_tx_history_impl(&mut self) { + match self { + Self::Enable(ref mut method) => method.set_tx_history_impl(), + Self::Electrum(ref mut method) => method.set_tx_history_impl(), + } + } +} + +impl SetTxHistory for ActivationMethodV2 { + fn set_tx_history_impl(&mut self) { + match self { + Self::EnableBchWithTokens(method) => method.set_tx_history_impl(), + Self::EnableSlp(_) => {}, + Self::EnableEthWithTokens(_) => {}, + Self::EnableErc20(_) => {}, + Self::EnableTendermintWithAssets(method) => method.set_tx_history_impl(), + Self::EnableTendermintToken(_) => {}, + Self::EnableZCoin(_) => {}, + } + } +} diff --git a/mm2src/komodefi_cli/src/rpc_data/activation/bch.rs b/mm2src/komodefi_cli/src/rpc_data/activation/bch.rs new file mode 100644 index 0000000000..2be313499b --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/activation/bch.rs @@ -0,0 +1,124 @@ +use rpc::v1::types::H256 as H256Json; +use serde::{Deserialize, Serialize}; +use skip_serializing_none::skip_serializing_none; +use std::collections::HashMap; + +use common::true_f; +use mm2_rpc::data::legacy::UtxoMergeParams; + +use super::{CoinAddressInfo, CoinBalance, ElectrumRpcRequest, SetTxHistory, TokenActivationRequest, TokenBalances}; + +#[derive(Deserialize, Serialize)] +pub(crate) struct BchWithTokensActivationParams { + #[serde(flatten)] + platform_request: BchActivationRequest, + slp_tokens_requests: Vec>, + #[serde(default = "true_f")] + pub(crate) get_balances: bool, +} + +impl SetTxHistory for BchWithTokensActivationParams { + fn set_tx_history_impl(&mut self) { self.platform_request.utxo_params.tx_history = true; } +} + +#[derive(Deserialize, Serialize)] +pub(crate) struct BchActivationRequest { + #[serde(default)] + allow_slp_unsafe_conf: bool, + bchd_urls: Vec, + #[serde(flatten)] + pub(crate) utxo_params: UtxoActivationParams, +} + +#[skip_serializing_none] +#[derive(Deserialize, Serialize)] +pub(crate) struct UtxoActivationParams { + pub(crate) mode: UtxoRpcMode, + pub(crate) utxo_merge_params: Option, + #[serde(default)] + pub(crate) tx_history: bool, + pub(crate) required_confirmations: Option, + pub(crate) requires_notarization: Option, + pub(crate) address_format: Option, + pub(crate) gap_limit: Option, + #[serde(flatten)] + pub(crate) enable_params: EnabledCoinBalanceParams, + #[serde(default)] + pub(crate) priv_key_policy: PrivKeyActivationPolicy, + pub(crate) check_utxo_maturity: Option, +} + +#[derive(Deserialize, Serialize)] +#[serde(tag = "rpc", content = "rpc_data")] +pub(crate) enum UtxoRpcMode { + Native, + Electrum { servers: Vec }, +} + +#[derive(Deserialize, Serialize)] +#[serde(tag = "format")] +pub(crate) enum UtxoAddressFormat { + #[serde(rename = "standard")] + Standard, + #[serde(rename = "segwit")] + Segwit, + #[serde(rename = "cashaddress")] + CashAddress { + network: String, + #[serde(default)] + pub_addr_prefix: u8, + #[serde(default)] + p2sh_addr_prefix: u8, + }, +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) enum PrivKeyActivationPolicy { + ContextPrivKey, + Trezor, +} + +impl Default for PrivKeyActivationPolicy { + fn default() -> Self { PrivKeyActivationPolicy::ContextPrivKey } +} + +#[skip_serializing_none] +#[derive(Debug, Default, Deserialize, Serialize)] +pub(crate) struct EnabledCoinBalanceParams { + #[serde(default)] + pub(crate) scan_policy: EnableCoinScanPolicy, + pub(crate) min_addresses_number: Option, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub(crate) enum EnableCoinScanPolicy { + DoNotScan, + ScanIfNewWallet, + Scan, +} + +impl Default for EnableCoinScanPolicy { + fn default() -> Self { EnableCoinScanPolicy::ScanIfNewWallet } +} + +#[skip_serializing_none] +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct SlpActivationRequest { + pub(crate) required_confirmations: Option, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct BchWithTokensActivationResult { + pub(crate) current_block: u64, + pub(crate) bch_addresses_infos: HashMap>, + pub(crate) slp_addresses_infos: HashMap>, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct SlpInitResult { + pub(crate) balances: HashMap, + pub(crate) token_id: H256Json, + pub(crate) platform_coin: String, + pub(crate) required_confirmations: u64, +} diff --git a/mm2src/komodefi_cli/src/rpc_data/activation/electrum.rs b/mm2src/komodefi_cli/src/rpc_data/activation/electrum.rs new file mode 100644 index 0000000000..5b089697d3 --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/activation/electrum.rs @@ -0,0 +1,34 @@ +use mm2_rpc::data::legacy::{ElectrumProtocol, UtxoMergeParams}; +use serde::{Deserialize, Serialize}; +use skip_serializing_none::skip_serializing_none; + +use super::SetTxHistory; + +#[skip_serializing_none] +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct ElectrumRequest { + coin: String, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub(crate) servers: Vec, + mm2: Option, + #[serde(default)] + pub(crate) tx_history: bool, + required_confirmations: Option, + requires_notarization: Option, + swap_contract_address: Option, + fallback_swap_contract: Option, + utxo_merge_params: Option, +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct Server { + url: String, + #[serde(default)] + protocol: ElectrumProtocol, + #[serde(default)] + disable_cert_verification: bool, +} + +impl SetTxHistory for ElectrumRequest { + fn set_tx_history_impl(&mut self) { self.tx_history = true; } +} diff --git a/mm2src/komodefi_cli/src/rpc_data/activation/enable.rs b/mm2src/komodefi_cli/src/rpc_data/activation/enable.rs new file mode 100644 index 0000000000..d678b94acb --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/activation/enable.rs @@ -0,0 +1,28 @@ +use mm2_rpc::data::legacy::GasStationPricePolicy; +use serde::{Deserialize, Serialize}; +use skip_serializing_none::skip_serializing_none; + +use super::SetTxHistory; + +#[skip_serializing_none] +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct EnableRequest { + coin: String, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + urls: Vec, + swap_contract_address: Option, + fallback_swap_contract: Option, + gas_station_url: Option, + gas_station_decimals: Option, + gas_station_policy: Option, + mm2: Option, + #[serde(default)] + pub(crate) tx_history: bool, + required_confirmations: Option, + requires_notarization: Option, + contract_supports_watchers: Option, +} + +impl SetTxHistory for EnableRequest { + fn set_tx_history_impl(&mut self) { self.tx_history = true; } +} diff --git a/mm2src/komodefi_cli/src/rpc_data/activation/eth.rs b/mm2src/komodefi_cli/src/rpc_data/activation/eth.rs new file mode 100644 index 0000000000..4120b9150e --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/activation/eth.rs @@ -0,0 +1,89 @@ +use ethereum_types::Address; +use serde::{Deserialize, Serialize}; +use skip_serializing_none::skip_serializing_none; +use std::collections::HashMap; + +use common::true_f; +use mm2_rpc::data::legacy::GasStationPricePolicy; + +use super::{CoinAddressInfo, CoinBalance, TokenActivationRequest, TokenBalances}; + +#[derive(Deserialize, Serialize)] +pub(crate) struct EthWithTokensActivationRequest { + #[serde(flatten)] + platform_request: EthActivationV2Request, + erc20_tokens_requests: Vec>, + #[serde(default = "true_f")] + pub(crate) get_balances: bool, +} + +#[skip_serializing_none] +#[derive(Deserialize, Serialize)] +pub(crate) struct EthActivationV2Request { + #[serde(default)] + nodes: Vec, + #[serde(default)] + rpc_mode: EthRpcMode, + swap_contract_address: Address, + fallback_swap_contract: Option
, + #[serde(default)] + contract_supports_watchers: bool, + gas_station_url: Option, + gas_station_decimals: Option, + #[serde(default)] + gas_station_policy: GasStationPricePolicy, + mm2: Option, + required_confirmations: Option, + #[serde(default)] + priv_key_policy: EthPrivKeyActivationPolicy, +} + +#[derive(Deserialize, Serialize)] +pub(crate) struct EthNode { + url: String, + #[serde(default)] + gui_auth: bool, +} + +#[derive(Deserialize, Serialize)] +pub(crate) enum EthRpcMode { + Http, + #[cfg(target_arch = "wasm32")] + Metamask, +} + +impl Default for EthRpcMode { + fn default() -> Self { EthRpcMode::Http } +} + +#[derive(Deserialize, Serialize)] +pub(crate) enum EthPrivKeyActivationPolicy { + ContextPrivKey, + #[cfg(target_arch = "wasm32")] + Metamask, +} + +impl Default for EthPrivKeyActivationPolicy { + fn default() -> Self { EthPrivKeyActivationPolicy::ContextPrivKey } +} + +#[skip_serializing_none] +#[derive(Deserialize, Serialize)] +pub(crate) struct Erc20TokenActivationRequest { + required_confirmations: Option, +} + +#[derive(Deserialize)] +pub(crate) struct Erc20InitResult { + pub(crate) balances: HashMap, + pub(crate) platform_coin: String, + pub(crate) token_contract_address: String, + pub(crate) required_confirmations: u64, +} + +#[derive(Deserialize)] +pub(crate) struct EthWithTokensActivationResult { + pub(crate) current_block: u64, + pub(crate) eth_addresses_infos: HashMap>, + pub(crate) erc20_addresses_infos: HashMap>, +} diff --git a/mm2src/komodefi_cli/src/rpc_data/activation/tendermint.rs b/mm2src/komodefi_cli/src/rpc_data/activation/tendermint.rs new file mode 100644 index 0000000000..2e1fbbfac2 --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/activation/tendermint.rs @@ -0,0 +1,41 @@ +use serde::{Deserialize, Serialize}; +use skip_serializing_none::skip_serializing_none; +use std::collections::{HashMap, HashSet}; + +use common::true_f; + +use super::{CoinBalance, SetTxHistory, TokenActivationRequest}; + +#[derive(Deserialize, Serialize)] +pub(crate) struct TendermintActivationParams { + rpc_urls: Vec, + tokens_params: Vec>, + #[serde(default)] + tx_history: bool, + #[serde(default = "true_f")] + get_balances: bool, +} + +impl SetTxHistory for TendermintActivationParams { + fn set_tx_history_impl(&mut self) { self.tx_history = true; } +} + +#[derive(Deserialize, Serialize)] +pub(crate) struct TendermintTokenActivationParams {} + +#[skip_serializing_none] +#[derive(Deserialize, Serialize)] +pub(crate) struct TendermintActivationResult { + pub(crate) ticker: String, + pub(crate) address: String, + pub(crate) current_block: u64, + pub(crate) balance: Option, + pub(crate) tokens_balances: Option>, + pub(crate) tokens_tickers: Option>, +} + +#[derive(Deserialize, Serialize)] +pub(crate) struct TendermintTokenInitResult { + pub(crate) balances: HashMap, + pub(crate) platform_coin: String, +} diff --git a/mm2src/komodefi_cli/src/rpc_data/activation/zcoin.rs b/mm2src/komodefi_cli/src/rpc_data/activation/zcoin.rs new file mode 100644 index 0000000000..ce88a927fc --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/activation/zcoin.rs @@ -0,0 +1,270 @@ +use derive_more::Display; +use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; +use serde::{Deserialize, Serialize}; +use skip_serializing_none::skip_serializing_none; +use std::time::Duration; + +use common::one_thousand_u32; + +use super::{CoinBalance, ElectrumRpcRequest, RpcTaskStatus, TaskId}; +use crate::rpc_data::Bip44Chain; + +#[skip_serializing_none] +#[derive(Deserialize, Serialize)] +pub(crate) struct ZcoinActivationParams { + pub(crate) mode: ZcoinRpcMode, + pub(crate) required_confirmations: Option, + pub(crate) requires_notarization: Option, + pub(crate) zcash_params_path: Option, + #[serde(default = "one_thousand_u32")] + pub(crate) scan_blocks_per_iteration: u32, + #[serde(default)] + pub(crate) scan_interval_ms: u64, +} + +#[derive(Deserialize, Serialize)] +#[serde(tag = "rpc", content = "rpc_data")] +pub(crate) enum ZcoinRpcMode { + #[cfg(not(target_arch = "wasm32"))] + Native, + Light { + electrum_servers: Vec, + light_wallet_d_servers: Vec, + }, +} + +pub(crate) type ZCoinStatus = + RpcTaskStatus; + +#[derive(Display, Deserialize)] +#[serde(tag = "error_type", content = "error_data")] +pub(crate) enum InitStandaloneCoinError { + #[display(fmt = "No such task '{}'", _0)] + NoSuchTask(TaskId), + #[display(fmt = "Initialization task has timed out {:?}", duration)] + TaskTimedOut { duration: Duration }, + #[display(fmt = "Coin {} is activated already", ticker)] + CoinIsAlreadyActivated { ticker: String }, + #[display(fmt = "Coin {} config is not found", _0)] + CoinConfigIsNotFound(String), + #[display(fmt = "Coin {} protocol parsing failed: {}", ticker, error)] + CoinProtocolParseError { ticker: String, error: String }, + #[display(fmt = "Unexpected platform protocol {:?} for {}", protocol, ticker)] + UnexpectedCoinProtocol { ticker: String, protocol: CoinProtocol }, + #[display(fmt = "Error on platform coin {} creation: {}", ticker, error)] + CoinCreationError { ticker: String, error: String }, + #[display(fmt = "{}", _0)] + HwError(HwRpcError), + #[display(fmt = "Transport error: {}", _0)] + Transport(String), + #[display(fmt = "Internal error: {}", _0)] + Internal(String), +} + +#[derive(Clone, Debug, Display, Serialize, Deserialize)] +pub(crate) enum HwRpcError { + #[display(fmt = "No Trezor device available")] + NoTrezorDeviceAvailable = 0, + #[display(fmt = "Found multiple devices. Please unplug unused devices")] + FoundMultipleDevices, + #[display(fmt = "Found unexpected device. Please re-initialize Hardware wallet")] + FoundUnexpectedDevice, + #[display(fmt = "Pin is invalid")] + InvalidPin, + #[display(fmt = "Unexpected message")] + UnexpectedMessage, + #[display(fmt = "Button expected")] + ButtonExpected, + #[display(fmt = "Got data error")] + DataError, + #[display(fmt = "Pin expected")] + PinExpected, + #[display(fmt = "Invalid signature")] + InvalidSignature, + #[display(fmt = "Got process error")] + ProcessError, + #[display(fmt = "Not enough funds")] + NotEnoughFunds, + #[display(fmt = "Not initialized")] + NotInitialized, + #[display(fmt = "Wipe code mismatch")] + WipeCodeMismatch, + #[display(fmt = "Invalid session")] + InvalidSession, + #[display(fmt = "Got firmware error")] + FirmwareError, + #[display(fmt = "Failure message not found")] + FailureMessageNotFound, + #[display(fmt = "User cancelled action")] + UserCancelled, + #[display(fmt = "PONG message mismatch after ping")] + PongMessageMismatch, +} + +#[derive(Display, Deserialize)] +#[non_exhaustive] +pub(crate) enum ZcoinInProgressStatus { + #[display(fmt = "Activating coin")] + ActivatingCoin, + #[display( + fmt = "Updating block cache, current_scanned_block: {}, latest_block: {}", + current_scanned_block, + latest_block + )] + UpdatingBlocksCache { + current_scanned_block: u64, + latest_block: u64, + }, + #[display( + fmt = "Building wallet db, current_scanned_block: {}, latest_block: {}", + current_scanned_block, + latest_block + )] + BuildingWalletDb { + current_scanned_block: u64, + latest_block: u64, + }, + #[display(fmt = "Temporary error: {}", _0)] + TemporaryError(String), + #[display(fmt = "Requesting wallet balance")] + RequestingWalletBalance, + #[display(fmt = "Finishing")] + Finishing, + #[display(fmt = "Waiting for trezor to connect")] + WaitingForTrezorToConnect, + #[display(fmt = "Waiting for user to confirm")] + WaitingForUserToConfirmPubkey, +} + +pub(crate) type ZcoinAwaitingStatus = HwRpcTaskAwaitingStatus; + +#[derive(Display, Deserialize)] +pub(crate) enum HwRpcTaskAwaitingStatus { + EnterTrezorPin, + EnterTrezorPassphrase, +} + +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug, Deserialize)] +#[serde(tag = "type", content = "protocol_data")] +pub(crate) enum CoinProtocol { + ZHTLC(ZcoinProtocolInfo), +} + +#[derive(Debug, Deserialize)] +pub(crate) struct ZcoinProtocolInfo { + pub(crate) consensus_params: ZcoinConsensusParams, + pub(crate) check_point_block: Option, + pub(crate) z_derivation_path: Option, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct CheckPointBlockInfo { + pub(crate) height: u32, + pub(crate) hash: H256Json, + pub(crate) time: u32, + pub(crate) sapling_tree: BytesJson, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct ZcoinConsensusParams { + pub(crate) overwinter_activation_height: u32, + pub(crate) sapling_activation_height: u32, + pub(crate) blossom_activation_height: Option, + pub(crate) heartwood_activation_height: Option, + pub(crate) canopy_activation_height: Option, + pub(crate) coin_type: u32, + pub(crate) hrp_sapling_extended_spending_key: String, + pub(crate) hrp_sapling_extended_full_viewing_key: String, + pub(crate) hrp_sapling_payment_address: String, + pub(crate) b58_pubkey_address_prefix: [u8; 2], + pub(crate) b58_script_address_prefix: [u8; 2], +} + +#[rustfmt::skip] +pub(crate) type StandardHDPathToCoin = +Bip32Child>; + +#[derive(Debug, Deserialize)] +pub(crate) struct Bip32Child { + pub(crate) value: Value, + pub(crate) child: Child, +} + +#[repr(u32)] +#[derive(Debug, Display, Deserialize)] +pub(crate) enum Bip43Purpose { + Bip32 = 32, + Bip44 = 44, + Bip49 = 49, + Bip84 = 84, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct Bip32PurposeValue { + pub(crate) purpose: Bip43Purpose, +} + +pub(crate) type HardenedValue = AnyValue; + +#[derive(Debug, Deserialize)] +pub(crate) struct AnyValue { + pub(crate) number: u32, +} + +#[derive(Debug, Display, Deserialize)] +pub(crate) struct Bip44Tail; + +#[derive(Deserialize)] +pub(crate) struct ZcoinActivationResult { + pub(crate) ticker: String, + pub(crate) current_block: u64, + pub(crate) wallet_balance: CoinBalanceReport, +} + +#[derive(Deserialize)] +#[serde(tag = "wallet_type")] +pub(crate) enum CoinBalanceReport { + Iguana(IguanaWalletBalance), + HD(HDWalletBalance), +} + +#[derive(Deserialize)] +pub(crate) struct IguanaWalletBalance { + pub(crate) address: String, + pub(crate) balance: CoinBalance, +} + +#[derive(Deserialize)] +pub(crate) struct HDWalletBalance { + pub(crate) accounts: Vec, +} + +#[derive(Deserialize)] +pub(crate) struct HDAccountBalance { + pub(crate) account_index: u32, + pub(crate) derivation_path: RpcDerivationPath, + pub(crate) total_balance: CoinBalance, + pub(crate) addresses: Vec, +} + +#[derive(Deserialize)] +pub(crate) struct HDAddressBalance { + pub(crate) address: String, + pub(crate) derivation_path: RpcDerivationPath, + pub(crate) chain: Bip44Chain, + pub(crate) balance: CoinBalance, +} + +#[derive(Deserialize)] +pub(crate) struct RpcDerivationPath(pub(crate) DerivationPath); + +#[derive(Deserialize)] +pub(crate) struct DerivationPath { + pub(crate) path: Vec, +} + +#[derive(Deserialize)] +pub(crate) struct ChildNumber(pub(crate) u32); diff --git a/mm2src/komodefi_cli/src/rpc_data/message_signing.rs b/mm2src/komodefi_cli/src/rpc_data/message_signing.rs new file mode 100644 index 0000000000..d6d302a083 --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/message_signing.rs @@ -0,0 +1,56 @@ +use derive_more::Display; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize)] +pub(crate) struct SignatureRequest { + pub(crate) coin: String, + pub(crate) message: String, +} + +#[derive(Deserialize)] +pub(crate) struct SignatureResponse { + pub(crate) signature: String, +} + +#[derive(Display, Deserialize)] +#[serde(tag = "error_type", content = "error_data")] +pub(crate) enum SignatureError { + #[display(fmt = "Invalid request: {}", _0)] + InvalidRequest(String), + #[display(fmt = "Internal error: {}", _0)] + InternalError(String), + #[display(fmt = "Coin is not found: {}", _0)] + CoinIsNotFound(String), + #[display(fmt = "sign_message_prefix is not set in coin config")] + PrefixNotFound, +} + +#[derive(Serialize)] +pub(crate) struct VerificationRequest { + pub(crate) coin: String, + pub(crate) message: String, + pub(crate) signature: String, + pub(crate) address: String, +} + +#[derive(Deserialize)] +pub(crate) struct VerificationResponse { + pub(crate) is_valid: bool, +} + +#[derive(Display, Deserialize)] +#[serde(tag = "error_type", content = "error_data")] +pub(crate) enum VerificationError { + #[display(fmt = "Invalid request: {}", _0)] + InvalidRequest(String), + #[display(fmt = "Internal error: {}", _0)] + InternalError(String), + #[display(fmt = "Signature decoding error: {}", _0)] + SignatureDecodingError(String), + #[display(fmt = "Address decoding error: {}", _0)] + AddressDecodingError(String), + #[display(fmt = "Coin is not found: {}", _0)] + CoinIsNotFound(String), + #[display(fmt = "sign_message_prefix is not set in coin config")] + PrefixNotFound, +} diff --git a/mm2src/komodefi_cli/src/rpc_data/mod.rs b/mm2src/komodefi_cli/src/rpc_data/mod.rs new file mode 100644 index 0000000000..ef89049179 --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/mod.rs @@ -0,0 +1,62 @@ +//! Contains rpc data layer structures that are not ready to become a part of the mm2_rpc::data module +//! +//! *Note: it's expected that the following data types will be moved to mm2_rpc::data when mm2 is refactored to be able to handle them* +//! + +pub(crate) mod activation; +pub(crate) mod message_signing; +pub(crate) mod network; +pub(crate) mod swaps; +pub(crate) mod trade_preimage; +pub(crate) mod utility; +pub(crate) mod version_stat; +pub(crate) mod wallet; + +pub(crate) use activation::{bch, eth, tendermint, zcoin, CancelRpcTaskError, CancelRpcTaskRequest, + CoinsToKickStartRequest, CoinsToKickstartResponse, DisableCoinFailed, DisableCoinRequest, + DisableCoinResponse, DisableCoinSuccess, GetEnabledRequest, SetRequiredConfResponse, + SetRequiredNotaResponse}; +pub(crate) use network::{GetGossipMeshRequest, GetGossipMeshResponse, GetGossipPeerTopicsRequest, + GetGossipPeerTopicsResponse, GetGossipTopicPeersRequest, GetGossipTopicPeersResponse, + GetMyPeerIdRequest, GetMyPeerIdResponse, GetPeersInfoRequest, GetPeersInfoResponse, + GetRelayMeshRequest, GetRelayMeshResponse}; +pub(crate) use swaps::{ActiveSwapsRequest, ActiveSwapsResponse, MakerNegotiationData, MakerSavedEvent, MakerSavedSwap, + MakerSwapData, MakerSwapEvent, MyRecentSwapResponse, MyRecentSwapsRequest, MySwapStatusRequest, + MySwapStatusResponse, Params, PaymentInstructions, RecoverFundsOfSwapRequest, + RecoverFundsOfSwapResponse, SavedSwap, SavedTradeFee, SwapError, TakerNegotiationData, + TakerPaymentSpentData, TakerSavedEvent, TakerSavedSwap, TakerSwapData, TakerSwapEvent, + TransactionIdentifier}; + +pub(crate) use trade_preimage::{MakerPreimage, MaxTakerVolRequest, MaxTakerVolResponse, MinTradingVolRequest, + TakerPreimage, TotalTradeFeeResponse, TradeFeeResponse, TradePreimageMethod, + TradePreimageRequest, TradePreimageResponse}; +pub(crate) use utility::{BanReason, ListBannedPubkeysRequest, ListBannedPubkeysResponse, UnbanPubkeysRequest, + UnbanPubkeysResponse}; +pub(crate) use wallet::{Bip44Chain, KmdRewardsDetails, SendRawTransactionRequest, SendRawTransactionResponse, + WithdrawFee, WithdrawFrom, WithdrawRequest, WithdrawResponse}; + +use serde::Deserialize; + +use mm2_rpc::data::version2::MmRpcVersion; + +#[derive(Deserialize)] +pub struct MmRpcResponseV2 { + pub mmrpc: MmRpcVersion, + #[serde(flatten)] + pub result: MmRpcResultV2, + pub id: Option, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(untagged)] +pub enum MmRpcResultV2 { + Ok { result: T }, + Err(MmRpcErrorV2), +} + +#[derive(Clone, Debug, Deserialize)] +pub struct MmRpcErrorV2 { + pub error: String, + pub error_path: String, + pub error_trace: String, +} diff --git a/mm2src/komodefi_cli/src/rpc_data/network.rs b/mm2src/komodefi_cli/src/rpc_data/network.rs new file mode 100644 index 0000000000..b7b368dc3e --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/network.rs @@ -0,0 +1,38 @@ +use serde::Serialize; +use std::collections::HashMap; + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "get_gossip_mesh")] +pub(crate) struct GetGossipMeshRequest {} + +pub(crate) type GetGossipMeshResponse = HashMap>; + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "get_gossip_peer_topics")] +pub(crate) struct GetGossipPeerTopicsRequest {} + +pub(crate) type GetGossipPeerTopicsResponse = HashMap>; + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "get_relay_mesh")] +pub(crate) struct GetRelayMeshRequest {} + +pub(crate) type GetRelayMeshResponse = Vec; + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "get_gossip_topic_peers")] +pub(crate) struct GetGossipTopicPeersRequest {} + +pub(crate) type GetGossipTopicPeersResponse = HashMap>; + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "get_my_peer_id")] +pub(crate) struct GetMyPeerIdRequest {} + +pub(crate) type GetMyPeerIdResponse = String; + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "get_peers_info")] +pub(crate) struct GetPeersInfoRequest {} + +pub(crate) type GetPeersInfoResponse = HashMap>; diff --git a/mm2src/komodefi_cli/src/rpc_data/swaps.rs b/mm2src/komodefi_cli/src/rpc_data/swaps.rs new file mode 100644 index 0000000000..dd7d166a74 --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/swaps.rs @@ -0,0 +1,349 @@ +use lightning_invoice::Invoice; +use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264 as H264Json}; +use serde::{Deserialize, Serialize}; +use skip_serializing_none::skip_serializing_none; +use std::collections::HashMap; +use uuid::Uuid; + +use mm2_number::BigDecimal; +use mm2_rpc::data::legacy::{MySwapsFilter, RecoveredSwapAction}; + +#[derive(Serialize)] +#[serde(tag = "method", rename = "active_swaps")] +pub(crate) struct ActiveSwapsRequest { + pub(crate) include_status: bool, +} + +#[derive(Deserialize)] +pub(crate) struct ActiveSwapsResponse { + #[allow(dead_code)] + pub(crate) uuids: Vec, + pub(crate) statuses: Option>, +} + +#[derive(Serialize)] +#[serde(tag = "method", rename = "my_swap_status")] +pub(crate) struct MySwapStatusRequest { + pub(crate) params: Params, +} + +#[derive(Serialize)] +pub(crate) struct Params { + pub(crate) uuid: Uuid, +} + +#[derive(Serialize)] +#[serde(tag = "method", rename = "recover_funds_of_swap")] +pub(crate) struct RecoverFundsOfSwapRequest { + pub(crate) params: Params, +} + +#[derive(Deserialize)] +pub(crate) struct RecoverFundsOfSwapResponse { + pub(crate) action: RecoveredSwapAction, + pub(crate) coin: String, + pub(crate) tx_hash: BytesJson, + pub(crate) tx_hex: BytesJson, +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "type")] +pub(crate) enum SavedSwap { + Maker(MakerSavedSwap), + Taker(TakerSavedSwap), +} + +#[derive(Debug, Deserialize)] +pub(crate) struct MakerSavedSwap { + pub(crate) uuid: Uuid, + pub(crate) my_order_uuid: Option, + pub(crate) events: Vec, + pub(crate) maker_amount: Option, + pub(crate) maker_coin: Option, + pub(crate) maker_coin_usd_price: Option, + pub(crate) taker_amount: Option, + pub(crate) taker_coin: Option, + pub(crate) taker_coin_usd_price: Option, + pub(crate) gui: Option, + pub(crate) mm_version: Option, + #[allow(dead_code)] + pub(crate) success_events: Vec, + #[allow(dead_code)] + pub(crate) error_events: Vec, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct TakerSavedSwap { + pub(crate) uuid: Uuid, + pub(crate) my_order_uuid: Option, + pub(crate) events: Vec, + pub(crate) maker_amount: Option, + pub(crate) maker_coin: Option, + pub(crate) maker_coin_usd_price: Option, + pub(crate) taker_amount: Option, + pub(crate) taker_coin: Option, + pub(crate) taker_coin_usd_price: Option, + pub(crate) gui: Option, + pub(crate) mm_version: Option, + #[allow(dead_code)] + pub(crate) success_events: Vec, + #[allow(dead_code)] + pub(crate) error_events: Vec, +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct MakerSavedEvent { + pub(crate) timestamp: u64, + pub(crate) event: MakerSwapEvent, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct TakerSavedEvent { + pub(crate) timestamp: u64, + pub(crate) event: TakerSwapEvent, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(tag = "type", content = "data")] +#[allow(clippy::large_enum_variant)] +pub(crate) enum MakerSwapEvent { + Started(MakerSwapData), + StartFailed(SwapError), + Negotiated(TakerNegotiationData), + NegotiateFailed(SwapError), + MakerPaymentInstructionsReceived(Option), + TakerFeeValidated(TransactionIdentifier), + TakerFeeValidateFailed(SwapError), + MakerPaymentSent(TransactionIdentifier), + MakerPaymentTransactionFailed(SwapError), + MakerPaymentDataSendFailed(SwapError), + MakerPaymentWaitConfirmFailed(SwapError), + TakerPaymentReceived(TransactionIdentifier), + TakerPaymentWaitConfirmStarted, + TakerPaymentValidatedAndConfirmed, + TakerPaymentValidateFailed(SwapError), + TakerPaymentWaitConfirmFailed(SwapError), + TakerPaymentSpent(TransactionIdentifier), + TakerPaymentSpendFailed(SwapError), + TakerPaymentSpendConfirmStarted, + TakerPaymentSpendConfirmed, + TakerPaymentSpendConfirmFailed(SwapError), + MakerPaymentWaitRefundStarted { wait_until: u64 }, + MakerPaymentRefundStarted, + MakerPaymentRefunded(Option), + MakerPaymentRefundFailed(SwapError), + MakerPaymentRefundFinished, + Finished, +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct MakerSwapData { + pub(crate) taker_coin: String, + pub(crate) maker_coin: String, + pub(crate) taker: H256Json, + pub(crate) secret: H256Json, + pub(crate) secret_hash: Option, + pub(crate) my_persistent_pub: H264Json, + pub(crate) lock_duration: u64, + pub(crate) maker_amount: BigDecimal, + pub(crate) taker_amount: BigDecimal, + pub(crate) maker_payment_confirmations: u64, + pub(crate) maker_payment_requires_nota: Option, + pub(crate) taker_payment_confirmations: u64, + pub(crate) taker_payment_requires_nota: Option, + pub(crate) maker_payment_lock: u64, + /// Allows to recognize one SWAP from the other in the logs. #274. + pub(crate) uuid: Uuid, + pub(crate) started_at: u64, + pub(crate) maker_coin_start_block: u64, + pub(crate) taker_coin_start_block: u64, + /// A `MakerPayment` transaction fee. + /// Note this value is used to calculate locked amount only. + pub(crate) maker_payment_trade_fee: Option, + /// A transaction fee that should be paid to spend a `TakerPayment`. + /// Note this value is used to calculate locked amount only. + pub(crate) taker_payment_spend_trade_fee: Option, + pub(crate) maker_coin_swap_contract_address: Option, + pub(crate) taker_coin_swap_contract_address: Option, + /// Temporary pubkey used in HTLC redeem script when applicable for maker coin + pub(crate) maker_coin_htlc_pubkey: Option, + /// Temporary pubkey used in HTLC redeem script when applicable for taker coin + pub(crate) taker_coin_htlc_pubkey: Option, + /// Temporary privkey used to sign P2P messages when applicable + pub(crate) p2p_privkey: Option, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(tag = "type", content = "data")] +#[allow(clippy::large_enum_variant)] +pub(crate) enum TakerSwapEvent { + Started(TakerSwapData), + StartFailed(SwapError), + Negotiated(MakerNegotiationData), + NegotiateFailed(SwapError), + TakerFeeSent(TransactionIdentifier), + TakerFeeSendFailed(SwapError), + TakerPaymentInstructionsReceived(Option), + MakerPaymentReceived(TransactionIdentifier), + MakerPaymentWaitConfirmStarted, + MakerPaymentValidatedAndConfirmed, + MakerPaymentValidateFailed(SwapError), + MakerPaymentWaitConfirmFailed(SwapError), + TakerPaymentSent(TransactionIdentifier), + WatcherMessageSent(Option>, Option>), + TakerPaymentTransactionFailed(SwapError), + TakerPaymentDataSendFailed(SwapError), + TakerPaymentWaitConfirmFailed(SwapError), + TakerPaymentSpent(TakerPaymentSpentData), + TakerPaymentWaitForSpendFailed(SwapError), + MakerPaymentSpent(TransactionIdentifier), + MakerPaymentSpendFailed(SwapError), + TakerPaymentWaitRefundStarted { wait_until: u64 }, + TakerPaymentRefundStarted, + TakerPaymentRefunded(Option), + TakerPaymentRefundFailed(SwapError), + TakerPaymentRefundFinished, + Finished, +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct SwapError { + pub(crate) error: String, +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct TakerNegotiationData { + pub(crate) taker_payment_locktime: u64, + pub(crate) taker_pubkey: H264Json, + pub(crate) maker_coin_swap_contract_addr: Option, + pub(crate) taker_coin_swap_contract_addr: Option, + pub(crate) maker_coin_htlc_pubkey: Option, + pub(crate) taker_coin_htlc_pubkey: Option, +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) enum PaymentInstructions { + #[cfg(not(target_arch = "wasm32"))] + Lightning(Invoice), + WatcherReward(BigDecimal), +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct TransactionIdentifier { + /// Raw bytes of signed transaction in hexadecimal string, this should be sent as is to send_raw_transaction RPC to broadcast the transaction. + /// Some payments like lightning payments don't have a tx_hex, for such payments tx_hex will be equal to tx_hash. + pub(crate) tx_hex: BytesJson, + /// Transaction hash in hexadecimal format + pub(crate) tx_hash: BytesJson, +} + +#[derive(Clone, Debug, Default, Deserialize)] +pub(crate) struct SavedTradeFee { + pub(crate) coin: String, + pub(crate) amount: BigDecimal, + #[serde(default)] + pub(crate) paid_from_trading_vol: bool, +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct SerializableSecp256k1Keypair { + pub(crate) inner: String, +} + +#[derive(Clone, Debug, Default, Deserialize)] +pub(crate) struct TakerSwapData { + pub(crate) taker_coin: String, + pub(crate) maker_coin: String, + pub(crate) maker: H256Json, + pub(crate) my_persistent_pub: H264Json, + pub(crate) lock_duration: u64, + pub(crate) maker_amount: BigDecimal, + pub(crate) taker_amount: BigDecimal, + pub(crate) maker_payment_confirmations: u64, + pub(crate) maker_payment_requires_nota: Option, + pub(crate) taker_payment_confirmations: u64, + pub(crate) taker_payment_requires_nota: Option, + pub(crate) taker_payment_lock: u64, + /// Allows to recognize one SWAP from the other in the logs. #274. + pub(crate) uuid: Uuid, + pub(crate) started_at: u64, + pub(crate) maker_payment_wait: u64, + pub(crate) maker_coin_start_block: u64, + pub(crate) taker_coin_start_block: u64, + /// A transaction fee that should be paid to send a `TakerFee`. + /// Note this value is used to calculate locked amount only. + pub(crate) fee_to_send_taker_fee: Option, + /// A `TakerPayment` transaction fee. + /// Note this value is used to calculate locked amount only. + pub(crate) taker_payment_trade_fee: Option, + /// A transaction fee that should be paid to spend a `MakerPayment`. + /// Note this value is used to calculate locked amount only. + pub(crate) maker_payment_spend_trade_fee: Option, + pub(crate) maker_coin_swap_contract_address: Option, + pub(crate) taker_coin_swap_contract_address: Option, + /// Temporary pubkey used in HTLC redeem script when applicable for maker coin + pub(crate) maker_coin_htlc_pubkey: Option, + /// Temporary pubkey used in HTLC redeem script when applicable for taker coin + pub(crate) taker_coin_htlc_pubkey: Option, + /// Temporary privkey used to sign P2P messages when applicable + pub(crate) p2p_privkey: Option, +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct MakerNegotiationData { + pub(crate) maker_payment_locktime: u64, + pub(crate) maker_pubkey: H264Json, + pub(crate) secret_hash: BytesJson, + pub(crate) maker_coin_swap_contract_addr: Option, + pub(crate) taker_coin_swap_contract_addr: Option, + pub(crate) maker_coin_htlc_pubkey: Option, + pub(crate) taker_coin_htlc_pubkey: Option, +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct TakerPaymentSpentData { + pub(crate) transaction: TransactionIdentifier, + pub(crate) secret: H256Json, +} + +#[derive(Deserialize)] +pub(crate) struct MySwapStatusResponse { + #[serde(flatten)] + pub(crate) swap: SavedSwap, + pub(crate) my_info: Option, + pub(crate) recoverable: bool, +} + +/// The helper structure that makes easier to parse the response for GUI devs +/// They won't have to parse the events themselves handling possible errors, index out of bounds etc. +#[derive(Deserialize)] +pub(crate) struct MySwapInfo { + pub(crate) my_coin: String, + pub(crate) other_coin: String, + pub(crate) my_amount: BigDecimal, + pub(crate) other_amount: BigDecimal, + pub(crate) started_at: u64, +} + +#[skip_serializing_none] +#[derive(Serialize)] +#[serde(tag = "method", rename = "my_recent_swaps")] +pub(crate) struct MyRecentSwapsRequest { + #[serde(flatten)] + pub(crate) filter: MySwapsFilter, + pub(crate) limit: usize, + pub(crate) from_uuid: Option, + pub(crate) page_number: Option, +} + +#[derive(Deserialize)] +pub(crate) struct MyRecentSwapResponse { + pub(crate) swaps: Vec, + pub(crate) from_uuid: Option, + pub(crate) skipped: usize, + pub(crate) limit: usize, + pub(crate) total: usize, + pub(crate) page_number: usize, + pub(crate) total_pages: usize, + pub(crate) found_records: usize, +} diff --git a/mm2src/komodefi_cli/src/rpc_data/trade_preimage.rs b/mm2src/komodefi_cli/src/rpc_data/trade_preimage.rs new file mode 100644 index 0000000000..f13a333ffd --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/trade_preimage.rs @@ -0,0 +1,88 @@ +use common::serde_derive::Deserialize; +use mm2_number::{construct_detailed, Fraction, MmNumber}; +use serde::Serialize; +use skip_serializing_none::skip_serializing_none; + +#[derive(Serialize)] +#[serde(tag = "method", rename = "min_trading_vol")] +pub(crate) struct MinTradingVolRequest { + pub(crate) coin: String, +} + +#[derive(Serialize)] +#[serde(tag = "method", rename = "max_taker_vol")] +pub(crate) struct MaxTakerVolRequest { + pub(crate) coin: String, +} + +#[derive(Deserialize)] +pub(crate) struct MaxTakerVolResponse { + pub(crate) coin: String, + pub(crate) result: Fraction, +} + +#[skip_serializing_none] +#[derive(Serialize)] +pub(crate) struct TradePreimageRequest { + pub(crate) base: String, + pub(crate) rel: String, + pub(crate) swap_method: TradePreimageMethod, + pub(crate) price: MmNumber, + pub(crate) volume: Option, + pub(crate) max: bool, +} + +#[derive(Serialize)] +#[serde(rename_all = "lowercase")] +pub(crate) enum TradePreimageMethod { + SetPrice, + Buy, + Sell, +} + +#[derive(Deserialize)] +#[serde(untagged)] +#[allow(clippy::large_enum_variant)] +pub(crate) enum TradePreimageResponse { + MakerPreimage(MakerPreimage), + TakerPreimage(TakerPreimage), +} + +#[derive(Deserialize)] +pub(crate) struct MakerPreimage { + pub(crate) base_coin_fee: TradeFeeResponse, + pub(crate) rel_coin_fee: TradeFeeResponse, + #[serde(flatten)] + pub(crate) volume: Option, + pub(crate) total_fees: Vec, +} + +#[derive(Deserialize)] +pub(crate) struct TakerPreimage { + pub(crate) base_coin_fee: TradeFeeResponse, + pub(crate) rel_coin_fee: TradeFeeResponse, + pub(crate) taker_fee: TradeFeeResponse, + pub(crate) fee_to_send_taker_fee: TradeFeeResponse, + pub(crate) total_fees: Vec, +} + +#[derive(Clone, Deserialize)] +pub(crate) struct TradeFeeResponse { + pub(crate) coin: String, + #[serde(flatten)] + pub(crate) amount: DetailedAmount, + pub(crate) paid_from_trading_vol: bool, +} + +#[derive(Clone, Deserialize)] +pub(crate) struct TotalTradeFeeResponse { + pub(crate) coin: String, + #[serde(flatten)] + pub(crate) amount: DetailedAmount, + #[serde(flatten)] + pub(crate) required_balance: DetailedRequiredBalance, +} + +construct_detailed!(DetailedAmount, amount); +construct_detailed!(DetailedVolume, volume); +construct_detailed!(DetailedRequiredBalance, required_balance); diff --git a/mm2src/komodefi_cli/src/rpc_data/utility.rs b/mm2src/komodefi_cli/src/rpc_data/utility.rs new file mode 100644 index 0000000000..a2a3ed564f --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/utility.rs @@ -0,0 +1,53 @@ +use derive_more::Display; +use rpc::v1::types::H256 as H256Json; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use uuid::Uuid; + +use mm2_rpc::data::legacy::UnbanPubkeysRequest as UnbanPubkeysRequestImpl; + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "list_banned_pubkeys")] +pub(crate) struct ListBannedPubkeysRequest {} + +pub(crate) type ListBannedPubkeysResponse = HashMap; + +#[derive(Deserialize)] +#[serde(tag = "type")] +#[allow(clippy::large_enum_variant)] +pub(crate) enum BanReason { + Manual { reason: String }, + FailedSwap { caused_by_swap: Uuid }, +} + +#[derive(Serialize)] +#[serde(tag = "method", rename = "unban_pubkeys")] +pub(crate) struct UnbanPubkeysRequest { + pub(crate) unban_by: UnbanPubkeysRequestImpl, +} + +#[derive(Deserialize)] +pub(crate) struct UnbanPubkeysResponse { + pub(crate) still_banned: HashMap, + pub(crate) unbanned: HashMap, + pub(crate) were_not_banned: Vec, +} + +#[derive(Serialize)] +pub(crate) struct GetCurrentMtpRequest { + pub(crate) coin: String, +} + +#[derive(Deserialize)] +pub(crate) struct GetCurrentMtpResponse { + pub(crate) mtp: u32, +} + +#[derive(Deserialize, Display)] +#[serde(tag = "error_type", content = "error_data")] +pub(crate) enum GetCurrentMtpError { + NoSuchCoin(String), + #[display(fmt = "Requested coin: {}; is not supported for this action.", _0)] + NotSupportedCoin(String), + RpcError(String), +} diff --git a/mm2src/komodefi_cli/src/rpc_data/version_stat.rs b/mm2src/komodefi_cli/src/rpc_data/version_stat.rs new file mode 100644 index 0000000000..416e59dab2 --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/version_stat.rs @@ -0,0 +1,45 @@ +use derive_more::Display; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize)] +pub(crate) struct VersionStatAddNodeRequest { + pub(crate) name: String, + pub(crate) address: String, + pub(crate) peer_id: String, +} + +#[derive(Debug, Deserialize, Display)] +#[serde(tag = "error_type", content = "error_data")] +pub(crate) enum NodeVersionError { + #[display(fmt = "Invalid request: {}", _0)] + InvalidRequest(String), + #[display(fmt = "Database error: {}", _0)] + DatabaseError(String), + #[display(fmt = "Invalid address: {}", _0)] + InvalidAddress(String), + #[display(fmt = "Error on parse peer id {}: {}", _0, _1)] + PeerIdParseError(String, String), + #[display(fmt = "{} is only supported in native mode", _0)] + UnsupportedMode(String), + #[display(fmt = "start_version_stat_collection is already running")] + AlreadyRunning, + #[display(fmt = "Version stat collection is currently stopping")] + CurrentlyStopping, + #[display(fmt = "start_version_stat_collection is not running")] + NotRunning, +} + +#[derive(Serialize)] +pub(crate) struct VersionStatRemoveNodeRequest { + pub(crate) name: String, +} + +#[derive(Serialize)] +pub(crate) struct VStatStartCollectionRequest { + pub(crate) interval: f64, +} + +#[derive(Serialize)] +pub(crate) struct VStatUpdateCollectionRequest { + pub(crate) interval: f64, +} diff --git a/mm2src/komodefi_cli/src/rpc_data/wallet.rs b/mm2src/komodefi_cli/src/rpc_data/wallet.rs new file mode 100644 index 0000000000..3ec9f7c59e --- /dev/null +++ b/mm2src/komodefi_cli/src/rpc_data/wallet.rs @@ -0,0 +1,358 @@ +use derive_more::Display; +use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; +use serde::{Deserialize, Serialize}; +use serde_json::Value as Json; +use skip_serializing_none::skip_serializing_none; +use std::collections::HashSet; + +use crate::rpc_data::zcoin::{AnyValue, Bip32Child, Bip32PurposeValue, Bip44Tail, HardenedValue}; +use common::PagingOptionsEnum; +use mm2_number::BigDecimal; + +#[derive(Debug, Serialize)] +#[serde(tag = "method", rename = "send_raw_transaction")] +pub(crate) struct SendRawTransactionRequest { + pub(crate) coin: String, + pub(crate) tx_hex: BytesJson, +} + +#[derive(Deserialize)] +pub(crate) struct SendRawTransactionResponse { + pub(crate) tx_hash: BytesJson, +} + +#[skip_serializing_none] +#[derive(Debug, Serialize)] +#[serde(tag = "method", rename = "withdraw")] +pub(crate) struct WithdrawRequest { + pub(crate) coin: String, + pub(crate) from: Option, + pub(crate) to: String, + #[serde(default)] + pub(crate) amount: BigDecimal, + #[serde(default)] + pub(crate) max: bool, + pub(crate) fee: Option, +} + +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub(crate) enum WithdrawFrom { + DerivationPath { derivation_path: String }, + HDWalletAddress(StandardHDCoinAddress), +} + +#[derive(Clone, Debug, Default, Serialize)] +pub struct StandardHDCoinAddress { + pub account: u32, + pub is_change: bool, + pub address_index: u32, +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct HDAccountAddressId { + pub(crate) account_id: u32, + pub(crate) chain: Bip44Chain, + pub(crate) address_id: u32, +} + +#[derive(Debug, Display, Deserialize, Serialize)] +#[repr(u32)] +pub(crate) enum Bip44Chain { + External = 0, + Internal = 1, +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type")] +pub(crate) enum WithdrawFee { + UtxoFixed { amount: BigDecimal }, + UtxoPerKbyte { amount: BigDecimal }, + EthGas { gas_price: BigDecimal, gas: u64 }, + Qrc20Gas { gas_limit: u64, gas_price: u64 }, + CosmosGas { gas_limit: u64, gas_price: f64 }, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct WithdrawResponse { + pub(crate) tx_hex: BytesJson, + pub(crate) tx_hash: String, + pub(crate) from: Vec, + pub(crate) to: Vec, + pub(crate) total_amount: BigDecimal, + pub(crate) spent_by_me: BigDecimal, + pub(crate) received_by_me: BigDecimal, + pub(crate) my_balance_change: BigDecimal, + pub(crate) block_height: u64, + pub(crate) timestamp: u64, + pub(crate) fee_details: Option, + pub(crate) coin: String, + pub(crate) internal_id: BytesJson, + pub(crate) kmd_rewards: Option, + pub(crate) transaction_type: Option, + pub(crate) memo: Option, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct KmdRewardsDetails { + pub(crate) amount: BigDecimal, + pub(crate) claimed_by_me: bool, +} + +#[skip_serializing_none] +#[derive(Debug, Serialize)] +#[serde(tag = "method", rename = "my_tx_history")] +pub(crate) struct MyTxHistoryRequest { + pub(crate) coin: String, + pub(crate) from_id: Option, + pub(crate) max: bool, + pub(crate) limit: usize, + pub(crate) page_number: Option, +} + +#[derive(Deserialize)] +pub(crate) struct MyTxHistoryResponse { + pub(crate) transactions: Vec, + pub(crate) limit: usize, + pub(crate) skipped: usize, + pub(crate) from_id: Option, + pub(crate) total: usize, + pub(crate) current_block: u64, + pub(crate) sync_status: HistorySyncState, + pub(crate) page_number: Option, + pub(crate) total_pages: Option, +} + +#[derive(Display, Deserialize)] +#[serde(tag = "state", content = "additional_info")] +pub(crate) enum HistorySyncState { + NotEnabled, + NotStarted, + InProgress(Json), + Error(Json), + Finished, +} + +#[derive(Deserialize)] +pub(crate) struct MyTxHistoryDetails { + #[serde(flatten)] + pub(crate) details: TransactionDetails, + pub(crate) confirmations: u64, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct TransactionDetails { + pub(crate) tx_hex: BytesJson, + pub(crate) tx_hash: String, + pub(crate) from: Vec, + pub(crate) to: Vec, + pub(crate) total_amount: BigDecimal, + pub(crate) spent_by_me: BigDecimal, + pub(crate) received_by_me: BigDecimal, + pub(crate) my_balance_change: BigDecimal, + pub(crate) block_height: u64, + pub(crate) timestamp: u64, + pub(crate) fee_details: Option, + pub(crate) coin: String, + pub(crate) internal_id: BytesJson, + pub(crate) kmd_rewards: Option, + pub(crate) transaction_type: TransactionType, + pub(crate) memo: Option, +} + +#[derive(Debug, Display, Deserialize)] +pub(crate) enum TransactionType { + StakingDelegation, + RemoveDelegation, + StandardTransfer, + #[display(fmt = "TokenTransfer: {}", "hex::encode(&_0.0)")] + TokenTransfer(BytesJson), + FeeForTokenTx, + #[display(fmt = "msg_type: {}, token_id: {}", "_0.msg_type", "format_bytes_json(&_0.token_id)")] + CustomTendermintMsg(CustomTendermintMsg), + NftTransfer, +} + +fn format_bytes_json(bytes: &Option) -> String { + bytes + .as_ref() + .map(|v| hex::encode(&v.0)) + .unwrap_or_else(|| "none".to_string()) +} + +#[derive(Debug, Deserialize)] +pub(crate) struct CustomTendermintMsg { + msg_type: CustomTendermintMsgType, + token_id: Option, +} + +#[derive(Debug, Display, Deserialize)] +pub(crate) enum CustomTendermintMsgType { + SendHtlcAmount, + ClaimHtlcAmount, + SignClaimHtlc, +} + +#[derive(Debug, Serialize)] +pub(crate) struct MyTxHistoryRequestV2 { + pub(crate) coin: String, + pub(crate) limit: usize, + pub(crate) paging_options: PagingOptionsEnum, +} + +#[derive(Deserialize)] +pub struct MyTxHistoryResponseV2 { + pub(crate) coin: String, + pub(crate) target: MyTxHistoryTarget, + pub(crate) current_block: u64, + pub(crate) transactions: Vec, + pub(crate) sync_status: HistorySyncState, + pub(crate) limit: usize, + pub(crate) skipped: usize, + pub(crate) total: usize, + pub(crate) total_pages: usize, + pub(crate) paging_options: PagingOptionsEnum, +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "type")] +#[serde(rename_all = "snake_case")] +pub(crate) enum MyTxHistoryTarget { + Iguana, + #[allow(dead_code)] + AccountId { + account_id: u32, + }, + AddressId(HDAccountAddressId), + AddressDerivationPath(StandardHDPath), +} + +#[rustfmt::skip] +pub(crate) type StandardHDPath = +Bip32Child>>>>; + +#[derive(Debug, Deserialize)] +pub(crate) struct Bip44ChainValue { + #[allow(dead_code)] + chain: Bip44Chain, +} + +pub(crate) type NonHardenedValue = AnyValue; + +#[derive(Debug, Serialize)] +#[serde(tag = "method", rename = "show_priv_key")] +pub(crate) struct ShowPrivateKeyRequest { + pub(crate) coin: String, +} + +#[derive(Deserialize)] +pub(crate) struct ShowPrivateKeyResponse { + pub(crate) coin: String, + pub(crate) priv_key: String, +} + +#[derive(Serialize)] +#[serde(tag = "method", rename = "validateaddress")] +pub(crate) struct ValidateAddressRequest { + pub(crate) coin: String, + pub(crate) address: String, +} + +#[derive(Deserialize)] +pub(crate) struct ValidateAddressResponse { + pub(crate) is_valid: bool, + pub(crate) reason: Option, +} + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "kmd_rewards_info")] +pub(crate) struct KmdRewardsInfoRequest {} + +pub(crate) type KmdRewardsInfoResponse = Vec; + +#[derive(Deserialize)] +pub(crate) struct KmdRewardsInfoElement { + pub(crate) tx_hash: H256Json, + pub(crate) height: Option, + pub(crate) output_index: u32, + pub(crate) amount: BigDecimal, + pub(crate) locktime: u64, + pub(crate) accrued_rewards: KmdRewardsAccrueInfo, + pub(crate) accrue_start_at: Option, + pub(crate) accrue_stop_at: Option, +} + +#[derive(Display, Deserialize)] +pub(crate) enum KmdRewardsAccrueInfo { + Accrued(BigDecimal), + NotAccruedReason(KmdRewardsNotAccruedReason), +} + +#[derive(Display, Deserialize)] +pub(crate) enum KmdRewardsNotAccruedReason { + LocktimeNotSet, + LocktimeLessThanThreshold, + UtxoHeightGreaterThanEndOfEra, + UtxoAmountLessThanTen, + OneHourNotPassedYet, + TransactionInMempool, +} + +#[derive(Deserialize)] +pub(crate) struct ZcoinTxDetails { + pub(crate) tx_hash: String, + pub(crate) from: HashSet, + pub(crate) to: HashSet, + pub(crate) spent_by_me: BigDecimal, + pub(crate) received_by_me: BigDecimal, + pub(crate) my_balance_change: BigDecimal, + pub(crate) block_height: i64, + pub(crate) confirmations: i64, + pub(crate) timestamp: i64, + pub(crate) transaction_fee: BigDecimal, + pub(crate) coin: String, + pub(crate) internal_id: i64, +} + +#[derive(Serialize)] +#[serde(tag = "method", rename = "convertaddress")] +pub(crate) struct ConvertAddressRequest { + pub(crate) coin: String, + pub(crate) from: String, + pub(crate) to_address_format: ConvertAddressFormat, +} + +#[derive(Serialize)] +#[serde(tag = "format", content = "network", rename_all = "lowercase")] +pub(crate) enum ConvertAddressFormat { + MixedCase, + CashAddress(CashAddressNetwork), + Standard, +} + +#[derive(Serialize)] +#[serde(rename_all = "lowercase")] +pub(crate) enum CashAddressNetwork { + BitcoinCash, + BchTest, + BchReg, +} + +#[derive(Deserialize)] +pub(crate) struct ConvertAddressResponse { + pub(crate) address: String, +} + +#[derive(Serialize)] +#[serde(tag = "method", rename = "convert_utxo_address")] +pub(crate) struct ConvertUtxoAddressRequest { + pub(crate) address: String, + pub(crate) to_coin: String, +} + +pub(crate) type ConvertUtxoAddressResponse = String; diff --git a/mm2src/komodefi_cli/src/scenarios/download_helper.rs b/mm2src/komodefi_cli/src/scenarios/download_helper.rs new file mode 100644 index 0000000000..616df20514 --- /dev/null +++ b/mm2src/komodefi_cli/src/scenarios/download_helper.rs @@ -0,0 +1,112 @@ +use crate::cli::get_cli_root; + +use anyhow::Result; +use common::log::{debug, error, info}; +use serde::Deserialize; +use std::fs; +use std::fs::File; +use std::io::Write; +use std::process::{Command, Stdio}; +use zip::ZipArchive; + +const BINARY_HOST_URL: &str = "https://api.github.com/repos/KomodoPlatform/komodo-defi-framework/releases"; + +#[cfg(all(unix, not(target_os = "macos")))] +const DOWNLOAD_NAME: &str = "Linux-Release."; +#[cfg(target_os = "macos")] +const DOWNLOAD_NAME: &str = "Darwin-Release."; +#[cfg(target_os = "windows")] +const DOWNLOAD_NAME: &str = "Win64."; + +#[cfg(not(target_os = "windows"))] +const BINARY_NAME: &str = "mm2"; +#[cfg(target_os = "windows")] +const BINARY_NAME: &str = "mm2.exe"; + +#[derive(Debug, Deserialize)] +struct Asset { + name: String, + browser_download_url: String, +} + +#[derive(Debug, Deserialize)] +struct Releases { + assets: Vec, +} + +/// Let komodefi_cli download and extract release binary for use. +pub(crate) async fn download_binary_and_extract_to_bin_folder() -> Result<()> { + // Create a reusable reqwest client with common headers + let client = reqwest::Client::builder().user_agent("from-tty").build()?; + let response = client.get(BINARY_HOST_URL).send().await?; + let releases: Vec = response.json().await?; + + // Extract the download URL for the latest release + if let Some(download_url) = releases.get(0).and_then(|release| { + release + .assets + .iter() + .find(|asset| asset.name.contains(DOWNLOAD_NAME)) + .map(|asset| &asset.browser_download_url) + }) { + info!("release: {download_url}"); + // Download the ZIP file + let zip_data = client.get(download_url).send().await?.bytes().await?; + // Create directories if they don't exist + let cli_root = get_cli_root()?; + let mut zip_path = cli_root.clone(); + zip_path.push("mm2.zip"); + // Save the ZIP file + let mut zip_file = File::create(&zip_path)?; + zip_file.write_all(&zip_data)?; + // Extract only mm2 binary file from the folder + extract_file_from_zip(&zip_path, BINARY_NAME).await?; + info!( + "Binary downloaded and extracted to {} folder!", + cli_root.to_string_lossy() + ); + Ok(()) + } else { + error!("No matching release found"); + Err(anyhow::anyhow!("No matching release found")) + } +} + +/// Extract binary file from zip file +async fn extract_file_from_zip(zip_path: &std::path::Path, file_name: &str) -> Result<(), anyhow::Error> { + let file = File::open(zip_path)?; + let reader = std::io::BufReader::new(file); + let mut archive = ZipArchive::new(reader)?; + + // Create directories if they don't exist and extract binary + let mut config_path = get_cli_root()?; + archive.extract(&config_path)?; + + // Check binary version + let version = get_binary_version(&format!("{}/{file_name}", config_path.to_string_lossy())).await?; + info!("running {version}"); + + // Delete zip + config_path.push("mm2.zip"); + fs::remove_file(config_path)?; + debug!("deleted downloaded_file.zip after use"); + + Ok(()) +} + +async fn get_binary_version(binary_path: &str) -> Result { + info!("{binary_path:?}"); + let output = Command::new(binary_path) + .arg("--version") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output()?; + + if output.status.success() { + let version = String::from_utf8_lossy(&output.stdout).trim().to_string(); + Ok(version) + } else { + let error_message = String::from_utf8_lossy(&output.stderr).trim().to_string(); + Err(anyhow::anyhow!("Failed to get binary version: {}", error_message)) + } +} diff --git a/mm2src/adex_cli/src/scenarios/init_coins.rs b/mm2src/komodefi_cli/src/scenarios/init_coins.rs similarity index 100% rename from mm2src/adex_cli/src/scenarios/init_coins.rs rename to mm2src/komodefi_cli/src/scenarios/init_coins.rs diff --git a/mm2src/adex_cli/src/scenarios/init_mm2_cfg.rs b/mm2src/komodefi_cli/src/scenarios/init_mm2_cfg.rs similarity index 86% rename from mm2src/adex_cli/src/scenarios/init_mm2_cfg.rs rename to mm2src/komodefi_cli/src/scenarios/init_mm2_cfg.rs index d58b035f73..ba84f1276a 100644 --- a/mm2src/adex_cli/src/scenarios/init_mm2_cfg.rs +++ b/mm2src/komodefi_cli/src/scenarios/init_mm2_cfg.rs @@ -2,9 +2,11 @@ use anyhow::{anyhow, Result}; use bip39::{Language, Mnemonic, MnemonicType}; use common::log::{error, info}; use common::password_policy; +use crypto::KeyPairPolicyBuilder; use inquire::{validator::Validation, Confirm, CustomType, CustomUserError, Text}; use passwords::PasswordGenerator; use serde::Serialize; +use std::env::current_dir; use std::net::Ipv4Addr; use std::ops::Not; use std::path::Path; @@ -15,15 +17,19 @@ use crate::helpers; use crate::logging::error_anyhow; const DEFAULT_NET_ID: u16 = 8762; -const DEFAULT_GID: &str = "adex-cli"; +const DEFAULT_GID: &str = "komodefi-cli"; const DEFAULT_OPTION_PLACEHOLDER: &str = "Tap enter to skip"; const RPC_PORT_MIN: u16 = 1024; const RPC_PORT_MAX: u16 = 49151; +const DEFAULT_RPC_PORT: u16 = 7783; +const DEFAULT_RPC_IP: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); pub(crate) fn init_mm2_cfg(cfg_file: &str) -> Result<()> { let mut mm2_cfg = Mm2Cfg::new(); info!("Start collecting mm2_cfg into: {cfg_file}"); mm2_cfg.inquire()?; + // write default config(dbdir, ip, port) + mm2_cfg.write_default_args()?; helpers::rewrite_json_file(&mm2_cfg, cfg_file)?; info!("mm2_cfg has been writen into: {cfg_file}"); @@ -52,6 +58,7 @@ struct Mm2Cfg { seednodes: Vec, #[serde(skip_serializing_if = "Option::is_none")] enable_hd: Option, + secure_conn: Option, } impl Mm2Cfg { @@ -69,6 +76,7 @@ impl Mm2Cfg { i_am_seed: None, seednodes: Vec::::new(), enable_hd: None, + secure_conn: None, } } @@ -79,6 +87,7 @@ impl Mm2Cfg { self.inquire_allow_weak_password()?; self.inquire_rpc_password()?; self.inquire_dbdir()?; + self.inquire_secure_connection()?; self.inquire_rpcip()?; self.inquire_rpcport()?; self.inquire_rpc_local_only()?; @@ -129,7 +138,6 @@ impl Mm2Cfg { self.netid = CustomType::::new("What is the network `mm2` is going to be a part, netid:") .with_default(DEFAULT_NET_ID) .with_help_message(r#"Network ID number, telling the AtomicDEX API which network to join. 8762 is the current main network, though alternative netids can be used for testing or "private" trades"#) - .with_placeholder(format!("{DEFAULT_NET_ID}").as_str()) .prompt() .map_err(|error| error_anyhow!("Failed to get netid: {error}") @@ -255,6 +263,7 @@ impl Mm2Cfg { .with_help_message(r#"Port to use for RPC communication. Optional, defaults to 7783"#) .with_validator(validator) .with_placeholder(DEFAULT_OPTION_PLACEHOLDER) + .with_default(InquireOption::Some(DEFAULT_RPC_PORT)) .prompt() .map_err(|error| error_anyhow!("Failed to get rpcport: {error}"))? .into(); @@ -325,4 +334,57 @@ impl Mm2Cfg { .into(); Ok(()) } + + fn inquire_secure_connection(&mut self) -> Result<()> { + if self.secure_conn.is_none() { + self.secure_conn = Confirm::new("Use secure connection for rpc:") + .with_default(false) + .with_placeholder("No") + .prompt() + .map_err(|error| error_anyhow!("Failed to get secure_conn option: {error}"))? + .into(); + } + + Ok(()) + } + + fn write_default_args(&mut self) -> Result<()> { + if self.secure_conn.is_none() { + self.secure_conn = Some(false) + } + + if self.rpcip.is_none() { + self.rpcip = Some(DEFAULT_RPC_IP) + } + + if self.rpcport.is_none() { + self.rpcport = Some(DEFAULT_RPC_PORT) + } + + if self.dbdir.is_none() { + let seed = self + .seed_phrase + .as_ref() + .ok_or_else(|| error_anyhow!("No seed phrase detected in config"))?; + let policy_builder = if let Some(true) = self.enable_hd { + KeyPairPolicyBuilder::GlobalHDAccount + } else { + KeyPairPolicyBuilder::Iguana + }; + + let (key, _) = policy_builder + .build(seed) + .map_err(|error| error_anyhow!("Failed pass default rpcip: {error}"))?; + let rmd160 = key.public().address_hash(); + + let current_dir = + current_dir().map_err(|error| error_anyhow!("Failed to load your current dir: {error}"))?; + let current_dir = current_dir.parent().unwrap().to_string_lossy().to_string(); + + let db_path = format!("{current_dir}/DB/{rmd160}"); + + self.dbdir = Some(db_path) + } + Ok(()) + } } diff --git a/mm2src/adex_cli/src/scenarios/inquire_extentions.rs b/mm2src/komodefi_cli/src/scenarios/inquire_extentions.rs similarity index 100% rename from mm2src/adex_cli/src/scenarios/inquire_extentions.rs rename to mm2src/komodefi_cli/src/scenarios/inquire_extentions.rs diff --git a/mm2src/adex_cli/src/scenarios/mm2_proc_mng.rs b/mm2src/komodefi_cli/src/scenarios/mm2_proc_mng.rs similarity index 92% rename from mm2src/adex_cli/src/scenarios/mm2_proc_mng.rs rename to mm2src/komodefi_cli/src/scenarios/mm2_proc_mng.rs index 36a864b5f8..73038e144e 100644 --- a/mm2src/adex_cli/src/scenarios/mm2_proc_mng.rs +++ b/mm2src/komodefi_cli/src/scenarios/mm2_proc_mng.rs @@ -1,10 +1,11 @@ use anyhow::{anyhow, Result}; use common::log::{error, info}; -use std::env; use std::path::PathBuf; +use crate::cli::get_cli_root; use crate::error_anyhow; +#[cfg(not(target_os = "macos"))] use std::env; #[cfg(not(target_os = "macos"))] use sysinfo::{PidExt, ProcessExt, System, SystemExt}; @@ -77,10 +78,10 @@ fn find_proc_by_name(pname: &'_ str) -> Vec { } fn get_mm2_binary_path() -> Result { - let mut dir = env::current_exe().map_err(|error| error_anyhow!("Failed to get current binary dir: {error}"))?; - dir.pop(); - dir.push(MM2_BINARY); - Ok(dir) + let mut mm2_base_path = + get_cli_root().map_err(|error| error_anyhow!("Failed to get current binary dir: {error}"))?; + mm2_base_path.push(MM2_BINARY); + Ok(mm2_base_path) } #[cfg(not(target_os = "macos"))] @@ -109,7 +110,10 @@ pub(crate) fn start_process(mm2_cfg_file: &Option, coins_file: &Option process, Err(error) => { @@ -201,9 +205,8 @@ pub(crate) fn stop_process() { #[cfg(target_os = "macos")] pub(crate) fn start_process(mm2_cfg_file: &Option, coins_file: &Option, log_file: &Option) { let Ok(mm2_binary) = get_mm2_binary_path() else { return; }; - - let Ok(current_dir) = env::current_dir() else { - error!("Failed to get current_dir"); + let Ok(cli_root_dir) = get_cli_root() else { + error!("Failed to get cli_root_dir"); return }; @@ -236,7 +239,7 @@ pub(crate) fn start_process(mm2_cfg_file: &Option, coins_file: &Option"#, LAUNCHCTL_MM2_ID, mm2_binary.display(), - current_dir.display(), + cli_root_dir.display(), log_file .as_deref() .map(|log_file| format!("MM_LOG{log_file}")) @@ -285,7 +288,7 @@ pub(crate) fn start_process(mm2_cfg_file: &Option, coins_file: &Option Result { - match env::current_dir() { + match get_cli_root() { Err(error) => Err(error_anyhow!( "Failed to get current_dir to construct plist_path: {error}" )), @@ -332,10 +335,13 @@ fn get_proc_uid() -> Result { #[cfg(target_os = "macos")] pub(crate) fn get_status() { - let output = Command::new("launchctl") - .args(["list", LAUNCHCTL_MM2_ID]) - .output() - .unwrap(); + let output = match Command::new("launchctl").args(["list", LAUNCHCTL_MM2_ID]).output() { + Err(error) => { + error!("Failed to `launchctl list {LAUNCHCTL_MM2_ID}`, error: {error}"); + return; + }, + Ok(output) => output, + }; if !output.status.success() { info!("Service '{LAUNCHCTL_MM2_ID}' is not running"); diff --git a/mm2src/adex_cli/src/scenarios/mod.rs b/mm2src/komodefi_cli/src/scenarios/mod.rs similarity index 64% rename from mm2src/adex_cli/src/scenarios/mod.rs rename to mm2src/komodefi_cli/src/scenarios/mod.rs index 070748c83c..8e169b5332 100644 --- a/mm2src/adex_cli/src/scenarios/mod.rs +++ b/mm2src/komodefi_cli/src/scenarios/mod.rs @@ -1,22 +1,27 @@ +mod download_helper; mod init_coins; mod init_mm2_cfg; mod inquire_extentions; mod mm2_proc_mng; use anyhow::Result; + +use common::log::info; use init_coins::init_coins; use init_mm2_cfg::init_mm2_cfg; -use log::info; use super::activation_scheme_db::init_activation_scheme; +use crate::cli::get_cli_root; +pub(super) use download_helper::download_binary_and_extract_to_bin_folder; pub(super) use mm2_proc_mng::{get_status, start_process, stop_process}; pub(super) async fn init(cfg_file: &str, coins_file: &str) { let _ = init_impl(cfg_file, coins_file).await; } async fn init_impl(cfg_file: &str, coins_file: &str) -> Result<()> { - init_mm2_cfg(cfg_file)?; - init_coins(coins_file).await?; + let root = get_cli_root()?; + init_mm2_cfg(&root.join(cfg_file).to_string_lossy())?; + init_coins(&root.join(coins_file).to_string_lossy()).await?; init_activation_scheme().await?; info!("Initialization done"); Ok(()) diff --git a/mm2src/komodefi_cli/src/tests/http_mock_data/active_swaps.http b/mm2src/komodefi_cli/src/tests/http_mock_data/active_swaps.http new file mode 100644 index 0000000000..694e871163 --- /dev/null +++ b/mm2src/komodefi_cli/src/tests/http_mock_data/active_swaps.http @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +access-control-allow-origin: http://localhost:3000 +content-length: 8528 +date: Tue, 25 Jul 2023 12:21:22 GMT + +{"uuids":["6b007706-d6e1-4565-8655-9eeb128d00e2"],"statuses":{"6b007706-d6e1-4565-8655-9eeb128d00e2":{"type":"Taker","uuid":"6b007706-d6e1-4565-8655-9eeb128d00e2","my_order_uuid":"6b007706-d6e1-4565-8655-9eeb128d00e2","events":[{"timestamp":1690287607463,"event":{"type":"Started","data":{"taker_coin":"DOC","maker_coin":"MARTY","maker":"2d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846","my_persistent_pub":"02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","lock_duration":7800,"maker_amount":"1","taker_amount":"1","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"taker_payment_lock":1690295407,"uuid":"6b007706-d6e1-4565-8655-9eeb128d00e2","started_at":1690287607,"maker_payment_wait":1690290727,"maker_coin_start_block":147860,"taker_coin_start_block":133421,"fee_to_send_taker_fee":{"coin":"DOC","amount":"0.00001","paid_from_trading_vol":false},"taker_payment_trade_fee":{"coin":"DOC","amount":"0.00001","paid_from_trading_vol":false},"maker_payment_spend_trade_fee":{"coin":"MARTY","amount":"0.00001","paid_from_trading_vol":true},"maker_coin_htlc_pubkey":"02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","taker_coin_htlc_pubkey":"02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","p2p_privkey":null}}},{"timestamp":1690287623468,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1690303206,"maker_pubkey":"000000000000000000000000000000000000000000000000000000000000000000","secret_hash":"a5cfc9787066562ba03c7538d024a88fd1a0fe12","maker_coin_swap_contract_addr":null,"taker_coin_swap_contract_addr":null,"maker_coin_htlc_pubkey":"022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846","taker_coin_htlc_pubkey":"022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846"}}},{"timestamp":1690287623689,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f890108742b73cfadf56dbc93d3fb8b33b54e5301869e2c950b200e67354b56e2d2ef010000006a4730440220736050089328c8ec984036b4a248ba3a130d58e9601da3358ffef3482d40927002204a8aa7b83560ee22792457465432bf4f098b38f41d0a483f68920eb63b487d02012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff02bcf60100000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ace0adaf08635600001976a9149934ebeaa56cb597c936a9ed8202d8d97a0a700388ac07bebf64000000000000000000000000000000","tx_hash":"c71f3793e976209674e2b00efb236c0fa8f0b1b552cb6cfe9068c6b731e570fd"}}},{"timestamp":1690287624691,"event":{"type":"TakerPaymentInstructionsReceived","data":null}},{"timestamp":1690287624693,"event":{"type":"MakerPaymentReceived","data":{"tx_hex":"0400008085202f890754448b50295dedd36f8de60aeaeeb56a5efa9d1a4464185e329c3aae9fd17673020000006a4730440220651b3753986a47026f36b082d870c3b2f7651684c0ed26637b64bfbc8722059302200ee7478e290327827daff8a2daf836e9446362169a4a8a4958f538c07f2093180121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffffe69a594c67460f781debbca0cfc731c29dddba613d65cf630acb00db6c93c9c0000000006b483045022100ef75d49925b7465bec5bc367f87fc7726d33aa17f472cd1ab6c13181d68613940220447c529336a478f4b9d89cc1453ca1cc22f34c13c3b69f7440fcc7fe889493880121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffff990db7e9fa1f052aba359969b64b892cb76ff881ccd38cb75c09129e9065dbb3000000006a473044022000e7b9f13c99aa71ce1b8559c2a63cec9b808767a744196e9ed0bde0b5e481a40220053e683e1efc9191207f8feb5e42646301cd2b1f2a8f9e7616e29914eb9937c10121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffff41b284103365315e6fb79a7aa111295a039ccc96aa90d888915f9f0eabec549b000000006b483045022100b2b3cdc669c916e88615b5d2dc3c99186fc1369879bf08b681097986b6842b63022014a5c0b86d9732d4f202eb5a6d8590a37b6de013e17746c5a7be76b7c932f7390121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffffdd878ff57eb64187cb74390a4b959c4012d11b766c698f706634e3360e48a6f0000000006b483045022100a166834300118a432b2b4374e8d076be488b0f84109381f0862c050e73873885022050b3ca3475f1e266ab0b84f30f9030b2d4186a9939c305071691502cde007d8c0121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffff509a8dc208434566c92ccfcc493df990da5edd7454c9527dd7df6d7c8aff49a8020000006b483045022100a02adf39ac6ad8e16603e0fc56948f7973223287f0fc0c9665ccd556b135193e022065ef4363429e83ae3703f6536a9f5294b178e7f1641bd24af7bbf3d72c0ada700121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffffc3fbc1f34fd64e0dcb1db65a18789f2fbb170cc41a874f8788d904a24b3c2c5d020000006a47304402201e819095555707955dc508afc6db4acc7662c62460efa79a982f921bfd4afcb90220694cb9276b5228d544571cfdca849f6c18a6abf7169b983f9d47e88da43cd4b90121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffff0300e1f5050000000017a914cea7345fe6ada43ef95fd6cdfd8c339ef7d1c864870000000000000000166a14a5cfc9787066562ba03c7538d024a88fd1a0fe12d08c15d460ba11001976a914046922483fab8ca76b23e55e9d338605e2dbab6088ac07bebf64000000000000000000000000000000","tx_hash":"3284af63a9fa0c4080fd5367c3c7c1ab1d00bb12ae47eb8fb0f5e2bd2da4736a"}}},{"timestamp":1690287624696,"event":{"type":"MakerPaymentWaitConfirmStarted"}},{"timestamp":1690287640041,"event":{"type":"MakerPaymentValidatedAndConfirmed"}},{"timestamp":1690287640365,"event":{"type":"TakerPaymentSent","data":{"tx_hex":"0400008085202f8901fd70e531b7c66890fe6ccb52b5b1f0a80f6c23fb0eb0e274962076e993371fc7010000006a4730440220259ab8ec216b802f32092ef307017f183f4bd8a52bec420363abf7f070d444a8022061fce8a8b562e07b8ab41afd8953521ad7d22ffb0aa5c710f044554d89833bb6012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff0300e1f5050000000017a914b491ff619f632ac1b7ef4e11f64404cff0e98adf870000000000000000166a14a5cfc9787066562ba03c7538d024a88fd1a0fe12f8c8b902635600001976a9149934ebeaa56cb597c936a9ed8202d8d97a0a700388ac18bebf64000000000000000000000000000000","tx_hash":"75cbad92b60fdb6be1fc7e73b6bac9b4b531c4f14d03b5201f8ff26f20ca1e5d"}}},{"timestamp":1690287681124,"event":{"type":"TakerPaymentSpent","data":{"transaction":{"tx_hex":"0400008085202f89015d1eca206ff28f1f20b5034df1c431b5b4c9bab6737efce16bdb0fb692adcb7500000000d8483045022100a0ec1d13d15a4f02a18a9adaa3442d8a9b956034c3e45b68bcbada8f877aef3b02206d59dcea375e86d5a014d51728c74a172c22a5b3cdc5dbe8daa70bb4b887a5a30120bed41dce1b0681670b3cad9d31c862bb166fcab656e23d4c00eef7dcac38cad4004c6b63046fdcbf64b1752102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dac6782012088a914a5cfc9787066562ba03c7538d024a88fd1a0fe128821022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ac68ffffffff0118ddf505000000001976a914046922483fab8ca76b23e55e9d338605e2dbab6088ac6fdcbf64000000000000000000000000000000","tx_hash":"13de819b027b4ae98e730679b2b716f98bd1154f729303efd89615f152865586"},"secret":"bed41dce1b0681670b3cad9d31c862bb166fcab656e23d4c00eef7dcac38cad4"}}},{"timestamp":1690287681573,"event":{"type":"MakerPaymentSpent","data":{"tx_hex":"0400008085202f89016a73a42dbde2f5b08feb47ae12bb001dabc1c7c36753fd80400cfaa963af843200000000d74730440220641be55ef769d759be59afe213d57eeeedf7d0f57bcf90835c8c3b7642d0e78902202a8f07ce745553107bea98a58cd50edb46782267fbeb4960c28073ad0412cc380120bed41dce1b0681670b3cad9d31c862bb166fcab656e23d4c00eef7dcac38cad4004c6b6304e6fabf64b17521022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ac6782012088a914a5cfc9787066562ba03c7538d024a88fd1a0fe12882102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dac68ffffffff0118ddf505000000001976a9149934ebeaa56cb597c936a9ed8202d8d97a0a700388ace6fabf64000000000000000000000000000000","tx_hash":"4f2cc7a83d7012c5d03fa64df188500db4bee51bbb9a6a0a1f06a50ca3409fdc"}}}],"maker_amount":"1","maker_coin":"MARTY","maker_coin_usd_price":null,"taker_amount":"1","taker_coin":"DOC","taker_coin_usd_price":null,"gui":"adex-cli","mm_version":"1.0.6-beta_dabdaf33b","success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundFailed","TakerPaymentRefundFinished"]}}} \ No newline at end of file diff --git a/mm2src/adex_cli/src/tests/http_mock_data/balance.http b/mm2src/komodefi_cli/src/tests/http_mock_data/balance.http similarity index 100% rename from mm2src/adex_cli/src/tests/http_mock_data/balance.http rename to mm2src/komodefi_cli/src/tests/http_mock_data/balance.http diff --git a/mm2src/komodefi_cli/src/tests/http_mock_data/best_orders.http b/mm2src/komodefi_cli/src/tests/http_mock_data/best_orders.http new file mode 100644 index 0000000000..b530c201e2 --- /dev/null +++ b/mm2src/komodefi_cli/src/tests/http_mock_data/best_orders.http @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +access-control-allow-origin: http://localhost:3000 +content-length: 4504 +date: Mon, 29 May 2023 15:22:10 GMT + +{"mmrpc":"2.0","result":{"orders":{"MORTY":[{"uuid":"e52246a2-f9b2-4145-9aa6-53b96bfabe9f","coin":"MORTY","address":{"address_type":"Transparent","address_data":"RMaprYNUp8ErJ9ZAKcxMfpC4ioVycYCCCc"},"price":{"decimal":"0.99999999","rational":[[1,[99999999]],[1,[100000000]]],"fraction":{"numer":"99999999","denom":"100000000"}},"pubkey":"037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5","is_mine":false,"base_max_volume":{"decimal":"2","rational":[[1,[2]],[1,[1]]],"fraction":{"numer":"2","denom":"1"}},"base_min_volume":{"decimal":"0.0001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000","rational":[[1,[10000]],[1,[99999999]]],"fraction":{"numer":"10000","denom":"99999999"}},"rel_max_volume":{"decimal":"1.99999998","rational":[[1,[99999999]],[1,[50000000]]],"fraction":{"numer":"99999999","denom":"50000000"}},"rel_min_volume":{"decimal":"0.0001","rational":[[1,[1]],[1,[10000]]],"fraction":{"numer":"1","denom":"10000"}},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false}},{"uuid":"2af2d0f3-35e8-4098-8362-99ec9867b9ac","coin":"MORTY","address":{"address_type":"Transparent","address_data":"RB8yufv3YTfdzYnwz5paNnnDynGJG6WsqD"},"price":{"decimal":"1","rational":[[1,[1]],[1,[1]]],"fraction":{"numer":"1","denom":"1"}},"pubkey":"0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732","is_mine":false,"base_max_volume":{"decimal":"363783.58497102","rational":[[1,[4287717287,4234]],[1,[50000000]]],"fraction":{"numer":"18189179248551","denom":"50000000"}},"base_min_volume":{"decimal":"0.0001","rational":[[1,[1]],[1,[10000]]],"fraction":{"numer":"1","denom":"10000"}},"rel_max_volume":{"decimal":"363783.58497102","rational":[[1,[4287717287,4234]],[1,[50000000]]],"fraction":{"numer":"18189179248551","denom":"50000000"}},"rel_min_volume":{"decimal":"0.0001","rational":[[1,[1]],[1,[10000]]],"fraction":{"numer":"1","denom":"10000"}},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false}}],"KMD":[{"uuid":"7c643319-52ea-4323-b0d2-1c448cfc007d","coin":"KMD","address":{"address_type":"Transparent","address_data":"REbPB4qfrB2D5KAnJJK1RTC1CLGa8hVEcM"},"price":{"decimal":"0.005","rational":[[1,[1]],[1,[200]]],"fraction":{"numer":"1","denom":"200"}},"pubkey":"0271e96e7442133b3d479f60423850848f7ed9ad30809f45832e64b01305b19fb6","is_mine":false,"base_max_volume":{"decimal":"9730.650974","rational":[[1,[570358191,1]],[1,[500000]]],"fraction":{"numer":"4865325487","denom":"500000"}},"base_min_volume":{"decimal":"0.02000001","rational":[[1,[2000001]],[1,[100000000]]],"fraction":{"numer":"2000001","denom":"100000000"}},"rel_max_volume":{"decimal":"48.65325487","rational":[[1,[570358191,1]],[1,[100000000]]],"fraction":{"numer":"4865325487","denom":"100000000"}},"rel_min_volume":{"decimal":"0.00010000005","rational":[[1,[2000001]],[1,[2820130816,4]]],"fraction":{"numer":"2000001","denom":"20000000000"}},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":2,"rel_nota":true}}],"ZOMBIE":[{"uuid":"2536e0d8-0a8b-4393-913b-d74543733e5e","coin":"ZOMBIE","address":{"address_type":"Shielded"},"price":{"decimal":"1","rational":[[1,[1]],[1,[1]]],"fraction":{"numer":"1","denom":"1"}},"pubkey":"022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846","is_mine":false,"base_max_volume":{"decimal":"0.23173","rational":[[1,[23173]],[1,[100000]]],"fraction":{"numer":"23173","denom":"100000"}},"base_min_volume":{"decimal":"0.0001","rational":[[1,[1]],[1,[10000]]],"fraction":{"numer":"1","denom":"10000"}},"rel_max_volume":{"decimal":"0.23173","rational":[[1,[23173]],[1,[100000]]],"fraction":{"numer":"23173","denom":"100000"}},"rel_min_volume":{"decimal":"0.0001","rational":[[1,[1]],[1,[10000]]],"fraction":{"numer":"1","denom":"10000"}},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false}}]},"original_tickers":{"UFO":["UFO-segwit"],"WHIVE":["WHIVE-segwit"],"QTUM":["QTUM-segwit"],"BSTY":["BSTY-segwit"],"MONA":["MONA-segwit"],"BTC":["BTC-segwit"],"CDN":["CDN-segwit"],"VIA":["VIA-segwit"],"LCC":["LCC-segwit"],"DGB":["DGB-segwit"],"BTX":["BTX-segwit"],"NMC":["NMC-segwit"],"tBTC":["tBTC-segwit"],"SYS":["SYS-segwit"],"FJC":["FJC-segwit"],"FTC":["FTC-segwit"],"WCN":["WCN-segwit"],"PIC":["PIC-segwit"],"tQTUM":["tQTUM-segwit"],"VTC":["VTC-segwit"],"LTFN":["LTFN-segwit"],"RIC":["RIC-segwit"],"BTE":["BTE-segwit"],"GRS":["GRS-segwit"],"XMY":["XMY-segwit"],"LTC":["LTC-segwit"],"LBC":["LBC-segwit"]}},"id":null} \ No newline at end of file diff --git a/mm2src/adex_cli/src/tests/http_mock_data/buy.http b/mm2src/komodefi_cli/src/tests/http_mock_data/buy.http similarity index 100% rename from mm2src/adex_cli/src/tests/http_mock_data/buy.http rename to mm2src/komodefi_cli/src/tests/http_mock_data/buy.http diff --git a/mm2src/adex_cli/src/tests/http_mock_data/enable.http b/mm2src/komodefi_cli/src/tests/http_mock_data/enable.http similarity index 100% rename from mm2src/adex_cli/src/tests/http_mock_data/enable.http rename to mm2src/komodefi_cli/src/tests/http_mock_data/enable.http diff --git a/mm2src/adex_cli/src/tests/http_mock_data/get_enabled.http b/mm2src/komodefi_cli/src/tests/http_mock_data/get_enabled.http similarity index 100% rename from mm2src/adex_cli/src/tests/http_mock_data/get_enabled.http rename to mm2src/komodefi_cli/src/tests/http_mock_data/get_enabled.http diff --git a/mm2src/komodefi_cli/src/tests/http_mock_data/history-common.http b/mm2src/komodefi_cli/src/tests/http_mock_data/history-common.http new file mode 100644 index 0000000000..aa14d5878a --- /dev/null +++ b/mm2src/komodefi_cli/src/tests/http_mock_data/history-common.http @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +access-control-allow-origin: http://localhost:3000 +content-length: 7330 +date: Fri, 23 Jun 2023 12:45:04 GMT + +{"result":{"orders":[{"uuid":"010a224e-a946-4726-bf6d-e521701053a2","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686066093059,"last_updated":1686134267941,"was_taker":0,"status":"Cancelled"},{"uuid":"ffc41a51-e110-43a0-bb60-203feceba50f","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686066077330,"last_updated":1686066093036,"was_taker":0,"status":"Cancelled"},{"uuid":"869cd8d1-914d-4756-a863-6f73e004c31c","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065916965,"last_updated":1686066093012,"was_taker":0,"status":"Cancelled"},{"uuid":"3af195fe-f202-428d-8849-6c0b7754e894","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065910567,"last_updated":1686065916956,"was_taker":0,"status":"Cancelled"},{"uuid":"73271a03-aab3-4789-83d9-9e9c3c808a96","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065884924,"last_updated":1686065910560,"was_taker":0,"status":"Cancelled"},{"uuid":"e3be3027-333a-4867-928d-61e8442db466","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065869535,"last_updated":1686065884908,"was_taker":0,"status":"Cancelled"},{"uuid":"a7a04dc8-c361-4cae-80e9-b0e883aa3ae1","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065809003,"last_updated":1686065869524,"was_taker":0,"status":"Cancelled"},{"uuid":"ecc708e0-df8f-4d3f-95c7-73927ec92acc","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065747543,"last_updated":1686065808992,"was_taker":0,"status":"Cancelled"},{"uuid":"e1797608-5b7d-45c4-80ae-b66da2e72209","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065716474,"last_updated":1686065747536,"was_taker":0,"status":"Cancelled"},{"uuid":"f164e567-9e41-4faf-8754-3f87edd5b6d7","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065552529,"last_updated":1686065716468,"was_taker":0,"status":"Cancelled"},{"uuid":"707c8428-779c-4e78-bcbd-97a7e403c14a","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065514865,"last_updated":1686065552520,"was_taker":0,"status":"Cancelled"},{"uuid":"b0992fe8-c019-4c86-9d07-03055eaa86ab","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":2.0,"created_at":1686065014449,"last_updated":1686065514854,"was_taker":0,"status":"Cancelled"},{"uuid":"85d6fc7c-5614-492a-9e85-4c19fab65949","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":2.0,"created_at":1686064987902,"last_updated":1686065000372,"was_taker":0,"status":"Cancelled"},{"uuid":"5968ffcf-5b25-40c8-8bd7-c7cf9d3154f9","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":8.16979475,"created_at":1686064971518,"last_updated":1686064987895,"was_taker":0,"status":"Cancelled"},{"uuid":"eab52e14-1460-4ece-943d-7a2950a22600","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":2.0,"created_at":1686064891752,"last_updated":1686064919431,"was_taker":0,"status":"Cancelled"},{"uuid":"4318bf91-8416-417d-ac30-7745f30df687","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1000.0,"volume":2.0,"created_at":1686064877589,"last_updated":1686064891743,"was_taker":0,"status":"Cancelled"},{"uuid":"a2f6930d-b97d-4c8c-9330-54912fd3b0b9","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1000.0,"volume":8.16979475,"created_at":1686064855520,"last_updated":1686064870271,"was_taker":0,"status":"Cancelled"},{"uuid":"d68a81fd-7a90-4785-ad83-d3b06e362f6f","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1000.0,"volume":0.001,"created_at":1686064685895,"last_updated":1686064855511,"was_taker":0,"status":"Cancelled"},{"uuid":"4c0ca34a-487c-43ef-b1f5-13eb4e1a8ece","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":0.001,"created_at":1686064665693,"last_updated":1686064685887,"was_taker":0,"status":"Cancelled"},{"uuid":"cba44f7f-5d52-492e-a3f0-44ee006296da","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":1.5,"created_at":1686064437319,"last_updated":1686064665686,"was_taker":0,"status":"Cancelled"},{"uuid":"02db133a-5e69-4056-9855-98d961927fdd","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":1.5,"created_at":1686064157542,"last_updated":1686064437310,"was_taker":0,"status":"Cancelled"},{"uuid":"6476641f-9014-496c-a608-1bdf81cfbf2e","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":8.16979475,"created_at":1686064138869,"last_updated":1686064157530,"was_taker":0,"status":"Cancelled"},{"uuid":"5a253d33-7c7c-40f5-977f-7805013e63b4","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":8.16979475,"created_at":1686063977416,"last_updated":1686063988774,"was_taker":0,"status":"Cancelled"},{"uuid":"064bf73f-2a2a-4ca0-b83f-344ec16c5f29","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":8.16979475,"created_at":1686063892488,"last_updated":1686063977405,"was_taker":0,"status":"Cancelled"},{"uuid":"475309b5-d6e1-40b2-a2d4-5307aa999d74","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":1.333,"created_at":1686063873103,"last_updated":1686063892477,"was_taker":0,"status":"Cancelled"},{"uuid":"916bbc09-6b57-4ded-93b0-5a8461be0e99","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":0.5,"created_at":1686063186663,"last_updated":1686063787261,"was_taker":0,"status":"Cancelled"},{"uuid":"fa256795-9ff3-4983-85d6-8a3fe4fb6f3a","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":8.16979475,"created_at":1686063140222,"last_updated":1686063179713,"was_taker":0,"status":"Cancelled"},{"uuid":"23d2c04b-6fa5-4e76-bde9-4a8fe0b7a144","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":8.16979475,"created_at":1686063100967,"last_updated":1686063140214,"was_taker":0,"status":"Cancelled"},{"uuid":"4e365431-4db0-4365-a67d-1e39820090a2","order_type":"Taker","initial_action":"Buy","base":"RICK","rel":"MORTY","price":1.1,"volume":0.05,"created_at":1683297331593,"last_updated":1683297362233,"was_taker":0,"status":"TimedOut"},{"uuid":"601bfc00-9033-45d8-86b2-3dbd54881212","order_type":"Taker","initial_action":"Buy","base":"RICK","rel":"MORTY","price":1.1,"volume":0.05,"created_at":1683297295530,"last_updated":1683297298809,"was_taker":0,"status":"Fulfilled"}],"details":[],"found_records":30,"warnings":[]}} \ No newline at end of file diff --git a/mm2src/komodefi_cli/src/tests/http_mock_data/history-makers-detailed.http b/mm2src/komodefi_cli/src/tests/http_mock_data/history-makers-detailed.http new file mode 100644 index 0000000000..9d4a49c6e1 --- /dev/null +++ b/mm2src/komodefi_cli/src/tests/http_mock_data/history-makers-detailed.http @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +access-control-allow-origin: http://localhost:3000 +content-length: 24813 +date: Fri, 23 Jun 2023 13:03:03 GMT + +{"result":{"orders":[{"uuid":"010a224e-a946-4726-bf6d-e521701053a2","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686066093059,"last_updated":1686134267941,"was_taker":0,"status":"Cancelled"},{"uuid":"ffc41a51-e110-43a0-bb60-203feceba50f","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686066077330,"last_updated":1686066093036,"was_taker":0,"status":"Cancelled"},{"uuid":"869cd8d1-914d-4756-a863-6f73e004c31c","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065916965,"last_updated":1686066093012,"was_taker":0,"status":"Cancelled"},{"uuid":"3af195fe-f202-428d-8849-6c0b7754e894","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065910567,"last_updated":1686065916956,"was_taker":0,"status":"Cancelled"},{"uuid":"73271a03-aab3-4789-83d9-9e9c3c808a96","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065884924,"last_updated":1686065910560,"was_taker":0,"status":"Cancelled"},{"uuid":"e3be3027-333a-4867-928d-61e8442db466","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065869535,"last_updated":1686065884908,"was_taker":0,"status":"Cancelled"},{"uuid":"a7a04dc8-c361-4cae-80e9-b0e883aa3ae1","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065809003,"last_updated":1686065869524,"was_taker":0,"status":"Cancelled"},{"uuid":"ecc708e0-df8f-4d3f-95c7-73927ec92acc","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065747543,"last_updated":1686065808992,"was_taker":0,"status":"Cancelled"},{"uuid":"e1797608-5b7d-45c4-80ae-b66da2e72209","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065716474,"last_updated":1686065747536,"was_taker":0,"status":"Cancelled"},{"uuid":"f164e567-9e41-4faf-8754-3f87edd5b6d7","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065552529,"last_updated":1686065716468,"was_taker":0,"status":"Cancelled"},{"uuid":"707c8428-779c-4e78-bcbd-97a7e403c14a","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065514865,"last_updated":1686065552520,"was_taker":0,"status":"Cancelled"},{"uuid":"b0992fe8-c019-4c86-9d07-03055eaa86ab","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":2.0,"created_at":1686065014449,"last_updated":1686065514854,"was_taker":0,"status":"Cancelled"},{"uuid":"85d6fc7c-5614-492a-9e85-4c19fab65949","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":2.0,"created_at":1686064987902,"last_updated":1686065000372,"was_taker":0,"status":"Cancelled"},{"uuid":"5968ffcf-5b25-40c8-8bd7-c7cf9d3154f9","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":8.16979475,"created_at":1686064971518,"last_updated":1686064987895,"was_taker":0,"status":"Cancelled"},{"uuid":"eab52e14-1460-4ece-943d-7a2950a22600","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":2.0,"created_at":1686064891752,"last_updated":1686064919431,"was_taker":0,"status":"Cancelled"},{"uuid":"4318bf91-8416-417d-ac30-7745f30df687","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1000.0,"volume":2.0,"created_at":1686064877589,"last_updated":1686064891743,"was_taker":0,"status":"Cancelled"},{"uuid":"a2f6930d-b97d-4c8c-9330-54912fd3b0b9","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1000.0,"volume":8.16979475,"created_at":1686064855520,"last_updated":1686064870271,"was_taker":0,"status":"Cancelled"},{"uuid":"d68a81fd-7a90-4785-ad83-d3b06e362f6f","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1000.0,"volume":0.001,"created_at":1686064685895,"last_updated":1686064855511,"was_taker":0,"status":"Cancelled"},{"uuid":"4c0ca34a-487c-43ef-b1f5-13eb4e1a8ece","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":0.001,"created_at":1686064665693,"last_updated":1686064685887,"was_taker":0,"status":"Cancelled"},{"uuid":"cba44f7f-5d52-492e-a3f0-44ee006296da","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":1.5,"created_at":1686064437319,"last_updated":1686064665686,"was_taker":0,"status":"Cancelled"},{"uuid":"02db133a-5e69-4056-9855-98d961927fdd","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":1.5,"created_at":1686064157542,"last_updated":1686064437310,"was_taker":0,"status":"Cancelled"},{"uuid":"6476641f-9014-496c-a608-1bdf81cfbf2e","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":8.16979475,"created_at":1686064138869,"last_updated":1686064157530,"was_taker":0,"status":"Cancelled"},{"uuid":"5a253d33-7c7c-40f5-977f-7805013e63b4","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":8.16979475,"created_at":1686063977416,"last_updated":1686063988774,"was_taker":0,"status":"Cancelled"},{"uuid":"064bf73f-2a2a-4ca0-b83f-344ec16c5f29","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":8.16979475,"created_at":1686063892488,"last_updated":1686063977405,"was_taker":0,"status":"Cancelled"},{"uuid":"475309b5-d6e1-40b2-a2d4-5307aa999d74","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":1.333,"created_at":1686063873103,"last_updated":1686063892477,"was_taker":0,"status":"Cancelled"},{"uuid":"916bbc09-6b57-4ded-93b0-5a8461be0e99","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":0.5,"created_at":1686063186663,"last_updated":1686063787261,"was_taker":0,"status":"Cancelled"},{"uuid":"fa256795-9ff3-4983-85d6-8a3fe4fb6f3a","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":8.16979475,"created_at":1686063140222,"last_updated":1686063179713,"was_taker":0,"status":"Cancelled"},{"uuid":"23d2c04b-6fa5-4e76-bde9-4a8fe0b7a144","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":8.16979475,"created_at":1686063100967,"last_updated":1686063140214,"was_taker":0,"status":"Cancelled"},{"uuid":"4e365431-4db0-4365-a67d-1e39820090a2","order_type":"Taker","initial_action":"Buy","base":"RICK","rel":"MORTY","price":1.1,"volume":0.05,"created_at":1683297331593,"last_updated":1683297362233,"was_taker":0,"status":"TimedOut"},{"uuid":"601bfc00-9033-45d8-86b2-3dbd54881212","order_type":"Taker","initial_action":"Buy","base":"RICK","rel":"MORTY","price":1.1,"volume":0.05,"created_at":1683297295530,"last_updated":1683297298809,"was_taker":0,"status":"Fulfilled"}],"details":[{"type":"Maker","order":{"uuid":"010a224e-a946-4726-bf6d-e521701053a2","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686066093059,"updated_at":1686066093059,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"ffc41a51-e110-43a0-bb60-203feceba50f","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686066077330,"updated_at":1686066077330,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"869cd8d1-914d-4756-a863-6f73e004c31c","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065916965,"updated_at":1686065916965,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"3af195fe-f202-428d-8849-6c0b7754e894","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065910567,"updated_at":1686065910567,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"73271a03-aab3-4789-83d9-9e9c3c808a96","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065884924,"updated_at":1686065884924,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"e3be3027-333a-4867-928d-61e8442db466","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065869535,"updated_at":1686065869535,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"a7a04dc8-c361-4cae-80e9-b0e883aa3ae1","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065809003,"updated_at":1686065809003,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"ecc708e0-df8f-4d3f-95c7-73927ec92acc","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065747543,"updated_at":1686065747543,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"e1797608-5b7d-45c4-80ae-b66da2e72209","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686065716474,"updated_at":1686065716474,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"f164e567-9e41-4faf-8754-3f87edd5b6d7","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686065552529,"updated_at":1686065552529,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"707c8428-779c-4e78-bcbd-97a7e403c14a","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686065514865,"updated_at":1686065514865,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"b0992fe8-c019-4c86-9d07-03055eaa86ab","base":"RICK","rel":"MORTY","price":"1.5","price_rat":[[1,[3]],[1,[2]]],"max_base_vol":"2","max_base_vol_rat":[[1,[2]],[1,[1]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686065014449,"updated_at":1686065014449,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"85d6fc7c-5614-492a-9e85-4c19fab65949","base":"RICK","rel":"MORTY","price":"1.5","price_rat":[[1,[3]],[1,[2]]],"max_base_vol":"2","max_base_vol_rat":[[1,[2]],[1,[1]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064987902,"updated_at":1686064987902,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"5968ffcf-5b25-40c8-8bd7-c7cf9d3154f9","base":"RICK","rel":"MORTY","price":"1.5","price_rat":[[1,[3]],[1,[2]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064971518,"updated_at":1686064971518,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"eab52e14-1460-4ece-943d-7a2950a22600","base":"RICK","rel":"MORTY","price":"1.5","price_rat":[[1,[3]],[1,[2]]],"max_base_vol":"2","max_base_vol_rat":[[1,[2]],[1,[1]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064891752,"updated_at":1686064891752,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"4318bf91-8416-417d-ac30-7745f30df687","base":"RICK","rel":"MORTY","price":"1000","price_rat":[[1,[1000]],[1,[1]]],"max_base_vol":"2","max_base_vol_rat":[[1,[2]],[1,[1]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064877589,"updated_at":1686064877589,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"a2f6930d-b97d-4c8c-9330-54912fd3b0b9","base":"RICK","rel":"MORTY","price":"1000","price_rat":[[1,[1000]],[1,[1]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064855520,"updated_at":1686064855520,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"d68a81fd-7a90-4785-ad83-d3b06e362f6f","base":"RICK","rel":"MORTY","price":"1000","price_rat":[[1,[1000]],[1,[1]]],"max_base_vol":"0.001","max_base_vol_rat":[[1,[1]],[1,[1000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064685895,"updated_at":1686064685895,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"4c0ca34a-487c-43ef-b1f5-13eb4e1a8ece","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"0.001","max_base_vol_rat":[[1,[1]],[1,[1000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064665693,"updated_at":1686064665693,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"cba44f7f-5d52-492e-a3f0-44ee006296da","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"1.5","max_base_vol_rat":[[1,[3]],[1,[2]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064437319,"updated_at":1686064437319,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"02db133a-5e69-4056-9855-98d961927fdd","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"1.5","max_base_vol_rat":[[1,[3]],[1,[2]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064157542,"updated_at":1686064157542,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"6476641f-9014-496c-a608-1bdf81cfbf2e","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064138869,"updated_at":1686064138869,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"5a253d33-7c7c-40f5-977f-7805013e63b4","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063977416,"updated_at":1686063977416,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"064bf73f-2a2a-4ca0-b83f-344ec16c5f29","base":"RICK","rel":"MORTY","price":"1.2","price_rat":[[1,[6]],[1,[5]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063892488,"updated_at":1686063892488,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"475309b5-d6e1-40b2-a2d4-5307aa999d74","base":"RICK","rel":"MORTY","price":"1.2","price_rat":[[1,[6]],[1,[5]]],"max_base_vol":"1.333","max_base_vol_rat":[[1,[1333]],[1,[1000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063873103,"updated_at":1686063873103,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"916bbc09-6b57-4ded-93b0-5a8461be0e99","base":"RICK","rel":"MORTY","price":"1.2","price_rat":[[1,[6]],[1,[5]]],"max_base_vol":"0.5","max_base_vol_rat":[[1,[1]],[1,[2]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063186663,"updated_at":1686063186663,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"fa256795-9ff3-4983-85d6-8a3fe4fb6f3a","base":"RICK","rel":"MORTY","price":"1.2","price_rat":[[1,[6]],[1,[5]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063140222,"updated_at":1686063140222,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"23d2c04b-6fa5-4e76-bde9-4a8fe0b7a144","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063100967,"updated_at":1686063100967,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Taker","order":{"request":{"uuid":"4e365431-4db0-4365-a67d-1e39820090a2","base":"RICK","rel":"MORTY","base_amount":"0.05","base_amount_rat":[[1,[1]],[1,[20]]],"rel_amount":"0.055","rel_amount_rat":[[1,[11]],[1,[200]]],"action":"Buy","method":"request","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","match_by":{"type":"Any"},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false}},"created_at":1683297331593,"matches":{"efbcb9d6-2d9d-4fa0-af82-919c7da46967":{"reserved":{"base":"RICK","rel":"MORTY","base_amount":"0.05","base_amount_rat":[[1,[1]],[1,[20]]],"rel_amount":"0.0499999995","rel_amount_rat":[[1,[99999999]],[1,[2000000000]]],"taker_order_uuid":"4e365431-4db0-4365-a67d-1e39820090a2","maker_order_uuid":"efbcb9d6-2d9d-4fa0-af82-919c7da46967","sender_pubkey":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","conf_settings":{"base_confs":0,"base_nota":false,"rel_confs":0,"rel_nota":false},"method":"reserved"},"connect":{"taker_order_uuid":"4e365431-4db0-4365-a67d-1e39820090a2","maker_order_uuid":"efbcb9d6-2d9d-4fa0-af82-919c7da46967","method":"connect","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5"},"connected":null,"last_updated":1683297334735}},"order_type":{"type":"GoodTillCancelled"},"cancellable":false,"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Taker","order":{"request":{"uuid":"601bfc00-9033-45d8-86b2-3dbd54881212","base":"RICK","rel":"MORTY","base_amount":"0.05","base_amount_rat":[[1,[1]],[1,[20]]],"rel_amount":"0.055","rel_amount_rat":[[1,[11]],[1,[200]]],"action":"Buy","method":"request","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","match_by":{"type":"Any"},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false}},"created_at":1683297295530,"matches":{"e16ee590-0562-4fbe-88cd-3cfd6e580615":{"reserved":{"base":"RICK","rel":"MORTY","base_amount":"0.05","base_amount_rat":[[1,[1]],[1,[20]]],"rel_amount":"0.0499999995","rel_amount_rat":[[1,[99999999]],[1,[2000000000]]],"taker_order_uuid":"601bfc00-9033-45d8-86b2-3dbd54881212","maker_order_uuid":"e16ee590-0562-4fbe-88cd-3cfd6e580615","sender_pubkey":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","conf_settings":{"base_confs":0,"base_nota":false,"rel_confs":0,"rel_nota":false},"method":"reserved"},"connect":{"taker_order_uuid":"601bfc00-9033-45d8-86b2-3dbd54881212","maker_order_uuid":"e16ee590-0562-4fbe-88cd-3cfd6e580615","method":"connect","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5"},"connected":null,"last_updated":1683297298668}},"order_type":{"type":"GoodTillCancelled"},"cancellable":false,"base_orderbook_ticker":null,"rel_orderbook_ticker":null}}],"found_records":30,"warnings":[]}} \ No newline at end of file diff --git a/mm2src/komodefi_cli/src/tests/http_mock_data/history-takers-detailed.http b/mm2src/komodefi_cli/src/tests/http_mock_data/history-takers-detailed.http new file mode 100644 index 0000000000..223e3d051b --- /dev/null +++ b/mm2src/komodefi_cli/src/tests/http_mock_data/history-takers-detailed.http @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +access-control-allow-origin: http://localhost:3000 +content-length: 24813 +date: Fri, 23 Jun 2023 12:48:45 GMT + +{"result":{"orders":[{"uuid":"010a224e-a946-4726-bf6d-e521701053a2","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686066093059,"last_updated":1686134267941,"was_taker":0,"status":"Cancelled"},{"uuid":"ffc41a51-e110-43a0-bb60-203feceba50f","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686066077330,"last_updated":1686066093036,"was_taker":0,"status":"Cancelled"},{"uuid":"869cd8d1-914d-4756-a863-6f73e004c31c","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065916965,"last_updated":1686066093012,"was_taker":0,"status":"Cancelled"},{"uuid":"3af195fe-f202-428d-8849-6c0b7754e894","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065910567,"last_updated":1686065916956,"was_taker":0,"status":"Cancelled"},{"uuid":"73271a03-aab3-4789-83d9-9e9c3c808a96","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065884924,"last_updated":1686065910560,"was_taker":0,"status":"Cancelled"},{"uuid":"e3be3027-333a-4867-928d-61e8442db466","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065869535,"last_updated":1686065884908,"was_taker":0,"status":"Cancelled"},{"uuid":"a7a04dc8-c361-4cae-80e9-b0e883aa3ae1","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065809003,"last_updated":1686065869524,"was_taker":0,"status":"Cancelled"},{"uuid":"ecc708e0-df8f-4d3f-95c7-73927ec92acc","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065747543,"last_updated":1686065808992,"was_taker":0,"status":"Cancelled"},{"uuid":"e1797608-5b7d-45c4-80ae-b66da2e72209","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065716474,"last_updated":1686065747536,"was_taker":0,"status":"Cancelled"},{"uuid":"f164e567-9e41-4faf-8754-3f87edd5b6d7","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065552529,"last_updated":1686065716468,"was_taker":0,"status":"Cancelled"},{"uuid":"707c8428-779c-4e78-bcbd-97a7e403c14a","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.23,"volume":8.16979475,"created_at":1686065514865,"last_updated":1686065552520,"was_taker":0,"status":"Cancelled"},{"uuid":"b0992fe8-c019-4c86-9d07-03055eaa86ab","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":2.0,"created_at":1686065014449,"last_updated":1686065514854,"was_taker":0,"status":"Cancelled"},{"uuid":"85d6fc7c-5614-492a-9e85-4c19fab65949","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":2.0,"created_at":1686064987902,"last_updated":1686065000372,"was_taker":0,"status":"Cancelled"},{"uuid":"5968ffcf-5b25-40c8-8bd7-c7cf9d3154f9","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":8.16979475,"created_at":1686064971518,"last_updated":1686064987895,"was_taker":0,"status":"Cancelled"},{"uuid":"eab52e14-1460-4ece-943d-7a2950a22600","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.5,"volume":2.0,"created_at":1686064891752,"last_updated":1686064919431,"was_taker":0,"status":"Cancelled"},{"uuid":"4318bf91-8416-417d-ac30-7745f30df687","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1000.0,"volume":2.0,"created_at":1686064877589,"last_updated":1686064891743,"was_taker":0,"status":"Cancelled"},{"uuid":"a2f6930d-b97d-4c8c-9330-54912fd3b0b9","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1000.0,"volume":8.16979475,"created_at":1686064855520,"last_updated":1686064870271,"was_taker":0,"status":"Cancelled"},{"uuid":"d68a81fd-7a90-4785-ad83-d3b06e362f6f","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1000.0,"volume":0.001,"created_at":1686064685895,"last_updated":1686064855511,"was_taker":0,"status":"Cancelled"},{"uuid":"4c0ca34a-487c-43ef-b1f5-13eb4e1a8ece","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":0.001,"created_at":1686064665693,"last_updated":1686064685887,"was_taker":0,"status":"Cancelled"},{"uuid":"cba44f7f-5d52-492e-a3f0-44ee006296da","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":1.5,"created_at":1686064437319,"last_updated":1686064665686,"was_taker":0,"status":"Cancelled"},{"uuid":"02db133a-5e69-4056-9855-98d961927fdd","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":1.5,"created_at":1686064157542,"last_updated":1686064437310,"was_taker":0,"status":"Cancelled"},{"uuid":"6476641f-9014-496c-a608-1bdf81cfbf2e","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":8.16979475,"created_at":1686064138869,"last_updated":1686064157530,"was_taker":0,"status":"Cancelled"},{"uuid":"5a253d33-7c7c-40f5-977f-7805013e63b4","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":8.16979475,"created_at":1686063977416,"last_updated":1686063988774,"was_taker":0,"status":"Cancelled"},{"uuid":"064bf73f-2a2a-4ca0-b83f-344ec16c5f29","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":8.16979475,"created_at":1686063892488,"last_updated":1686063977405,"was_taker":0,"status":"Cancelled"},{"uuid":"475309b5-d6e1-40b2-a2d4-5307aa999d74","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":1.333,"created_at":1686063873103,"last_updated":1686063892477,"was_taker":0,"status":"Cancelled"},{"uuid":"916bbc09-6b57-4ded-93b0-5a8461be0e99","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":0.5,"created_at":1686063186663,"last_updated":1686063787261,"was_taker":0,"status":"Cancelled"},{"uuid":"fa256795-9ff3-4983-85d6-8a3fe4fb6f3a","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.2,"volume":8.16979475,"created_at":1686063140222,"last_updated":1686063179713,"was_taker":0,"status":"Cancelled"},{"uuid":"23d2c04b-6fa5-4e76-bde9-4a8fe0b7a144","order_type":"Maker","initial_action":"Sell","base":"RICK","rel":"MORTY","price":1.1,"volume":8.16979475,"created_at":1686063100967,"last_updated":1686063140214,"was_taker":0,"status":"Cancelled"},{"uuid":"4e365431-4db0-4365-a67d-1e39820090a2","order_type":"Taker","initial_action":"Buy","base":"RICK","rel":"MORTY","price":1.1,"volume":0.05,"created_at":1683297331593,"last_updated":1683297362233,"was_taker":0,"status":"TimedOut"},{"uuid":"601bfc00-9033-45d8-86b2-3dbd54881212","order_type":"Taker","initial_action":"Buy","base":"RICK","rel":"MORTY","price":1.1,"volume":0.05,"created_at":1683297295530,"last_updated":1683297298809,"was_taker":0,"status":"Fulfilled"}],"details":[{"type":"Maker","order":{"uuid":"010a224e-a946-4726-bf6d-e521701053a2","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686066093059,"updated_at":1686066093059,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"ffc41a51-e110-43a0-bb60-203feceba50f","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686066077330,"updated_at":1686066077330,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"869cd8d1-914d-4756-a863-6f73e004c31c","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065916965,"updated_at":1686065916965,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"3af195fe-f202-428d-8849-6c0b7754e894","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065910567,"updated_at":1686065910567,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"73271a03-aab3-4789-83d9-9e9c3c808a96","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065884924,"updated_at":1686065884924,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"e3be3027-333a-4867-928d-61e8442db466","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065869535,"updated_at":1686065869535,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"a7a04dc8-c361-4cae-80e9-b0e883aa3ae1","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065809003,"updated_at":1686065809003,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":3,"base_nota":true,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"ecc708e0-df8f-4d3f-95c7-73927ec92acc","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"1","min_base_vol_rat":[[1,[1]],[1,[1]]],"created_at":1686065747543,"updated_at":1686065747543,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"e1797608-5b7d-45c4-80ae-b66da2e72209","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686065716474,"updated_at":1686065716474,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"f164e567-9e41-4faf-8754-3f87edd5b6d7","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686065552529,"updated_at":1686065552529,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"707c8428-779c-4e78-bcbd-97a7e403c14a","base":"RICK","rel":"MORTY","price":"1.23","price_rat":[[1,[123]],[1,[100]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686065514865,"updated_at":1686065514865,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"b0992fe8-c019-4c86-9d07-03055eaa86ab","base":"RICK","rel":"MORTY","price":"1.5","price_rat":[[1,[3]],[1,[2]]],"max_base_vol":"2","max_base_vol_rat":[[1,[2]],[1,[1]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686065014449,"updated_at":1686065014449,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"85d6fc7c-5614-492a-9e85-4c19fab65949","base":"RICK","rel":"MORTY","price":"1.5","price_rat":[[1,[3]],[1,[2]]],"max_base_vol":"2","max_base_vol_rat":[[1,[2]],[1,[1]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064987902,"updated_at":1686064987902,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"5968ffcf-5b25-40c8-8bd7-c7cf9d3154f9","base":"RICK","rel":"MORTY","price":"1.5","price_rat":[[1,[3]],[1,[2]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064971518,"updated_at":1686064971518,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"eab52e14-1460-4ece-943d-7a2950a22600","base":"RICK","rel":"MORTY","price":"1.5","price_rat":[[1,[3]],[1,[2]]],"max_base_vol":"2","max_base_vol_rat":[[1,[2]],[1,[1]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064891752,"updated_at":1686064891752,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"4318bf91-8416-417d-ac30-7745f30df687","base":"RICK","rel":"MORTY","price":"1000","price_rat":[[1,[1000]],[1,[1]]],"max_base_vol":"2","max_base_vol_rat":[[1,[2]],[1,[1]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064877589,"updated_at":1686064877589,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"a2f6930d-b97d-4c8c-9330-54912fd3b0b9","base":"RICK","rel":"MORTY","price":"1000","price_rat":[[1,[1000]],[1,[1]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064855520,"updated_at":1686064855520,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"d68a81fd-7a90-4785-ad83-d3b06e362f6f","base":"RICK","rel":"MORTY","price":"1000","price_rat":[[1,[1000]],[1,[1]]],"max_base_vol":"0.001","max_base_vol_rat":[[1,[1]],[1,[1000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064685895,"updated_at":1686064685895,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"4c0ca34a-487c-43ef-b1f5-13eb4e1a8ece","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"0.001","max_base_vol_rat":[[1,[1]],[1,[1000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064665693,"updated_at":1686064665693,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"cba44f7f-5d52-492e-a3f0-44ee006296da","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"1.5","max_base_vol_rat":[[1,[3]],[1,[2]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064437319,"updated_at":1686064437319,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"02db133a-5e69-4056-9855-98d961927fdd","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"1.5","max_base_vol_rat":[[1,[3]],[1,[2]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064157542,"updated_at":1686064157542,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"6476641f-9014-496c-a608-1bdf81cfbf2e","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686064138869,"updated_at":1686064138869,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"5a253d33-7c7c-40f5-977f-7805013e63b4","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063977416,"updated_at":1686063977416,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"064bf73f-2a2a-4ca0-b83f-344ec16c5f29","base":"RICK","rel":"MORTY","price":"1.2","price_rat":[[1,[6]],[1,[5]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063892488,"updated_at":1686063892488,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"475309b5-d6e1-40b2-a2d4-5307aa999d74","base":"RICK","rel":"MORTY","price":"1.2","price_rat":[[1,[6]],[1,[5]]],"max_base_vol":"1.333","max_base_vol_rat":[[1,[1333]],[1,[1000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063873103,"updated_at":1686063873103,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"916bbc09-6b57-4ded-93b0-5a8461be0e99","base":"RICK","rel":"MORTY","price":"1.2","price_rat":[[1,[6]],[1,[5]]],"max_base_vol":"0.5","max_base_vol_rat":[[1,[1]],[1,[2]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063186663,"updated_at":1686063186663,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"fa256795-9ff3-4983-85d6-8a3fe4fb6f3a","base":"RICK","rel":"MORTY","price":"1.2","price_rat":[[1,[6]],[1,[5]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063140222,"updated_at":1686063140222,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Maker","order":{"uuid":"23d2c04b-6fa5-4e76-bde9-4a8fe0b7a144","base":"RICK","rel":"MORTY","price":"1.1","price_rat":[[1,[11]],[1,[10]]],"max_base_vol":"8.16979475","max_base_vol_rat":[[1,[32679179]],[1,[4000000]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1686063100967,"updated_at":1686063100967,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Taker","order":{"request":{"uuid":"4e365431-4db0-4365-a67d-1e39820090a2","base":"RICK","rel":"MORTY","base_amount":"0.05","base_amount_rat":[[1,[1]],[1,[20]]],"rel_amount":"0.055","rel_amount_rat":[[1,[11]],[1,[200]]],"action":"Buy","method":"request","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","match_by":{"type":"Any"},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false}},"created_at":1683297331593,"matches":{"efbcb9d6-2d9d-4fa0-af82-919c7da46967":{"reserved":{"base":"RICK","rel":"MORTY","base_amount":"0.05","base_amount_rat":[[1,[1]],[1,[20]]],"rel_amount":"0.0499999995","rel_amount_rat":[[1,[99999999]],[1,[2000000000]]],"taker_order_uuid":"4e365431-4db0-4365-a67d-1e39820090a2","maker_order_uuid":"efbcb9d6-2d9d-4fa0-af82-919c7da46967","sender_pubkey":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","conf_settings":{"base_confs":0,"base_nota":false,"rel_confs":0,"rel_nota":false},"method":"reserved"},"connect":{"taker_order_uuid":"4e365431-4db0-4365-a67d-1e39820090a2","maker_order_uuid":"efbcb9d6-2d9d-4fa0-af82-919c7da46967","method":"connect","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5"},"connected":null,"last_updated":1683297334735}},"order_type":{"type":"GoodTillCancelled"},"cancellable":false,"base_orderbook_ticker":null,"rel_orderbook_ticker":null}},{"type":"Taker","order":{"request":{"uuid":"601bfc00-9033-45d8-86b2-3dbd54881212","base":"RICK","rel":"MORTY","base_amount":"0.05","base_amount_rat":[[1,[1]],[1,[20]]],"rel_amount":"0.055","rel_amount_rat":[[1,[11]],[1,[200]]],"action":"Buy","method":"request","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","match_by":{"type":"Any"},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false}},"created_at":1683297295530,"matches":{"e16ee590-0562-4fbe-88cd-3cfd6e580615":{"reserved":{"base":"RICK","rel":"MORTY","base_amount":"0.05","base_amount_rat":[[1,[1]],[1,[20]]],"rel_amount":"0.0499999995","rel_amount_rat":[[1,[99999999]],[1,[2000000000]]],"taker_order_uuid":"601bfc00-9033-45d8-86b2-3dbd54881212","maker_order_uuid":"e16ee590-0562-4fbe-88cd-3cfd6e580615","sender_pubkey":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","conf_settings":{"base_confs":0,"base_nota":false,"rel_confs":0,"rel_nota":false},"method":"reserved"},"connect":{"taker_order_uuid":"601bfc00-9033-45d8-86b2-3dbd54881212","maker_order_uuid":"e16ee590-0562-4fbe-88cd-3cfd6e580615","method":"connect","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5"},"connected":null,"last_updated":1683297298668}},"order_type":{"type":"GoodTillCancelled"},"cancellable":false,"base_orderbook_ticker":null,"rel_orderbook_ticker":null}}],"found_records":30,"warnings":[]}} \ No newline at end of file diff --git a/mm2src/komodefi_cli/src/tests/http_mock_data/my_orders.http b/mm2src/komodefi_cli/src/tests/http_mock_data/my_orders.http new file mode 100644 index 0000000000..bb4097be07 --- /dev/null +++ b/mm2src/komodefi_cli/src/tests/http_mock_data/my_orders.http @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +access-control-allow-origin: http://localhost:3000 +content-length: 3713 +date: Mon, 29 May 2023 12:19:14 GMT + +{"result":{"maker_orders":{"7f097435-f482-415b-9bdf-6780f4be4828":{"uuid":"7f097435-f482-415b-9bdf-6780f4be4828","base":"RICK","rel":"MORTY","price":"1.111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111","price_rat":[[1,[10]],[1,[9]]],"max_base_vol":"0.09","max_base_vol_rat":[[1,[9]],[1,[100]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1685362669110,"updated_at":1685362669110,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null,"cancellable":true,"available_amount":"0.09"},"28315c31-4fd7-4847-9873-352924252fbe":{"uuid":"28315c31-4fd7-4847-9873-352924252fbe","base":"RICK","rel":"MORTY","price":"1.111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111","price_rat":[[1,[10]],[1,[9]]],"max_base_vol":"0.09","max_base_vol_rat":[[1,[9]],[1,[100]]],"min_base_vol":"0.0001","min_base_vol_rat":[[1,[1]],[1,[10000]]],"created_at":1685362666776,"updated_at":1685362666776,"matches":{},"started_swaps":[],"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"base_orderbook_ticker":null,"rel_orderbook_ticker":null,"cancellable":true,"available_amount":"0.09"}},"taker_orders":{"ce90f89f-8074-4e9f-8649-7f7689c56fa9":{"request":{"uuid":"ce90f89f-8074-4e9f-8649-7f7689c56fa9","base":"MORTY","rel":"RICK","base_amount":"0.1","base_amount_rat":[[1,[1]],[1,[10]]],"rel_amount":"0.11","rel_amount_rat":[[1,[11]],[1,[100]]],"action":"Buy","method":"request","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","match_by":{"type":"Any"},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false}},"created_at":1685362750777,"matches":{"09a0e11e-837e-4763-bc1f-1659573df9dd":{"reserved":{"base":"MORTY","rel":"RICK","base_amount":"0.1","base_amount_rat":[[1,[1]],[1,[10]]],"rel_amount":"0.099999999","rel_amount_rat":[[1,[99999999]],[1,[1000000000]]],"taker_order_uuid":"ce90f89f-8074-4e9f-8649-7f7689c56fa9","maker_order_uuid":"09a0e11e-837e-4763-bc1f-1659573df9dd","sender_pubkey":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"method":"reserved"},"connect":{"taker_order_uuid":"ce90f89f-8074-4e9f-8649-7f7689c56fa9","maker_order_uuid":"09a0e11e-837e-4763-bc1f-1659573df9dd","method":"connect","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5"},"connected":null,"last_updated":0}},"order_type":{"type":"GoodTillCancelled"},"cancellable":false,"base_orderbook_ticker":null,"rel_orderbook_ticker":null},"2739152a-3f87-4f6d-a199-3659aa1e864f":{"request":{"uuid":"2739152a-3f87-4f6d-a199-3659aa1e864f","base":"MORTY","rel":"RICK","base_amount":"0.1","base_amount_rat":[[1,[1]],[1,[10]]],"rel_amount":"0.09","rel_amount_rat":[[1,[9]],[1,[100]]],"action":"Buy","method":"request","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","match_by":{"type":"Any"},"conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false}},"created_at":1685362732713,"matches":{},"order_type":{"type":"GoodTillCancelled"},"cancellable":true,"base_orderbook_ticker":null,"rel_orderbook_ticker":null}}}} \ No newline at end of file diff --git a/mm2src/adex_cli/src/tests/http_mock_data/orderbook.http b/mm2src/komodefi_cli/src/tests/http_mock_data/orderbook.http similarity index 100% rename from mm2src/adex_cli/src/tests/http_mock_data/orderbook.http rename to mm2src/komodefi_cli/src/tests/http_mock_data/orderbook.http diff --git a/mm2src/komodefi_cli/src/tests/http_mock_data/orderbook_depth.http b/mm2src/komodefi_cli/src/tests/http_mock_data/orderbook_depth.http new file mode 100644 index 0000000000..144f097b50 --- /dev/null +++ b/mm2src/komodefi_cli/src/tests/http_mock_data/orderbook_depth.http @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +access-control-allow-origin: http://localhost:3000 +content-length: 168 +date: Wed, 07 Jun 2023 12:37:05 GMT + +{"result":[{"pair":["BTC","KMD"],"depth":{"asks":1,"bids":5}},{"pair":["BTC","ETH"],"depth":{"asks":1,"bids":0}},{"pair":["RICK","MORTY"],"depth":{"asks":5,"bids":5}}]} \ No newline at end of file diff --git a/mm2src/komodefi_cli/src/tests/http_mock_data/taker_status.http b/mm2src/komodefi_cli/src/tests/http_mock_data/taker_status.http new file mode 100644 index 0000000000..858b0c6861 --- /dev/null +++ b/mm2src/komodefi_cli/src/tests/http_mock_data/taker_status.http @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +access-control-allow-origin: http://localhost:3000 +content-length: 1664 +date: Thu, 11 May 2023 19:28:50 GMT + +{"type":"Taker","order":{"created_at":1683833326624,"request":{"base":"MORTY","rel":"RICK","base_amount":"0.01","base_amount_rat":[[1,[1]],[1,[100]]],"rel_amount":"0.01000001","rel_amount_rat":[[1,[1000001]],[1,[100000000]]],"action":"Buy","uuid":"1ae94a08-47e3-4938-bebb-5df8ff74b8e0","method":"request","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","match_by":{"type":"Any"},"conf_settings":{"base_confs":111,"base_nota":true,"rel_confs":555,"rel_nota":true}},"matches":{"600f62b3-5248-4905-9618-14f339cc7d30":{"reserved":{"base":"MORTY","rel":"RICK","base_amount":"0.01","base_amount_rat":[[1,[1]],[1,[100]]],"rel_amount":"0.0099999999","rel_amount_rat":[[1,[99999999]],[1,[1410065408,2]]],"taker_order_uuid":"1ae94a08-47e3-4938-bebb-5df8ff74b8e0","maker_order_uuid":"600f62b3-5248-4905-9618-14f339cc7d30","sender_pubkey":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5","dest_pub_key":"0000000000000000000000000000000000000000000000000000000000000000","conf_settings":{"base_confs":1,"base_nota":false,"rel_confs":1,"rel_nota":false},"method":"reserved"},"connect":{"taker_order_uuid":"1ae94a08-47e3-4938-bebb-5df8ff74b8e0","maker_order_uuid":"600f62b3-5248-4905-9618-14f339cc7d30","method":"connect","sender_pubkey":"264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d","dest_pub_key":"7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5"},"connected":null,"last_updated":0}},"order_type":{"type":"GoodTillCancelled"},"cancellable":false,"base_orderbook_ticker":null,"rel_orderbook_ticker":null}} \ No newline at end of file diff --git a/mm2src/adex_cli/src/tests/http_mock_data/version.http b/mm2src/komodefi_cli/src/tests/http_mock_data/version.http similarity index 100% rename from mm2src/adex_cli/src/tests/http_mock_data/version.http rename to mm2src/komodefi_cli/src/tests/http_mock_data/version.http diff --git a/mm2src/komodefi_cli/src/tests/mod.rs b/mm2src/komodefi_cli/src/tests/mod.rs new file mode 100644 index 0000000000..86a2c35ad4 --- /dev/null +++ b/mm2src/komodefi_cli/src/tests/mod.rs @@ -0,0 +1,581 @@ +use std::io::Write; +use std::time::Duration; +use tokio::io::AsyncWriteExt; +use tokio::net::{TcpListener, TcpStream}; + +use crate::activation_scheme_db::{get_activation_scheme, get_activation_scheme_path, init_activation_scheme}; +use crate::cli::Cli; +use crate::config::KomodefiConfigImpl; +use crate::komodefi_proc::ResponseHandlerImpl; +use crate::rpc_data::activation::{ActivationMethod, ActivationMethodLegacy}; + +const FAKE_SERVER_COOLDOWN_TIMEOUT_MS: u64 = 10; +const FAKE_SERVER_WARMUP_TIMEOUT_MS: u64 = 100; + +#[tokio::test] +async fn test_get_version() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/version.http"); + let command = &["komodefi-cli", "mm2", "version"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7784).await; + assert_eq!(VERSION_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_get_orderbook() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/orderbook.http"); + let command = &["komodefi-cli", "orders", "book", "RICK", "MORTY"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7785).await; + assert_eq!(RICK_AND_MORTY_ORDERBOOK, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_get_orderbook_with_uuids() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/orderbook.http"); + let command = &["komodefi-cli", "orders", "book", "RICK", "MORTY", "--uuids"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7786).await; + assert_eq!(RICK_AND_MORTY_ORDERBOOK_WITH_UUIDS, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_get_orderbook_with_publics() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/orderbook.http"); + let command = &["komodefi-cli", "orders", "book", "RICK", "MORTY", "--publics"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7787).await; + assert_eq!(ORDERBOOK_WITH_PUBLICS, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_get_enabled() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/get_enabled.http"); + let command = &["komodefi-cli", "coin", "get-enabled"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7788).await; + assert_eq!(ENABLED_COINS, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_get_balance() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/balance.http"); + let command = &["komodefi-cli", "wallet", "balance", "RICK"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7789).await; + assert_eq!(RICK_BALANCE, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_enable() { + test_activation_scheme().await; + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/enable.http"); + let command = &["komodefi-cli", "coin", "enable", "ETH"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7790).await; + assert_eq!(ENABLE_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +async fn test_activation_scheme() { + let _ = std::fs::remove_file(get_activation_scheme_path().unwrap()); + init_activation_scheme().await.unwrap(); + let scheme = get_activation_scheme().unwrap(); + let kmd_scheme = scheme.get_activation_method("KMD"); + let Ok(ActivationMethod::Legacy(ActivationMethodLegacy::Electrum(electrum))) = kmd_scheme else { + panic!("Failed to get electrum scheme") + }; + assert_ne!(electrum.servers.len(), 0); +} + +#[tokio::test] +async fn test_buy_morty_for_rick() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/buy.http"); + let command = &["komodefi-cli", "buy", "MORTY", "RICK", "0.01", "0.5"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7791).await; + assert_eq!( + "4685e133-dfb3-4b31-8d4c-0ffa79933c8e\n", + String::from_utf8(buffer).unwrap() + ); +} + +#[tokio::test] +async fn test_order_status() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/taker_status.http"); + let command = &[ + "komodefi-cli", + "order", + "status", + "b7611502-eae8-4855-8bd7-16d992f952bf", + ]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7792).await; + assert_eq!(TAKER_STATUS_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_my_orders() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/my_orders.http"); + let command = &["komodefi-cli", "orders", "mine"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7793).await; + assert_eq!(MY_ORDERS_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_best_orders() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/best_orders.http"); + let command = &["komodefi-cli", "orders", "best", "--number", "2", "RICK", "buy"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7794).await; + assert_eq!(BEST_ORDERS_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_orderbook_depth() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/orderbook_depth.http"); + let command = &["komodefi-cli", "orders", "depth", "RICK/MORTY", "BTC/KMD", "BTC/ETH"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7795).await; + assert_eq!(ORDERBOOK_DEPTH_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_history_common() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/history-common.http"); + let command = &["komodefi-cli", "orders", "history", "--all"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7796).await; + assert_eq!(HISTORY_COMMON_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_history_takers_detailed() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/history-takers-detailed.http"); + let command = &["komodefi-cli", "orders", "history", "--takers"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7797).await; + assert_eq!(HISTORY_TAKERS_DETAILED_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_history_makers_detailed() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/history-makers-detailed.http"); + let command = &["komodefi-cli", "orders", "history", "--makers"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7798).await; + assert_eq!(HISTORY_MAKERS_DETAILED_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +#[tokio::test] +async fn test_active_swaps() { + let mut buffer: Vec = vec![]; + let mock_resp = include_bytes!("http_mock_data/active_swaps.http"); + let command = &["komodefi-cli", "swaps", "active", "--include-status"]; + start_server_and_do_command(command, &mut buffer, mock_resp, 7799).await; + assert_eq!(ACTIVE_SWAPS_OUTPUT, String::from_utf8(buffer).unwrap()); +} + +async fn fake_mm2_server(port: u16, predefined_response: &'static [u8]) { + let server = TcpListener::bind(("0.0.0.0", port)) + .await + .expect("Failed to bind tcp server"); + + if let Ok((stream, _)) = server.accept().await { + tokio::spawn(handle_connection(stream, predefined_response)); + } +} + +async fn handle_connection(mut stream: TcpStream, predefined_response: &'static [u8]) { + let (reader, mut writer) = stream.split(); + reader.readable().await.unwrap(); + writer.write_all(predefined_response).await.unwrap(); + tokio::time::sleep(Duration::from_millis(FAKE_SERVER_COOLDOWN_TIMEOUT_MS)).await; +} + +async fn start_server_and_do_command(args: &[&str], buf: &mut dyn Write, mock_resp: &'static [u8], port: u16) { + tokio::spawn(fake_mm2_server(port, mock_resp)); + tokio::time::sleep(Duration::from_millis(FAKE_SERVER_WARMUP_TIMEOUT_MS)).await; + + let response_handler = ResponseHandlerImpl { writer: buf.into() }; + let config = KomodefiConfigImpl::new("dummy", format!("http://127.0.0.1:{port}").as_str()); + Cli::execute(args.iter().map(|arg| arg.to_string()), &config, &response_handler) + .await + .unwrap(); +} + +const VERSION_OUTPUT: &str = "\ +Version: 1.0.1-beta_824ca36f3 +Datetime: 2023-04-06T22:35:43+05:00 +"; + +const RICK_AND_MORTY_ORDERBOOK: &str = r" Volume: RICK Price: MORTY + 0.23 1.00000000 + 340654.03 1.00000000 + 2.00 0.99999999 + 2.00 0.99999999 + 2.00 0.99999999 +- --------------- ---------------- + 0.96 1.02438024 + 1.99 1.00000001 + 1.99 1.00000001 + 1.99 1.00000001 + 32229.14 1.00000000 + 0.22 1.00000000 +"; + +const RICK_AND_MORTY_ORDERBOOK_WITH_UUIDS: &str = r" Volume: RICK Price: MORTY Uuid + 0.23 1.00000000 c7585a1b-6060-4319-9da6-c67321628a06 + 340654.03 1.00000000 d69fe2a9-51ca-4d69-96ad-b141a01d8bb4 + 2.00 0.99999999 a2337218-7f6f-46a1-892e-6febfb7f5403 + 2.00 0.99999999 c172c295-7fe3-4131-9c81-c3a7182f0617 + 2.00 0.99999999 fbbc44d2-fb50-4b4b-8ac3-d9857cae16b6 +- --------------- ---------------- ------------------------------------ + 0.96 1.02438024 c480675b-3352-4159-9b3c-55cb2b1329de + 1.99 1.00000001 fdb0de9c-e283-48c3-9de6-8117fecf0aff + 1.99 1.00000001 6a3bb75d-8e91-4192-bf50-d8190a69600d + 1.99 1.00000001 b24b40de-e93d-4218-8d93-1940ceadce7f + 32229.14 1.00000000 652a7e97-f42c-4f87-bc26-26bd1a0fea24 + 0.22 1.00000000 1082c93c-8c23-4944-b8f1-a92ec703b03a +"; + +const ORDERBOOK_WITH_PUBLICS: &str = r" Volume: RICK Price: MORTY Public + 0.23 1.00000000 022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846 + 340654.03 1.00000000 0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732 + 2.00 0.99999999 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 + 2.00 0.99999999 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 + 2.00 0.99999999 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 +- --------------- ---------------- ------------------------------------------------------------------ + 0.96 1.02438024 02d6c3e22a419a4034272acb215f1d39cd6a0413cfd83ac0c68f482db80accd89a + 1.99 1.00000001 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 + 1.99 1.00000001 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 + 1.99 1.00000001 037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 + 32229.14 1.00000000 0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732 + 0.22 1.00000000 022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846 +"; + +const ENABLED_COINS: &str = "\ +Ticker Address +MORTY RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM +RICK RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM +KMD RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM +ETH 0x224050fb7EB13Fa0D342F5b245f1237bAB531650 +"; + +const RICK_BALANCE: &str = "\ +coin: RICK +balance: 0.5767226 +unspendable: 0 +address: RPFGrvJWjSYN4qYvcXsECW1HoHbvQjowZM +"; + +const ENABLE_OUTPUT: &str = "\ +coin: ETH +address: 0x224050fb7EB13Fa0D342F5b245f1237bAB531650 +balance: 0.02 +unspendable_balance: 0 +required_confirmations: 3 +requires_notarization: No +"; + +const TAKER_STATUS_OUTPUT: &str = r" uuid: 1ae94a08-47e3-4938-bebb-5df8ff74b8e0 + req.(base,rel): MORTY(0.0100), RICK(0.01) + req.action: Buy + req.(sender, dest): 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d, 0000000000000000000000000000000000000000000000000000000000000000 + req.match_by: Any + req.conf_settings: 111,true:555,true + created_at: 23-05-11 19:28:46 + order_type: GoodTillCancelled + cancellable: false + matches: + uuid: 600f62b3-5248-4905-9618-14f339cc7d30 + reserved.(base,rel): MORTY(0.0100), RICK(0.0099) + reserved.(taker, maker): 1ae94a08-47e3-4938-bebb-5df8ff74b8e0,600f62b3-5248-4905-9618-14f339cc7d30 + reserved.(sender, dest): 7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5,0000000000000000000000000000000000000000000000000000000000000000 + reserved.conf_settings: 1,false:1,false + last_updated: none + connect.(taker,maker): 1ae94a08-47e3-4938-bebb-5df8ff74b8e0,600f62b3-5248-4905-9618-14f339cc7d30 + connect.(sender, dest): 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d,7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 +"; + +const MY_ORDERS_OUTPUT: &str = " Taker orders: +│ action │ uuid, sender, dest │ type,created_at │ match_by │ base,rel │ cancellable │ +│ base(vol),rel(vol) │ │ confirmation │ │ orderbook ticker │ │ +│ Buy │ 2739152a-3f87-4f6d-a199-3659aa1e864f │ GoodTillCancelled │ Any │ none │ true │ +│ MORTY(0.10),RICK(0.09) │ 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ 23-05-29 12:18:52 │ │ none │ │ +│ │ 0000000000000000000000000000000000000000000000000000000000000000 │ 1,false:1,false │ │ │ │ +│ Buy │ ce90f89f-8074-4e9f-8649-7f7689c56fa9 │ GoodTillCancelled │ Any │ none │ false │ +│ MORTY(0.10),RICK(0.11) │ 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ 23-05-29 12:19:10 │ │ none │ │ +│ │ 0000000000000000000000000000000000000000000000000000000000000000 │ 1,false:1,false │ │ │ │ +│ matches │ +│ uuid: 09a0e11e-837e-4763-bc1f-1659573df9dd │ +│ reserved.(base,rel): MORTY(0.10), RICK(0.09) │ +│ reserved.(taker, maker): ce90f89f-8074-4e9f-8649-7f7689c56fa9,09a0e11e-837e-4763-bc1f-1659573df9dd │ +│ reserved.(sender, dest): 7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5,0000000000000000000000000000000000000000000000000000000000000000 │ +│ reserved.conf_settings: 1,false:1,false │ +│ last_updated: none │ +│ connect.(taker,maker): ce90f89f-8074-4e9f-8649-7f7689c56fa9,09a0e11e-837e-4763-bc1f-1659573df9dd │ +│ connect.(sender, dest): 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d,7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 │ +│ │ + + Maker orders: +│ base,rel │ price │ uuid │ created at, │ min base vol, │ cancellable │ available │ swaps │ conf_settings │ history changes │ +│ │ │ │ updated at │ max base vol │ │ amount │ │ │ │ +│ RICK,MORTY │ 1.11 │ 28315c31-4fd7-4847-9873-352924252fbe │ 23-05-29 12:17:46, │ 0.000100, │ true │ 0.09 │ empty │ 1,false:1,false │ none │ +│ │ │ │ 23-05-29 12:17:46 │ 0.09 │ │ │ │ │ │ +│ RICK,MORTY │ 1.11 │ 7f097435-f482-415b-9bdf-6780f4be4828 │ 23-05-29 12:17:49, │ 0.000100, │ true │ 0.09 │ empty │ 1,false:1,false │ none │ +│ │ │ │ 23-05-29 12:17:49 │ 0.09 │ │ │ │ │ │ + +"; + +const BEST_ORDERS_OUTPUT:&str = "\ +│ │ Price │ Uuid │ Base vol(min:max) │ Rel vol(min:max) │ Address │ Confirmation │ +│ KMD │ +│ │ 0.0050 │ 7c643319-52ea-4323-b0d2-1c448cfc007d │ 0.02:9730.65 │ 0.00010:48.65 │ REbPB4qfrB2D5KAnJJK1RTC1CLGa8hVEcM │ 1,false:2,true │ +│ MORTY │ +│ │ 1.00 │ 2af2d0f3-35e8-4098-8362-99ec9867b9ac │ 0.000100:363783.58 │ 0.000100:363783.58 │ RB8yufv3YTfdzYnwz5paNnnDynGJG6WsqD │ 1,false:1,false │ +│ │ 0.99 │ e52246a2-f9b2-4145-9aa6-53b96bfabe9f │ 0.00010:2.00 │ 0.000100:1.99 │ RMaprYNUp8ErJ9ZAKcxMfpC4ioVycYCCCc │ 1,false:1,false │ +│ ZOMBIE │ +│ │ 1.00 │ 2536e0d8-0a8b-4393-913b-d74543733e5e │ 0.000100:0.23 │ 0.000100:0.23 │ Shielded │ 1,false:1,false │ +"; + +const ORDERBOOK_DEPTH_OUTPUT: &str = " Bids Asks + BTC/KMD: 5 1 + BTC/ETH: 0 1 + RICK/MORTY: 5 5 +"; + +const HISTORY_COMMON_OUTPUT: &str = "\ +Orders history: +│uuid │Type │Action│Base│Rel │Volume │Price │Status │Created │Updated │Was taker│ +│ │ │ │ │ │ │ │ │ │ │ │ +│010a224e-a946-4726-bf6d-e521701053a2│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:41:33│23-06-07 10:37:47│false │ +│ffc41a51-e110-43a0-bb60-203feceba50f│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:41:17│23-06-06 15:41:33│false │ +│869cd8d1-914d-4756-a863-6f73e004c31c│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:38:36│23-06-06 15:41:33│false │ +│3af195fe-f202-428d-8849-6c0b7754e894│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:38:30│23-06-06 15:38:36│false │ +│73271a03-aab3-4789-83d9-9e9c3c808a96│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:38:04│23-06-06 15:38:30│false │ +│e3be3027-333a-4867-928d-61e8442db466│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:37:49│23-06-06 15:38:04│false │ +│a7a04dc8-c361-4cae-80e9-b0e883aa3ae1│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:36:49│23-06-06 15:37:49│false │ +│ecc708e0-df8f-4d3f-95c7-73927ec92acc│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:35:47│23-06-06 15:36:48│false │ +│e1797608-5b7d-45c4-80ae-b66da2e72209│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:35:16│23-06-06 15:35:47│false │ +│f164e567-9e41-4faf-8754-3f87edd5b6d7│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:32:32│23-06-06 15:35:16│false │ +│707c8428-779c-4e78-bcbd-97a7e403c14a│Maker│Sell │RICK│MORTY│8.16 │1.23 │Cancelled│23-06-06 15:31:54│23-06-06 15:32:32│false │ +│b0992fe8-c019-4c86-9d07-03055eaa86ab│Maker│Sell │RICK│MORTY│2.00 │1.50 │Cancelled│23-06-06 15:23:34│23-06-06 15:31:54│false │ +│85d6fc7c-5614-492a-9e85-4c19fab65949│Maker│Sell │RICK│MORTY│2.00 │1.50 │Cancelled│23-06-06 15:23:07│23-06-06 15:23:20│false │ +│5968ffcf-5b25-40c8-8bd7-c7cf9d3154f9│Maker│Sell │RICK│MORTY│8.16 │1.50 │Cancelled│23-06-06 15:22:51│23-06-06 15:23:07│false │ +│eab52e14-1460-4ece-943d-7a2950a22600│Maker│Sell │RICK│MORTY│2.00 │1.50 │Cancelled│23-06-06 15:21:31│23-06-06 15:21:59│false │ +│4318bf91-8416-417d-ac30-7745f30df687│Maker│Sell │RICK│MORTY│2.00 │1000.00│Cancelled│23-06-06 15:21:17│23-06-06 15:21:31│false │ +│a2f6930d-b97d-4c8c-9330-54912fd3b0b9│Maker│Sell │RICK│MORTY│8.16 │1000.00│Cancelled│23-06-06 15:20:55│23-06-06 15:21:10│false │ +│d68a81fd-7a90-4785-ad83-d3b06e362f6f│Maker│Sell │RICK│MORTY│0.00100│1000.00│Cancelled│23-06-06 15:18:05│23-06-06 15:20:55│false │ +│4c0ca34a-487c-43ef-b1f5-13eb4e1a8ece│Maker│Sell │RICK│MORTY│0.00100│1.10 │Cancelled│23-06-06 15:17:45│23-06-06 15:18:05│false │ +│cba44f7f-5d52-492e-a3f0-44ee006296da│Maker│Sell │RICK│MORTY│1.50 │1.10 │Cancelled│23-06-06 15:13:57│23-06-06 15:17:45│false │ +│02db133a-5e69-4056-9855-98d961927fdd│Maker│Sell │RICK│MORTY│1.50 │1.10 │Cancelled│23-06-06 15:09:17│23-06-06 15:13:57│false │ +│6476641f-9014-496c-a608-1bdf81cfbf2e│Maker│Sell │RICK│MORTY│8.16 │1.10 │Cancelled│23-06-06 15:08:58│23-06-06 15:09:17│false │ +│5a253d33-7c7c-40f5-977f-7805013e63b4│Maker│Sell │RICK│MORTY│8.16 │1.10 │Cancelled│23-06-06 15:06:17│23-06-06 15:06:28│false │ +│064bf73f-2a2a-4ca0-b83f-344ec16c5f29│Maker│Sell │RICK│MORTY│8.16 │1.20 │Cancelled│23-06-06 15:04:52│23-06-06 15:06:17│false │ +│475309b5-d6e1-40b2-a2d4-5307aa999d74│Maker│Sell │RICK│MORTY│1.33 │1.20 │Cancelled│23-06-06 15:04:33│23-06-06 15:04:52│false │ +│916bbc09-6b57-4ded-93b0-5a8461be0e99│Maker│Sell │RICK│MORTY│0.50 │1.20 │Cancelled│23-06-06 14:53:06│23-06-06 15:03:07│false │ +│fa256795-9ff3-4983-85d6-8a3fe4fb6f3a│Maker│Sell │RICK│MORTY│8.16 │1.20 │Cancelled│23-06-06 14:52:20│23-06-06 14:52:59│false │ +│23d2c04b-6fa5-4e76-bde9-4a8fe0b7a144│Maker│Sell │RICK│MORTY│8.16 │1.10 │Cancelled│23-06-06 14:51:40│23-06-06 14:52:20│false │ +│4e365431-4db0-4365-a67d-1e39820090a2│Taker│Buy │RICK│MORTY│0.05 │1.10 │TimedOut │23-05-05 14:35:31│23-05-05 14:36:02│false │ +│601bfc00-9033-45d8-86b2-3dbd54881212│Taker│Buy │RICK│MORTY│0.05 │1.10 │Fulfilled│23-05-05 14:34:55│23-05-05 14:34:58│false │ +"; + +const HISTORY_TAKERS_DETAILED_OUTPUT: &str = "\ +Taker orders history detailed: +│ action │ uuid, sender, dest │ type,created_at │ match_by │ base,rel │ cancellable │ +│ base(vol),rel(vol) │ │ confirmation │ │ orderbook ticker │ │ +│ │ │ │ │ │ │ +│ Buy │ 4e365431-4db0-4365-a67d-1e39820090a2 │ GoodTillCancelled │ Any │ none │ false │ +│ RICK(0.05),MORTY(0.05) │ 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ 23-05-05 14:35:31 │ │ none │ │ +│ │ 0000000000000000000000000000000000000000000000000000000000000000 │ 1,false:1,false │ │ │ │ +│ matches │ +│ uuid: efbcb9d6-2d9d-4fa0-af82-919c7da46967 │ +│ reserved.(base,rel): RICK(0.05), MORTY(0.04) │ +│ reserved.(taker, maker): 4e365431-4db0-4365-a67d-1e39820090a2,efbcb9d6-2d9d-4fa0-af82-919c7da46967 │ +│ reserved.(sender, dest): 7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5,0000000000000000000000000000000000000000000000000000000000000000 │ +│ reserved.conf_settings: 0,false:0,false │ +│ last_updated: 23-05-05 14:35:34 │ +│ connect.(taker,maker): 4e365431-4db0-4365-a67d-1e39820090a2,efbcb9d6-2d9d-4fa0-af82-919c7da46967 │ +│ connect.(sender, dest): 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d,7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 │ +│ │ +│ Buy │ 601bfc00-9033-45d8-86b2-3dbd54881212 │ GoodTillCancelled │ Any │ none │ false │ +│ RICK(0.05),MORTY(0.05) │ 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ 23-05-05 14:34:55 │ │ none │ │ +│ │ 0000000000000000000000000000000000000000000000000000000000000000 │ 1,false:1,false │ │ │ │ +│ matches │ +│ uuid: e16ee590-0562-4fbe-88cd-3cfd6e580615 │ +│ reserved.(base,rel): RICK(0.05), MORTY(0.04) │ +│ reserved.(taker, maker): 601bfc00-9033-45d8-86b2-3dbd54881212,e16ee590-0562-4fbe-88cd-3cfd6e580615 │ +│ reserved.(sender, dest): 7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5,0000000000000000000000000000000000000000000000000000000000000000 │ +│ reserved.conf_settings: 0,false:0,false │ +│ last_updated: 23-05-05 14:34:58 │ +│ connect.(taker,maker): 601bfc00-9033-45d8-86b2-3dbd54881212,e16ee590-0562-4fbe-88cd-3cfd6e580615 │ +│ connect.(sender, dest): 264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d,7310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5 │ +│ │ +"; + +const HISTORY_MAKERS_DETAILED_OUTPUT: &str = "\ +Maker orders history detailed: +│ base,rel │ price │ uuid │ created at, │ min base vol, │ swaps │ conf_settings │ history changes │ orderbook ticker │ +│ │ │ │ updated at │ max base vol │ │ │ │ base, rel │ +│ │ │ │ │ │ │ │ │ │ +│ RICK,MORTY │ 1.23 │ 010a224e-a946-4726-bf6d-e521701053a2 │ 23-06-06 15:41:33, │ 1.00, │ empty │ 3,true:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:41:33 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ ffc41a51-e110-43a0-bb60-203feceba50f │ 23-06-06 15:41:17, │ 1.00, │ empty │ 3,true:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:41:17 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ 869cd8d1-914d-4756-a863-6f73e004c31c │ 23-06-06 15:38:36, │ 1.00, │ empty │ 3,true:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:38:36 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ 3af195fe-f202-428d-8849-6c0b7754e894 │ 23-06-06 15:38:30, │ 1.00, │ empty │ 3,true:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:38:30 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ 73271a03-aab3-4789-83d9-9e9c3c808a96 │ 23-06-06 15:38:04, │ 1.00, │ empty │ 3,true:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:38:04 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ e3be3027-333a-4867-928d-61e8442db466 │ 23-06-06 15:37:49, │ 1.00, │ empty │ 3,true:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:37:49 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ a7a04dc8-c361-4cae-80e9-b0e883aa3ae1 │ 23-06-06 15:36:49, │ 1.00, │ empty │ 3,true:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:36:49 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ ecc708e0-df8f-4d3f-95c7-73927ec92acc │ 23-06-06 15:35:47, │ 1.00, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:35:47 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ e1797608-5b7d-45c4-80ae-b66da2e72209 │ 23-06-06 15:35:16, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:35:16 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ f164e567-9e41-4faf-8754-3f87edd5b6d7 │ 23-06-06 15:32:32, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:32:32 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.23 │ 707c8428-779c-4e78-bcbd-97a7e403c14a │ 23-06-06 15:31:54, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:31:54 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.50 │ b0992fe8-c019-4c86-9d07-03055eaa86ab │ 23-06-06 15:23:34, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:23:34 │ 2.00 │ │ │ │ none │ +│ RICK,MORTY │ 1.50 │ 85d6fc7c-5614-492a-9e85-4c19fab65949 │ 23-06-06 15:23:07, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:23:07 │ 2.00 │ │ │ │ none │ +│ RICK,MORTY │ 1.50 │ 5968ffcf-5b25-40c8-8bd7-c7cf9d3154f9 │ 23-06-06 15:22:51, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:22:51 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.50 │ eab52e14-1460-4ece-943d-7a2950a22600 │ 23-06-06 15:21:31, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:21:31 │ 2.00 │ │ │ │ none │ +│ RICK,MORTY │ 1000.00 │ 4318bf91-8416-417d-ac30-7745f30df687 │ 23-06-06 15:21:17, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:21:17 │ 2.00 │ │ │ │ none │ +│ RICK,MORTY │ 1000.00 │ a2f6930d-b97d-4c8c-9330-54912fd3b0b9 │ 23-06-06 15:20:55, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:20:55 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1000.00 │ d68a81fd-7a90-4785-ad83-d3b06e362f6f │ 23-06-06 15:18:05, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:18:05 │ 0.00100 │ │ │ │ none │ +│ RICK,MORTY │ 1.10 │ 4c0ca34a-487c-43ef-b1f5-13eb4e1a8ece │ 23-06-06 15:17:45, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:17:45 │ 0.00100 │ │ │ │ none │ +│ RICK,MORTY │ 1.10 │ cba44f7f-5d52-492e-a3f0-44ee006296da │ 23-06-06 15:13:57, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:13:57 │ 1.50 │ │ │ │ none │ +│ RICK,MORTY │ 1.10 │ 02db133a-5e69-4056-9855-98d961927fdd │ 23-06-06 15:09:17, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:09:17 │ 1.50 │ │ │ │ none │ +│ RICK,MORTY │ 1.10 │ 6476641f-9014-496c-a608-1bdf81cfbf2e │ 23-06-06 15:08:58, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:08:58 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.10 │ 5a253d33-7c7c-40f5-977f-7805013e63b4 │ 23-06-06 15:06:17, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:06:17 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.20 │ 064bf73f-2a2a-4ca0-b83f-344ec16c5f29 │ 23-06-06 15:04:52, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:04:52 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.20 │ 475309b5-d6e1-40b2-a2d4-5307aa999d74 │ 23-06-06 15:04:33, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 15:04:33 │ 1.33 │ │ │ │ none │ +│ RICK,MORTY │ 1.20 │ 916bbc09-6b57-4ded-93b0-5a8461be0e99 │ 23-06-06 14:53:06, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 14:53:06 │ 0.50 │ │ │ │ none │ +│ RICK,MORTY │ 1.20 │ fa256795-9ff3-4983-85d6-8a3fe4fb6f3a │ 23-06-06 14:52:20, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 14:52:20 │ 8.16 │ │ │ │ none │ +│ RICK,MORTY │ 1.10 │ 23d2c04b-6fa5-4e76-bde9-4a8fe0b7a144 │ 23-06-06 14:51:40, │ 0.000100, │ empty │ 1,false:1,false │ none │ none │ +│ │ │ │ 23-06-06 14:51:40 │ 8.16 │ │ │ │ none │ +"; + +const ACTIVE_SWAPS_OUTPUT: &str = " +TakerSwap: 6b007706-d6e1-4565-8655-9eeb128d00e2 +my_order_uuid: 6b007706-d6e1-4565-8655-9eeb128d00e2 +gui: adex-cli +mm_version: 1.0.6-beta_dabdaf33b +taker_coin: DOC +taker_amount: 1.00 +maker_coin: MARTY +maker_amount: 1.00 +events: +│ Started │ uuid: 6b007706-d6e1-4565-8655-9eeb128d00e2 │ +│ 23-07-25 12:20:07 │ started_at: 23-07-25 12:20:07 │ +│ │ taker_coin: DOC │ +│ │ maker_coin: MARTY │ +│ │ maker: 2d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846 │ +│ │ my_persistent_pub: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ +│ │ lock_duration: 7800 │ +│ │ maker_amount: 1.00 │ +│ │ taker_amount: 1.00 │ +│ │ maker_payment_confirmations: 1 │ +│ │ maker_payment_requires_nota: false │ +│ │ taker_payment_confirmations: 1 │ +│ │ taker_payment_requires_nota: false │ +│ │ tacker_payment_lock: 23-07-25 14:30:07 │ +│ │ maker_payment_wait: 23-07-25 13:12:07 │ +│ │ maker_coin_start_block: 147860 │ +│ │ taker_coin_start_block: 133421 │ +│ │ fee_to_send_taker_fee: coin: DOC, amount: 0.00001, paid_from_trading_vol: false │ +│ │ taker_payment_trade_fee: coin: DOC, amount: 0.00001, paid_from_trading_vol: false │ +│ │ maker_payment_spend_trade_fee: coin: MARTY, amount: 0.00001, paid_from_trading_vol: true │ +│ │ maker_coin_htlc_pubkey: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ +│ │ taker_coin_htlc_pubkey: 02264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4d │ +│ │ │ +│ Negotiated │ maker_payment_locktime: 23-07-25 16:40:06 │ +│ 23-07-25 12:20:23 │ maker_pubkey: 000000000000000000000000000000000000000000000000000000000000000000 │ +│ │ secret_hash: a5cfc9787066562ba03c7538d024a88fd1a0fe12 │ +│ │ maker_coin_htlc_pubkey: 022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846 │ +│ │ taker_coin_htlc_pubkey: 022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846 │ +│ │ │ +│ TakerFeeSent │ tx_hex: 0400008085202f890108742b73cfadf56dbc93d3fb8b33b54e5301869e2c950b200e67354b56e2d2ef010000006a473044022073605008 │ +│ 23-07-25 12:20:23 │ 9328c8ec984036b4a248ba3a130d58e9601da3358ffef3482d40927002204a8aa7b83560ee22792457465432bf4f098b38f41d0a483f68920eb63b │ +│ │ 487d02012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff02bcf60100000000001976a914ca1e0474 │ +│ │ 5e8ca0c60d8c5881531d51bec470743f88ace0adaf08635600001976a9149934ebeaa56cb597c936a9ed8202d8d97a0a700388ac07bebf64000000 │ +│ │ 000000000000000000000000 │ +│ │ tx_hash: c71f3793e976209674e2b00efb236c0fa8f0b1b552cb6cfe9068c6b731e570fd │ +│ │ │ +│ TakerPaymentInstructionsReceived │ none │ +│ 23-07-25 12:20:24 │ │ +│ │ │ +│ MakerPaymentReceived │ tx_hex: 0400008085202f890754448b50295dedd36f8de60aeaeeb56a5efa9d1a4464185e329c3aae9fd17673020000006a4730440220651b3753 │ +│ 23-07-25 12:20:24 │ 986a47026f36b082d870c3b2f7651684c0ed26637b64bfbc8722059302200ee7478e290327827daff8a2daf836e9446362169a4a8a4958f538c07f │ +│ │ 2093180121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffffe69a594c67460f781debbca0cfc731c29d │ +│ │ ddba613d65cf630acb00db6c93c9c0000000006b483045022100ef75d49925b7465bec5bc367f87fc7726d33aa17f472cd1ab6c13181d686139402 │ +│ │ 20447c529336a478f4b9d89cc1453ca1cc22f34c13c3b69f7440fcc7fe889493880121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09 │ +│ │ e359a3d4c850834846ffffffff990db7e9fa1f052aba359969b64b892cb76ff881ccd38cb75c09129e9065dbb3000000006a473044022000e7b9f1 │ +│ │ 3c99aa71ce1b8559c2a63cec9b808767a744196e9ed0bde0b5e481a40220053e683e1efc9191207f8feb5e42646301cd2b1f2a8f9e7616e29914eb │ +│ │ 9937c10121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffff41b284103365315e6fb79a7aa111295a03 │ +│ │ 9ccc96aa90d888915f9f0eabec549b000000006b483045022100b2b3cdc669c916e88615b5d2dc3c99186fc1369879bf08b681097986b6842b6302 │ +│ │ 2014a5c0b86d9732d4f202eb5a6d8590a37b6de013e17746c5a7be76b7c932f7390121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09 │ +│ │ e359a3d4c850834846ffffffffdd878ff57eb64187cb74390a4b959c4012d11b766c698f706634e3360e48a6f0000000006b483045022100a16683 │ +│ │ 4300118a432b2b4374e8d076be488b0f84109381f0862c050e73873885022050b3ca3475f1e266ab0b84f30f9030b2d4186a9939c305071691502c │ +│ │ de007d8c0121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffff509a8dc208434566c92ccfcc493df990 │ +│ │ da5edd7454c9527dd7df6d7c8aff49a8020000006b483045022100a02adf39ac6ad8e16603e0fc56948f7973223287f0fc0c9665ccd556b135193e │ +│ │ 022065ef4363429e83ae3703f6536a9f5294b178e7f1641bd24af7bbf3d72c0ada700121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a │ +│ │ 09e359a3d4c850834846ffffffffc3fbc1f34fd64e0dcb1db65a18789f2fbb170cc41a874f8788d904a24b3c2c5d020000006a47304402201e8190 │ +│ │ 95555707955dc508afc6db4acc7662c62460efa79a982f921bfd4afcb90220694cb9276b5228d544571cfdca849f6c18a6abf7169b983f9d47e88d │ +│ │ a43cd4b90121022d7424c741213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ffffffff0300e1f5050000000017a914cea7345f │ +│ │ e6ada43ef95fd6cdfd8c339ef7d1c864870000000000000000166a14a5cfc9787066562ba03c7538d024a88fd1a0fe12d08c15d460ba11001976a9 │ +│ │ 14046922483fab8ca76b23e55e9d338605e2dbab6088ac07bebf64000000000000000000000000000000 │ +│ │ tx_hash: 3284af63a9fa0c4080fd5367c3c7c1ab1d00bb12ae47eb8fb0f5e2bd2da4736a │ +│ │ │ +│ MakerPaymentWaitConfirmStarted │ │ +│ 23-07-25 12:20:24 │ │ +│ │ │ +│ MakerPaymentValidatedAndConfirmed │ │ +│ 23-07-25 12:20:40 │ │ +│ │ │ +│ TakerPaymentSent │ tx_hex: 0400008085202f8901fd70e531b7c66890fe6ccb52b5b1f0a80f6c23fb0eb0e274962076e993371fc7010000006a4730440220259ab8ec │ +│ 23-07-25 12:20:40 │ 216b802f32092ef307017f183f4bd8a52bec420363abf7f070d444a8022061fce8a8b562e07b8ab41afd8953521ad7d22ffb0aa5c710f044554d89 │ +│ │ 833bb6012102264fcd9401d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dffffffff0300e1f5050000000017a914b491ff619f │ +│ │ 632ac1b7ef4e11f64404cff0e98adf870000000000000000166a14a5cfc9787066562ba03c7538d024a88fd1a0fe12f8c8b902635600001976a914 │ +│ │ 9934ebeaa56cb597c936a9ed8202d8d97a0a700388ac18bebf64000000000000000000000000000000 │ +│ │ tx_hash: 75cbad92b60fdb6be1fc7e73b6bac9b4b531c4f14d03b5201f8ff26f20ca1e5d │ +│ │ │ +│ TakerPaymentSpent │ tx_hex: 0400008085202f89015d1eca206ff28f1f20b5034df1c431b5b4c9bab6737efce16bdb0fb692adcb7500000000d8483045022100a0ec1d │ +│ 23-07-25 12:21:21 │ 13d15a4f02a18a9adaa3442d8a9b956034c3e45b68bcbada8f877aef3b02206d59dcea375e86d5a014d51728c74a172c22a5b3cdc5dbe8daa70bb4 │ +│ │ b887a5a30120bed41dce1b0681670b3cad9d31c862bb166fcab656e23d4c00eef7dcac38cad4004c6b63046fdcbf64b1752102264fcd9401d797c5 │ +│ │ 0fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dac6782012088a914a5cfc9787066562ba03c7538d024a88fd1a0fe128821022d7424c7 │ +│ │ 41213a2b9b49aebdaa10e84419e642a8db0a09e359a3d4c850834846ac68ffffffff0118ddf505000000001976a914046922483fab8ca76b23e55e │ +│ │ 9d338605e2dbab6088ac6fdcbf64000000000000000000000000000000 │ +│ │ tx_hash: 13de819b027b4ae98e730679b2b716f98bd1154f729303efd89615f152865586 │ +│ │ secret: bed41dce1b0681670b3cad9d31c862bb166fcab656e23d4c00eef7dcac38cad4 │ +│ │ │ +│ MakerPaymentSpent │ tx_hex: 0400008085202f89016a73a42dbde2f5b08feb47ae12bb001dabc1c7c36753fd80400cfaa963af843200000000d74730440220641be55e │ +│ 23-07-25 12:21:21 │ f769d759be59afe213d57eeeedf7d0f57bcf90835c8c3b7642d0e78902202a8f07ce745553107bea98a58cd50edb46782267fbeb4960c28073ad04 │ +│ │ 12cc380120bed41dce1b0681670b3cad9d31c862bb166fcab656e23d4c00eef7dcac38cad4004c6b6304e6fabf64b17521022d7424c741213a2b9b │ +│ │ 49aebdaa10e84419e642a8db0a09e359a3d4c850834846ac6782012088a914a5cfc9787066562ba03c7538d024a88fd1a0fe12882102264fcd9401 │ +│ │ d797c50fe2f1c7d5fe09bbc10f3838c1d8d6f793061fa5f38b2b4dac68ffffffff0118ddf505000000001976a9149934ebeaa56cb597c936a9ed82 │ +│ │ 02d8d97a0a700388ace6fabf64000000000000000000000000000000 │ +│ │ tx_hash: 4f2cc7a83d7012c5d03fa64df188500db4bee51bbb9a6a0a1f06a50ca3409fdc │ +│ │ │ +"; diff --git a/mm2src/adex_cli/src/transport.rs b/mm2src/komodefi_cli/src/transport.rs similarity index 86% rename from mm2src/adex_cli/src/transport.rs rename to mm2src/komodefi_cli/src/transport.rs index 92e28c0655..b42399276b 100644 --- a/mm2src/adex_cli/src/transport.rs +++ b/mm2src/komodefi_cli/src/transport.rs @@ -1,13 +1,13 @@ use anyhow::{anyhow, bail, Result}; use async_trait::async_trait; use http::{HeaderMap, StatusCode}; +use hyper_dangerous::get_hyper_client_dangerous; use serde::{Deserialize, Serialize}; -use common::log::{error, warn}; -use hyper_dangerous::get_hyper_client_dangerous; +use common::log::{debug, error}; use mm2_net::native_http::SlurpHttpClient; -use crate::{error_anyhow, error_bail, warn_bail}; +use crate::{error_anyhow, error_bail}; #[async_trait] pub(super) trait Transport { @@ -58,7 +58,6 @@ impl Response for (StatusCode, HeaderMap, Vec) { ErrT: for<'a> Deserialize<'a>, { let (status, _headers, data) = self; - match status { StatusCode::OK => match serde_json::from_slice::(&data) { Ok(resp_data) => Ok(Ok(resp_data)), @@ -68,16 +67,16 @@ impl Response for (StatusCode, HeaderMap, Vec) { error_bail!("Failed to deserialize response from data: {data:?}, error: {error}") }, }, - StatusCode::INTERNAL_SERVER_ERROR => match serde_json::from_slice::(&data) { - Ok(resp_data) => Ok(Err(resp_data)), - Err(error) => { - let data = String::from_utf8(data) - .map_err(|error| error_anyhow!("Failed to get string from resp data: {error}"))?; - error_bail!("Failed to deserialize response from data: {data:?}, error: {error}") - }, - }, - _ => { - warn_bail!("Bad http status: {status}, data: {data:?}") + code => { + debug!("Remote service answered with the code: {}", code); + match serde_json::from_slice::(&data) { + Ok(resp_data) => Ok(Err(resp_data)), + Err(error) => { + let data = String::from_utf8(data) + .map_err(|error| error_anyhow!("Failed to get string from resp data: {error}"))?; + error_bail!("Failed to deserialize response from data: {data:?}, error: {error}") + }, + } }, } } diff --git a/mm2src/mm2_main/src/database/my_orders.rs b/mm2src/mm2_main/src/database/my_orders.rs index 898d1f1620..090ea6fd96 100644 --- a/mm2src/mm2_main/src/database/my_orders.rs +++ b/mm2src/mm2_main/src/database/my_orders.rs @@ -1,6 +1,6 @@ #![allow(deprecated)] // TODO: remove this once rusqlite is >= 0.29 -use crate::mm2::lp_ordermatch::{FilteringOrder, MakerOrder, MyOrdersFilter, RecentOrdersSelectResult, TakerOrder}; +use crate::mm2::lp_ordermatch::{MakerOrder, RecentOrdersSelectResult, TakerOrder}; /// This module contains code to work with my_orders table in MM2 SQLite DB use common::log::debug; use common::{now_ms, PagingOptions}; @@ -8,7 +8,7 @@ use db_common::sqlite::offset_by_uuid; use db_common::sqlite::rusqlite::{params_from_iter, Connection, Error as SqlError, Result as SqlResult, ToSql}; use db_common::sqlite::sql_builder::SqlBuilder; use mm2_core::mm_ctx::MmArc; -use mm2_rpc::data::legacy::TakerAction; +use mm2_rpc::data::legacy::{FilteringOrder, OrdersHistoryRequest, TakerAction}; use std::convert::TryInto; use std::string::ParseError; use uuid::Uuid; @@ -122,7 +122,7 @@ pub fn update_order_status(ctx: &MmArc, uuid: Uuid, status: String) -> SqlResult } /// Adds where clauses determined by MyOrdersFilter -fn apply_my_orders_filter(builder: &mut SqlBuilder, params: &mut Vec<(&str, String)>, filter: &MyOrdersFilter) { +fn apply_my_orders_filter(builder: &mut SqlBuilder, params: &mut Vec<(&str, String)>, filter: &OrdersHistoryRequest) { if let Some(order_type) = &filter.order_type { builder.and_where("type = :order_type"); params.push((":order_type", order_type.clone())); @@ -204,7 +204,7 @@ impl From for SelectRecentOrdersUuidsErr { pub fn select_orders_by_filter( conn: &Connection, - filter: &MyOrdersFilter, + filter: &OrdersHistoryRequest, paging_options: Option<&PagingOptions>, ) -> SqlResult { let mut query_builder = SqlBuilder::select_from(MY_ORDERS_TABLE); diff --git a/mm2src/mm2_main/src/database/my_swaps.rs b/mm2src/mm2_main/src/database/my_swaps.rs index 55b08f3957..c22918b1ac 100644 --- a/mm2src/mm2_main/src/database/my_swaps.rs +++ b/mm2src/mm2_main/src/database/my_swaps.rs @@ -1,13 +1,14 @@ #![allow(deprecated)] // TODO: remove this once rusqlite is >= 0.29 /// This module contains code to work with my_swaps table in MM2 SQLite DB -use crate::mm2::lp_swap::{MyRecentSwapsUuids, MySwapsFilter, SavedSwap, SavedSwapIo}; +use crate::mm2::lp_swap::{MyRecentSwapsUuids, SavedSwap, SavedSwapIo}; use common::log::debug; use common::PagingOptions; use db_common::sqlite::rusqlite::{Connection, Error as SqlError, Result as SqlResult, ToSql}; use db_common::sqlite::sql_builder::SqlBuilder; use db_common::sqlite::{offset_by_uuid, query_single_row}; use mm2_core::mm_ctx::MmArc; +use mm2_rpc::data::legacy::MySwapsFilter; use std::convert::TryInto; use uuid::{Error as UuidError, Uuid}; diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index a1393bf71e..2e11bf48f9 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -21,7 +21,6 @@ // use async_trait::async_trait; -use best_orders::BestOrdersAction; use blake2::digest::{Update, VariableOutput}; use blake2::Blake2bVar; use coins::utxo::{compressed_pub_key_from_priv_raw, ChecksumType, UtxoAddressFormat}; @@ -45,9 +44,16 @@ use mm2_err_handle::prelude::*; use mm2_libp2p::{decode_signed, encode_and_sign, encode_message, pub_sub_topic, PublicKey, TopicHash, TopicPrefix, TOPIC_SEPARATOR}; use mm2_metrics::mm_gauge; -use mm2_number::{BigDecimal, BigRational, MmNumber, MmNumberMultiRepr}; -use mm2_rpc::data::legacy::{MatchBy, Mm2RpcResult, OrderConfirmationsSettings, OrderType, RpcOrderbookEntry, - SellBuyRequest, SellBuyResponse, TakerAction, TakerRequestForRpc}; +use mm2_number::{BigDecimal, BigRational, MmNumber}; +use mm2_rpc::data::legacy::{CancelAllOrdersRequest, CancelAllOrdersResponse, CancelBy, CancelOrderRequest, + FilteringOrder, HistoricalOrder, MakerConnectedForRpc, MakerMatchForRpc, + MakerOrderForMyOrdersRpc, MakerOrderForRpc, MakerReservedForRpc, MatchBy, Mm2RpcResult, + MyOrdersResponse, OrderConfirmationsSettings, OrderForRpc, OrderStatusRequest, + OrderStatusResponse, OrderType, OrdersHistoryRequest, OrdersHistoryResponse, + RpcOrderbookEntry, SellBuyRequest, SellBuyResponse, SetPriceRequest, Status, TakerAction, + TakerConnectForRpc, TakerMatchForRpc, TakerOrderForRpc, TakerRequestForRpc, + UpdateMakerOrderRequest, UuidParseError}; +use mm2_rpc::data::version2::{BestOrdersAction, OrderbookAddress, RpcOrderbookEntryV2}; use mm2_state_machine::prelude::*; #[cfg(test)] use mocktopus::macros::*; use my_orders_storage::{delete_my_maker_order, delete_my_taker_order, save_maker_order_on_update, @@ -188,12 +194,6 @@ impl From for OrdermatchInitError { fn from(e: AbortedError) -> Self { OrdermatchInitError::Internal(e.to_string()) } } -#[derive(Debug, Serialize, Deserialize)] -pub struct CancelAllOrdersResponse { - cancelled: Vec, - currently_matching: Vec, -} - #[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] pub enum CancelAllOrdersError { @@ -2199,9 +2199,7 @@ impl MakerReserved { fn get_rel_amount(&self) -> &MmNumber { &self.rel_amount } fn price(&self) -> MmNumber { &self.rel_amount / &self.base_amount } -} -impl MakerReserved { fn from_new_proto_and_pubkey(message: new_protocol::MakerReserved, sender_pubkey: H256Json) -> Self { let base_amount = MmNumber::from(message.base_amount); let rel_amount = MmNumber::from(message.rel_amount); @@ -4400,127 +4398,51 @@ impl OrderbookItem { fn get_true() -> bool { true } -#[derive(Deserialize)] -pub struct SetPriceReq { - base: String, - rel: String, - price: MmNumber, - #[serde(default)] - max: bool, - #[serde(default)] - volume: MmNumber, - min_volume: Option, - #[serde(default = "get_true")] - cancel_previous: bool, - base_confs: Option, - base_nota: Option, - rel_confs: Option, - rel_nota: Option, - #[serde(default = "get_true")] - save_in_history: bool, -} - -#[derive(Deserialize)] -pub struct MakerOrderUpdateReq { - uuid: Uuid, - new_price: Option, - max: Option, - volume_delta: Option, - min_volume: Option, - base_confs: Option, - base_nota: Option, - rel_confs: Option, - rel_nota: Option, -} - -#[derive(Debug, Serialize)] -pub struct MakerReservedForRpc<'a> { - base: &'a str, - rel: &'a str, - base_amount: BigDecimal, - base_amount_rat: BigRational, - rel_amount: BigDecimal, - rel_amount_rat: BigRational, - taker_order_uuid: &'a Uuid, - maker_order_uuid: &'a Uuid, - sender_pubkey: &'a H256Json, - dest_pub_key: &'a H256Json, - conf_settings: &'a Option, - method: String, -} - -#[derive(Debug, Serialize)] -pub struct TakerConnectForRpc<'a> { - taker_order_uuid: &'a Uuid, - maker_order_uuid: &'a Uuid, - method: String, - sender_pubkey: &'a H256Json, - dest_pub_key: &'a H256Json, -} - -impl<'a> From<&'a TakerConnect> for TakerConnectForRpc<'a> { +impl<'a> From<&'a TakerConnect> for TakerConnectForRpc { fn from(connect: &'a TakerConnect) -> TakerConnectForRpc { TakerConnectForRpc { - taker_order_uuid: &connect.taker_order_uuid, - maker_order_uuid: &connect.maker_order_uuid, + taker_order_uuid: connect.taker_order_uuid, + maker_order_uuid: connect.maker_order_uuid, method: "connect".to_string(), - sender_pubkey: &connect.sender_pubkey, - dest_pub_key: &connect.dest_pub_key, + sender_pubkey: connect.sender_pubkey, + dest_pub_key: connect.dest_pub_key, } } } -#[derive(Debug, Serialize)] -pub struct MakerConnectedForRpc<'a> { - taker_order_uuid: &'a Uuid, - maker_order_uuid: &'a Uuid, - method: String, - sender_pubkey: &'a H256Json, - dest_pub_key: &'a H256Json, -} - -impl<'a> From<&'a MakerConnected> for MakerConnectedForRpc<'a> { +impl<'a> From<&'a MakerConnected> for MakerConnectedForRpc { fn from(connected: &'a MakerConnected) -> MakerConnectedForRpc { MakerConnectedForRpc { - taker_order_uuid: &connected.taker_order_uuid, - maker_order_uuid: &connected.maker_order_uuid, + taker_order_uuid: connected.taker_order_uuid, + maker_order_uuid: connected.maker_order_uuid, method: "connected".to_string(), - sender_pubkey: &connected.sender_pubkey, - dest_pub_key: &connected.dest_pub_key, + sender_pubkey: connected.sender_pubkey, + dest_pub_key: connected.dest_pub_key, } } } -impl<'a> From<&'a MakerReserved> for MakerReservedForRpc<'a> { - fn from(reserved: &MakerReserved) -> MakerReservedForRpc { +impl<'a> From<&'a MakerReserved> for MakerReservedForRpc { + fn from(reserved: &'a MakerReserved) -> MakerReservedForRpc { MakerReservedForRpc { - base: &reserved.base, - rel: &reserved.rel, + base: reserved.base.clone(), + rel: reserved.rel.clone(), base_amount: reserved.base_amount.to_decimal(), base_amount_rat: reserved.base_amount.to_ratio(), rel_amount: reserved.rel_amount.to_decimal(), rel_amount_rat: reserved.rel_amount.to_ratio(), - taker_order_uuid: &reserved.taker_order_uuid, - maker_order_uuid: &reserved.maker_order_uuid, - sender_pubkey: &reserved.sender_pubkey, - dest_pub_key: &reserved.dest_pub_key, - conf_settings: &reserved.conf_settings, + taker_order_uuid: reserved.taker_order_uuid, + maker_order_uuid: reserved.maker_order_uuid, + sender_pubkey: reserved.sender_pubkey, + dest_pub_key: reserved.dest_pub_key, + conf_settings: reserved.conf_settings.clone(), method: "reserved".to_string(), } } } -#[derive(Debug, Serialize)] -struct MakerMatchForRpc<'a> { - request: TakerRequestForRpc, - reserved: MakerReservedForRpc<'a>, - connect: Option>, - connected: Option>, - last_updated: u64, -} - #[allow(clippy::needless_borrow)] -impl<'a> From<&'a MakerMatch> for MakerMatchForRpc<'a> { +impl<'a> From<&'a MakerMatch> for MakerMatchForRpc { fn from(maker_match: &'a MakerMatch) -> MakerMatchForRpc { MakerMatchForRpc { request: (&maker_match.request).into(), @@ -4532,39 +4454,17 @@ impl<'a> From<&'a MakerMatch> for MakerMatchForRpc<'a> { } } -#[derive(Serialize)] -struct MakerOrderForRpc<'a> { - base: &'a str, - rel: &'a str, - price: BigDecimal, - price_rat: &'a MmNumber, - max_base_vol: BigDecimal, - max_base_vol_rat: &'a MmNumber, - min_base_vol: BigDecimal, - min_base_vol_rat: &'a MmNumber, - created_at: u64, - updated_at: Option, - matches: HashMap>, - started_swaps: &'a [Uuid], - uuid: Uuid, - conf_settings: &'a Option, - #[serde(skip_serializing_if = "Option::is_none")] - changes_history: &'a Option>, - base_orderbook_ticker: &'a Option, - rel_orderbook_ticker: &'a Option, -} - -impl<'a> From<&'a MakerOrder> for MakerOrderForRpc<'a> { - fn from(order: &'a MakerOrder) -> MakerOrderForRpc<'a> { +impl<'a> From<&'a MakerOrder> for MakerOrderForRpc { + fn from(order: &'a MakerOrder) -> MakerOrderForRpc { MakerOrderForRpc { - base: &order.base, - rel: &order.rel, + base: order.base.clone(), + rel: order.rel.clone(), price: order.price.to_decimal(), - price_rat: &order.price, + price_rat: order.price.clone().into(), max_base_vol: order.max_base_vol.to_decimal(), - max_base_vol_rat: &order.max_base_vol, + max_base_vol_rat: order.max_base_vol.clone().into(), min_base_vol: order.min_base_vol.to_decimal(), - min_base_vol_rat: &order.min_base_vol, + min_base_vol_rat: order.min_base_vol.clone().into(), created_at: order.created_at, updated_at: order.updated_at, matches: order @@ -4572,19 +4472,19 @@ impl<'a> From<&'a MakerOrder> for MakerOrderForRpc<'a> { .iter() .map(|(uuid, order_match)| (*uuid, order_match.into())) .collect(), - started_swaps: &order.started_swaps, + started_swaps: order.started_swaps.clone(), uuid: order.uuid, - conf_settings: &order.conf_settings, - changes_history: &order.changes_history, - base_orderbook_ticker: &order.base_orderbook_ticker, - rel_orderbook_ticker: &order.rel_orderbook_ticker, + conf_settings: order.conf_settings.clone(), + changes_history: order.changes_history.clone(), + base_orderbook_ticker: order.base_orderbook_ticker.clone(), + rel_orderbook_ticker: order.rel_orderbook_ticker.clone(), } } } /// Cancels the orders in case of error on different checks /// https://github.com/KomodoPlatform/atomicDEX-API/issues/794 -async fn cancel_orders_on_error(ctx: &MmArc, req: &SetPriceReq, error: E) -> Result { +async fn cancel_orders_on_error(ctx: &MmArc, req: &SetPriceRequest, error: E) -> Result { if req.cancel_previous { let ordermatch_ctx = OrdermatchContext::from_ctx(ctx).unwrap(); cancel_previous_maker_orders(ctx, &ordermatch_ctx, &req.base, &req.rel).await; @@ -4624,7 +4524,7 @@ pub async fn check_balance_update_loop(ctx: MmWeak, ticker: String, balance: Opt } } -pub async fn create_maker_order(ctx: &MmArc, req: SetPriceReq) -> Result { +pub async fn create_maker_order(ctx: &MmArc, req: SetPriceRequest) -> Result { let base_coin: MmCoinEnum = match try_s!(lp_coinfind(ctx, &req.base).await) { Some(coin) => coin, None => return ERR!("Base coin {} is not found", req.base), @@ -4717,7 +4617,7 @@ pub async fn create_maker_order(ctx: &MmArc, req: SetPriceReq) -> Result Result>, String> { - let req: SetPriceReq = try_s!(json::from_value(req)); + let req: SetPriceRequest = try_s!(json::from_value(req)); let maker_order = create_maker_order(&ctx, req).await?; let rpc_result = MakerOrderForRpc::from(&maker_order); let res = try_s!(json::to_vec(&Mm2RpcResult::new(rpc_result))); @@ -4757,7 +4657,7 @@ async fn cancel_previous_maker_orders( } } -pub async fn update_maker_order(ctx: &MmArc, req: MakerOrderUpdateReq) -> Result { +pub async fn update_maker_order(ctx: &MmArc, req: UpdateMakerOrderRequest) -> Result { let ordermatch_ctx = try_s!(OrdermatchContext::from_ctx(ctx)); let order_mutex = { let maker_orders_ctx = ordermatch_ctx.maker_orders_ctx.lock(); @@ -4888,7 +4788,7 @@ pub async fn update_maker_order(ctx: &MmArc, req: MakerOrderUpdateReq) -> Result } pub async fn update_maker_order_rpc(ctx: MmArc, req: Json) -> Result>, String> { - let req: MakerOrderUpdateReq = try_s!(json::from_value(req)); + let req: UpdateMakerOrderRequest = try_s!(json::from_value(req)); let order = try_s!(update_maker_order(&ctx, req).await); let rpc_result = MakerOrderForRpc::from(&order); let res = try_s!(json::to_vec(&Mm2RpcResult::new(rpc_result))); @@ -4905,20 +4805,15 @@ enum OrderMatchResult { NotMatched, } -#[derive(Deserialize)] -struct OrderStatusReq { - uuid: Uuid, -} - #[derive(Serialize)] struct OrderForRpcWithCancellationReason<'a> { #[serde(flatten)] - order: OrderForRpc<'a>, + order: OrderForRpc, cancellation_reason: &'a str, } pub async fn order_status(ctx: MmArc, req: Json) -> Result>, String> { - let req: OrderStatusReq = try_s!(json::from_value(req)); + let req: OrderStatusRequest = try_s!(json::from_value(req)); let ordermatch_ctx = try_s!(OrdermatchContext::from_ctx(&ctx)); let storage = MyOrdersStorage::new(ctx.clone()); @@ -4926,10 +4821,7 @@ pub async fn order_status(ctx: MmArc, req: Json) -> Result>, St let maybe_order_mutex = ordermatch_ctx.maker_orders_ctx.lock().get_order(&req.uuid).cloned(); if let Some(order_mutex) = maybe_order_mutex { let order = order_mutex.lock().await.clone(); - let res = json!({ - "type": "Maker", - "order": MakerOrderForMyOrdersRpc::from(&order), - }); + let res = OrderStatusResponse::Maker(MakerOrderForMyOrdersRpc::from(&order)); return Response::builder() .body(json::to_vec(&res).expect("Serialization failed")) .map_err(|e| ERRL!("{}", e)); @@ -4973,24 +4865,6 @@ pub enum TakerOrderCancellationReason { Cancelled, } -#[derive(Debug, Deserialize)] -pub struct MyOrdersFilter { - pub order_type: Option, - pub initial_action: Option, - pub base: Option, - pub rel: Option, - pub from_price: Option, - pub to_price: Option, - pub from_volume: Option, - pub to_volume: Option, - pub from_timestamp: Option, - pub to_timestamp: Option, - pub was_taker: Option, - pub status: Option, - #[serde(default)] - pub include_details: bool, -} - #[derive(Debug, Clone, Deserialize, Eq, PartialEq, Serialize)] #[serde(tag = "type", content = "order")] pub enum Order { @@ -4998,7 +4872,7 @@ pub enum Order { Taker(TakerOrder), } -impl<'a> From<&'a Order> for OrderForRpc<'a> { +impl<'a> From<&'a Order> for OrderForRpc { fn from(order: &'a Order) -> OrderForRpc { match order { Order::Maker(o) => OrderForRpc::Maker(MakerOrderForRpc::from(o)), @@ -5016,12 +4890,6 @@ impl Order { } } -#[derive(Serialize)] -struct UuidParseError { - uuid: String, - warning: String, -} - #[derive(Debug, Default)] pub struct RecentOrdersSelectResult { /// Orders matching the query @@ -5032,26 +4900,11 @@ pub struct RecentOrdersSelectResult { pub skipped: usize, } -#[derive(Debug, Serialize)] -pub struct FilteringOrder { - pub uuid: String, - pub order_type: String, - pub initial_action: String, - pub base: String, - pub rel: String, - pub price: f64, - pub volume: f64, - pub created_at: i64, - pub last_updated: i64, - pub was_taker: i8, - pub status: String, -} - /// Returns *all* uuids of swaps, which match the selected filter. pub async fn orders_history_by_filter(ctx: MmArc, req: Json) -> Result>, String> { let storage = MyOrdersStorage::new(ctx.clone()); - let filter: MyOrdersFilter = try_s!(json::from_value(req)); + let filter: OrdersHistoryRequest = try_s!(json::from_value(req)); let db_result = try_s!(storage.select_orders_by_filter(&filter, None).await); let mut warnings = vec![]; @@ -5101,24 +4954,17 @@ pub async fn orders_history_by_filter(ctx: MmArc, req: Json) -> Result = rpc_orders.iter().map(OrderForRpc::from).collect(); - let json = json!({ - "result": { - "orders": db_result.orders, - "details": details, - "found_records": db_result.total_count, - "warnings": warnings, - }}); - - let res = try_s!(json::to_vec(&json)); + let response = Mm2RpcResult::new(OrdersHistoryResponse { + orders: db_result.orders, + details, + found_records: db_result.total_count, + warnings, + }); + let res = try_s!(json::to_vec(&response)); Ok(try_s!(Response::builder().body(res))) } -#[derive(Deserialize)] -pub struct CancelOrderReq { - uuid: Uuid, -} - #[derive(Debug, Deserialize, Serialize, SerializeErrorType, Display)] #[serde(tag = "error_type", content = "error_data")] pub enum CancelOrderError { @@ -5135,7 +4981,10 @@ pub struct CancelOrderResponse { result: String, } -pub async fn cancel_order(ctx: MmArc, req: CancelOrderReq) -> Result> { +pub async fn cancel_order( + ctx: MmArc, + req: CancelOrderRequest, +) -> Result> { let ordermatch_ctx = match OrdermatchContext::from_ctx(&ctx) { Ok(x) => x, Err(_) => return MmError::err(CancelOrderError::CannotRetrieveOrderMatchContext), @@ -5182,7 +5031,7 @@ pub async fn cancel_order(ctx: MmArc, req: CancelOrderReq) -> Result Result>, String> { - let req: CancelOrderReq = try_s!(json::from_value(req)); + let req: CancelOrderRequest = try_s!(json::from_value(req)); let ordermatch_ctx = try_s!(OrdermatchContext::from_ctx(&ctx)); let maybe_order_mutex = ordermatch_ctx.maker_orders_ctx.lock().get_order(&req.uuid).cloned(); @@ -5200,12 +5049,8 @@ pub async fn cancel_order_rpc(ctx: MmArc, req: Json) -> Result> .await .ok(); } - let res = json!({ - "result": "success" - }); - return Response::builder() - .body(json::to_vec(&res).expect("Serialization failed")) - .map_err(|e| ERRL!("{}", e)); + let data = json::to_vec(&Mm2RpcResult::new(Status::Success)).map_err(|e| ERRL!("{}", e))?; + return Response::builder().body(data).map_err(|e| ERRL!("{}", e)); } let mut taker_orders = ordermatch_ctx.my_taker_orders.lock().await; @@ -5239,15 +5084,7 @@ pub async fn cancel_order_rpc(ctx: MmArc, req: Json) -> Result> .map_err(|e| ERRL!("{}", e)) } -#[derive(Serialize)] -struct MakerOrderForMyOrdersRpc<'a> { - #[serde(flatten)] - order: MakerOrderForRpc<'a>, - cancellable: bool, - available_amount: BigDecimal, -} - -impl<'a> From<&'a MakerOrder> for MakerOrderForMyOrdersRpc<'a> { +impl<'a> From<&'a MakerOrder> for MakerOrderForMyOrdersRpc { fn from(order: &'a MakerOrder) -> MakerOrderForMyOrdersRpc { MakerOrderForMyOrdersRpc { order: order.into(), @@ -5257,16 +5094,8 @@ impl<'a> From<&'a MakerOrder> for MakerOrderForMyOrdersRpc<'a> { } } -#[derive(Serialize)] -struct TakerMatchForRpc<'a> { - reserved: MakerReservedForRpc<'a>, - connect: TakerConnectForRpc<'a>, - connected: Option>, - last_updated: u64, -} - #[allow(clippy::needless_borrow)] -impl<'a> From<&'a TakerMatch> for TakerMatchForRpc<'a> { +impl<'a> From<&'a TakerMatch> for TakerMatchForRpc { fn from(taker_match: &'a TakerMatch) -> TakerMatchForRpc { TakerMatchForRpc { reserved: (&taker_match.reserved).into(), @@ -5277,19 +5106,8 @@ impl<'a> From<&'a TakerMatch> for TakerMatchForRpc<'a> { } } -#[derive(Serialize)] -struct TakerOrderForRpc<'a> { - created_at: u64, - request: TakerRequestForRpc, - matches: HashMap>, - order_type: &'a OrderType, - cancellable: bool, - base_orderbook_ticker: &'a Option, - rel_orderbook_ticker: &'a Option, -} - #[allow(clippy::needless_borrow)] -impl<'a> From<&'a TakerOrder> for TakerOrderForRpc<'a> { +impl<'a> From<&'a TakerOrder> for TakerOrderForRpc { fn from(order: &'a TakerOrder) -> TakerOrderForRpc { TakerOrderForRpc { created_at: order.created_at, @@ -5300,21 +5118,13 @@ impl<'a> From<&'a TakerOrder> for TakerOrderForRpc<'a> { .map(|(uuid, taker_match)| (*uuid, taker_match.into())) .collect(), cancellable: order.is_cancellable(), - order_type: &order.order_type, - base_orderbook_ticker: &order.base_orderbook_ticker, - rel_orderbook_ticker: &order.rel_orderbook_ticker, + order_type: order.order_type.clone(), + base_orderbook_ticker: order.base_orderbook_ticker.clone(), + rel_orderbook_ticker: order.rel_orderbook_ticker.clone(), } } } -#[allow(clippy::large_enum_variant)] -#[derive(Serialize)] -#[serde(tag = "type", content = "order")] -enum OrderForRpc<'a> { - Maker(MakerOrderForRpc<'a>), - Taker(TakerOrderForRpc<'a>), -} - pub async fn my_orders(ctx: MmArc) -> Result>, String> { let ordermatch_ctx = try_s!(OrdermatchContext::from_ctx(&ctx)); let my_maker_orders = ordermatch_ctx.maker_orders_ctx.lock().orders.clone(); @@ -5325,23 +5135,21 @@ pub async fn my_orders(ctx: MmArc) -> Result>, String> { } let maker_orders_for_rpc: HashMap<_, _> = maker_orders_map .iter() - .map(|(uuid, order)| (uuid, MakerOrderForMyOrdersRpc::from(order))) + .map(|(uuid, order)| (**uuid, MakerOrderForMyOrdersRpc::from(order))) .collect(); let taker_orders = ordermatch_ctx.my_taker_orders.lock().await; let taker_orders_for_rpc: HashMap<_, _> = taker_orders .iter() - .map(|(uuid, order)| (uuid, TakerOrderForRpc::from(order))) + .map(|(uuid, order)| (*uuid, TakerOrderForRpc::from(order))) .collect(); - let res = json!({ - "result": { - "maker_orders": maker_orders_for_rpc, - "taker_orders": taker_orders_for_rpc, - } + + let response = Mm2RpcResult::new(MyOrdersResponse { + maker_orders: maker_orders_for_rpc, + taker_orders: taker_orders_for_rpc, }); - Response::builder() - .body(json::to_vec(&res).expect("Serialization failed")) - .map_err(|e| ERRL!("{}", e)) + let data = json::to_vec(&response).map_err(|e| ERRL!("{}", e))?; + Response::builder().body(data).map_err(|e| ERRL!("{}", e)) } #[cfg(not(target_arch = "wasm32"))] @@ -5368,20 +5176,6 @@ fn my_order_history_file_path(ctx: &MmArc, uuid: &Uuid) -> PathBuf { my_orders_history_dir(ctx).join(format!("{}.json", uuid)) } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct HistoricalOrder { - #[serde(skip_serializing_if = "Option::is_none")] - max_base_vol: Option, - #[serde(skip_serializing_if = "Option::is_none")] - min_base_vol: Option, - #[serde(skip_serializing_if = "Option::is_none")] - price: Option, - #[serde(skip_serializing_if = "Option::is_none")] - updated_at: Option, - #[serde(skip_serializing_if = "Option::is_none")] - conf_settings: Option, -} - pub async fn orders_kick_start(ctx: &MmArc) -> Result, String> { let mut coins = HashSet::new(); let ordermatch_ctx = try_s!(OrdermatchContext::from_ctx(ctx)); @@ -5408,17 +5202,6 @@ pub async fn orders_kick_start(ctx: &MmArc) -> Result, String> { Ok(coins) } -#[derive(Deserialize)] -#[serde(tag = "type", content = "data")] -pub enum CancelBy { - /// All orders of current node - All, - /// All orders of specific pair - Pair { base: String, rel: String }, - /// All orders using the coin ticker as base or rel - Coin { ticker: String }, -} - pub async fn get_matching_orders(ctx: &MmArc, coins: &HashSet) -> Result, String> { let ordermatch_ctx = try_s!(OrdermatchContext::from_ctx(ctx)); let mut matching_orders = vec![]; @@ -5576,19 +5359,15 @@ pub async fn cancel_all_orders( } pub async fn cancel_all_orders_rpc(ctx: MmArc, req: Json) -> Result>, String> { - let cancel_by: CancelBy = try_s!(json::from_value(req["cancel_by"].clone())); + let request: CancelAllOrdersRequest = try_s!(json::from_value(req.clone())); - let (cancelled, currently_matching) = try_s!(cancel_orders_by(&ctx, cancel_by).await); - - let res = json!({ - "result": { - "cancelled": cancelled, - "currently_matching": currently_matching, - } + let (cancelled, currently_matching) = try_s!(cancel_orders_by(&ctx, request.cancel_by).await); + let response = Mm2RpcResult::new(CancelAllOrdersResponse { + cancelled, + currently_matching, }); - Response::builder() - .body(json::to_vec(&res).expect("Serialization failed")) - .map_err(|e| ERRL!("{}", e)) + let data = json::to_vec(&response).map_err(|e| ERRL!("{}", e))?; + Response::builder().body(data).map_err(|e| ERRL!("{}", e)) } /// Subscribe to an orderbook topic (see [`orderbook_topic`]). @@ -5641,21 +5420,6 @@ async fn subscribe_to_orderbook_topic( Ok(()) } -#[derive(Clone, Debug, Serialize)] -pub struct RpcOrderbookEntryV2 { - coin: String, - address: OrderbookAddress, - price: MmNumberMultiRepr, - pubkey: String, - uuid: Uuid, - is_mine: bool, - base_max_volume: MmNumberMultiRepr, - base_min_volume: MmNumberMultiRepr, - rel_max_volume: MmNumberMultiRepr, - rel_min_volume: MmNumberMultiRepr, - conf_settings: Option, -} - fn choose_maker_confs_and_notas( maker_confs: Option, taker_req: &TakerRequest, @@ -5766,13 +5530,6 @@ fn choose_taker_confs_and_notas( } } -#[derive(Clone, Debug, Serialize)] -#[serde(tag = "address_type", content = "address_data")] -pub enum OrderbookAddress { - Transparent(String), - Shielded, -} - #[derive(Debug, Display)] enum OrderbookAddrErr { AddrFromPubkeyError(String), diff --git a/mm2src/mm2_main/src/lp_ordermatch/best_orders.rs b/mm2src/mm2_main/src/lp_ordermatch/best_orders.rs index 7e20ed46bc..82127321b3 100644 --- a/mm2src/mm2_main/src/lp_ordermatch/best_orders.rs +++ b/mm2src/mm2_main/src/lp_ordermatch/best_orders.rs @@ -6,22 +6,16 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigRational, MmNumber}; use mm2_rpc::data::legacy::OrderConfirmationsSettings; +use mm2_rpc::data::version2::{BestOrdersAction, BestOrdersByRequest, BestOrdersRequestV2, BestOrdersV2Response}; use num_traits::Zero; use serde_json::{self as json, Value as Json}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use uuid::Uuid; use super::{addr_format_from_protocol_info, is_my_order, mm2_internal_pubkey_hex, orderbook_address, - BaseRelProtocolInfo, OrderbookP2PItemWithProof, OrdermatchContext, OrdermatchRequest, RpcOrderbookEntryV2}; + BaseRelProtocolInfo, OrderbookP2PItemWithProof, OrdermatchContext, OrdermatchRequest}; use crate::mm2::lp_network::{request_any_relay, P2PRequest}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "lowercase")] -pub enum BestOrdersAction { - Buy, - Sell, -} - #[derive(Debug, Deserialize)] pub struct BestOrdersRequest { coin: String, @@ -38,24 +32,6 @@ struct BestOrdersP2PRes { conf_infos: HashMap, } -#[derive(Debug, Deserialize)] -#[serde(tag = "type", content = "value")] -pub enum RequestBestOrdersBy { - #[serde(rename = "volume")] - Volume(MmNumber), - #[serde(rename = "number")] - Number(usize), -} - -#[derive(Debug, Deserialize)] -pub struct BestOrdersRequestV2 { - coin: String, - action: BestOrdersAction, - request_by: RequestBestOrdersBy, - #[serde(default)] - exclude_mine: bool, -} - pub fn process_best_orders_p2p_request( ctx: MmArc, coin: String, @@ -217,7 +193,7 @@ pub async fn best_orders_rpc(ctx: MmArc, req: Json) -> Result>, } let p2p_request = OrdermatchRequest::BestOrders { coin: ordermatch_ctx.orderbook_ticker_bypass(&req.coin), - action: req.action, + action: req.action.clone(), volume: req.volume.into(), }; @@ -304,12 +280,6 @@ impl HttpStatusCode for BestOrdersRpcError { } } -#[derive(Serialize)] -pub struct BestOrdersV2Response { - orders: HashMap>, - original_tickers: HashMap>, -} - pub async fn best_orders_rpc_v2( ctx: MmArc, req: BestOrdersRequestV2, @@ -319,14 +289,14 @@ pub async fn best_orders_rpc_v2( } let ordermatch_ctx = OrdermatchContext::from_ctx(&ctx).unwrap(); let p2p_request = match req.request_by { - RequestBestOrdersBy::Volume(mm_number) => OrdermatchRequest::BestOrders { + BestOrdersByRequest::Volume(mm_number) => OrdermatchRequest::BestOrders { coin: ordermatch_ctx.orderbook_ticker_bypass(&req.coin), - action: req.action, + action: req.action.clone(), volume: mm_number.into(), }, - RequestBestOrdersBy::Number(size) => OrdermatchRequest::BestOrdersByNumber { + BestOrdersByRequest::Number(size) => OrdermatchRequest::BestOrdersByNumber { coin: ordermatch_ctx.orderbook_ticker_bypass(&req.coin), - action: req.action, + action: req.action.clone(), number: size, }, }; diff --git a/mm2src/mm2_main/src/lp_ordermatch/my_orders_storage.rs b/mm2src/mm2_main/src/lp_ordermatch/my_orders_storage.rs index 38a144952f..38ee172a82 100644 --- a/mm2src/mm2_main/src/lp_ordermatch/my_orders_storage.rs +++ b/mm2src/mm2_main/src/lp_ordermatch/my_orders_storage.rs @@ -1,4 +1,4 @@ -use super::{MakerOrder, MakerOrderCancellationReason, MyOrdersFilter, Order, RecentOrdersSelectResult, TakerOrder, +use super::{MakerOrder, MakerOrderCancellationReason, Order, RecentOrdersSelectResult, TakerOrder, TakerOrderCancellationReason}; use async_trait::async_trait; use common::log::LogOnError; @@ -7,6 +7,7 @@ use derive_more::Display; use futures::{FutureExt, TryFutureExt}; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; +use mm2_rpc::data::legacy::OrdersHistoryRequest; #[cfg(test)] use mocktopus::macros::*; use uuid::Uuid; @@ -178,7 +179,7 @@ pub trait MyOrdersHistory { pub trait MyOrdersFilteringHistory { async fn select_orders_by_filter( &self, - filter: &MyOrdersFilter, + filter: &OrdersHistoryRequest, paging_options: Option<&PagingOptions>, ) -> MyOrdersResult; @@ -312,7 +313,7 @@ mod native_impl { impl MyOrdersFilteringHistory for MyOrdersStorage { async fn select_orders_by_filter( &self, - filter: &MyOrdersFilter, + filter: &OrdersHistoryRequest, paging_options: Option<&PagingOptions>, ) -> MyOrdersResult { select_orders_by_filter(&self.ctx.sqlite_connection(), filter, paging_options) @@ -539,7 +540,7 @@ mod wasm_impl { impl MyOrdersFilteringHistory for MyOrdersStorage { async fn select_orders_by_filter( &self, - _filter: &MyOrdersFilter, + _filter: &OrdersHistoryRequest, _paging_options: Option<&PagingOptions>, ) -> MyOrdersResult { warn!("'select_orders_by_filter' not supported in WASM yet"); diff --git a/mm2src/mm2_main/src/lp_ordermatch/orderbook_depth.rs b/mm2src/mm2_main/src/lp_ordermatch/orderbook_depth.rs index dcc991a361..bfb26d7391 100644 --- a/mm2src/mm2_main/src/lp_ordermatch/orderbook_depth.rs +++ b/mm2src/mm2_main/src/lp_ordermatch/orderbook_depth.rs @@ -1,37 +1,22 @@ -use super::{orderbook_topic_from_base_rel, OrdermatchContext, OrdermatchRequest}; -use crate::mm2::lp_network::{request_any_relay, P2PRequest}; use coins::is_wallet_only_ticker; use common::log; use http::Response; use mm2_core::mm_ctx::MmArc; +use mm2_rpc::data::legacy::{OrderbookDepthRequest, PairDepth, PairWithDepth}; use serde_json::{self as json, Value as Json}; use std::collections::HashMap; -#[derive(Debug, Deserialize)] -struct OrderbookDepthReq { - pairs: Vec<(String, String)>, -} - -#[derive(Copy, Clone, Debug, Deserialize, Serialize)] -struct PairDepth { - asks: usize, - bids: usize, -} +use super::{orderbook_topic_from_base_rel, OrdermatchContext, OrdermatchRequest}; +use crate::mm2::lp_network::{request_any_relay, P2PRequest}; #[derive(Debug, Deserialize, Serialize)] struct OrderbookDepthP2PResponse { depth: HashMap<(String, String), PairDepth>, } -#[derive(Debug, Deserialize, Serialize)] -struct PairWithDepth { - pair: (String, String), - depth: PairDepth, -} - pub async fn orderbook_depth_rpc(ctx: MmArc, req: Json) -> Result>, String> { let ordermatch_ctx = OrdermatchContext::from_ctx(&ctx).unwrap(); - let req: OrderbookDepthReq = try_s!(json::from_value(req)); + let req: OrderbookDepthRequest = try_s!(json::from_value(req)); let wallet_only_pairs: Vec<_> = req .pairs @@ -93,7 +78,7 @@ pub async fn orderbook_depth_rpc(ctx: MmArc, req: Json) -> Result info!("Order with uuid: {} successfully cancelled", uuid), Err(err) => warn!("Couldn't cancel the order with uuid: {} - err: {}", uuid, err), }; @@ -526,7 +526,7 @@ async fn update_single_order( ctx: &MmArc, ) -> OrderProcessingResult { let (min_vol, volume, calculated_price, is_max) = prepare_order(rates, &cfg, &key_trade_pair, ctx).await?; - let req = MakerOrderUpdateReq { + let req = UpdateMakerOrderRequest { uuid, new_price: Some(calculated_price.clone()), max: is_max.into(), @@ -586,7 +586,7 @@ async fn create_single_order( ) -> OrderProcessingResult { let (min_vol, volume, calculated_price, is_max) = prepare_order(rates, &cfg, &key_trade_pair, &ctx).await?; - let req = SetPriceReq { + let req = SetPriceRequest { base: cfg.base.clone(), rel: cfg.rel.clone(), price: calculated_price.clone(), diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index c4b7a405a0..531186b932 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -128,6 +128,7 @@ pub use maker_swap::{calc_max_maker_vol, check_balance_for_maker_swap, get_max_m run_maker_swap, CoinVolumeInfo, MakerSavedEvent, MakerSavedSwap, MakerSwap, MakerSwapStatusChanged, MakerTradePreimage, RunMakerSwapInput, MAKER_PAYMENT_SENT_LOG}; pub use max_maker_vol_rpc::max_maker_vol; +use mm2_rpc::data::legacy::{MySwapsFilter, RecoveredSwapAction}; use my_swaps_storage::{MySwapsOps, MySwapsStorage}; use pubkey_banning::BanReason; pub use pubkey_banning::{ban_pubkey_rpc, is_pubkey_banned, list_banned_pubkeys_rpc, unban_pubkeys_rpc}; @@ -462,12 +463,6 @@ const _SWAP_DEFAULT_MAX_CONFIRMS: u32 = 6; /// MM2 checks that swap payment is confirmed every WAIT_CONFIRM_INTERVAL seconds const WAIT_CONFIRM_INTERVAL_SEC: u64 = 15; -#[derive(Debug, PartialEq, Serialize)] -pub enum RecoveredSwapAction { - RefundedMyPayment, - SpentOtherPayment, -} - #[derive(Debug, PartialEq)] pub struct RecoveredSwap { action: RecoveredSwapAction, @@ -1191,14 +1186,6 @@ async fn broadcast_my_swap_status(ctx: &MmArc, uuid: Uuid) -> Result<(), String> Ok(()) } -#[derive(Debug, Deserialize)] -pub struct MySwapsFilter { - pub my_coin: Option, - pub other_coin: Option, - pub from_timestamp: Option, - pub to_timestamp: Option, -} - // TODO: Should return the result from SQL like in order history. So it can be clear the exact started_at time // and the coins if they are not included in the filter request /// Returns *all* uuids of swaps, which match the selected filter. diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index bc2151ef2d..bf407638f4 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -7,9 +7,9 @@ use super::{broadcast_my_swap_status, broadcast_p2p_tx_msg, broadcast_swap_msg_e check_other_coin_balance_for_swap, detect_secret_hash_algo, dex_fee_amount_from_taker_coin, get_locked_amount, recv_swap_msg, swap_topic, taker_payment_spend_deadline, tx_helper_topic, wait_for_maker_payment_conf_until, AtomicSwap, LockedAmount, MySwapInfo, NegotiationDataMsg, - NegotiationDataV2, NegotiationDataV3, RecoveredSwap, RecoveredSwapAction, SavedSwap, SavedSwapIo, - SavedTradeFee, SecretHashAlgo, SwapConfirmationsSettings, SwapError, SwapMsg, SwapPubkeys, SwapTxDataMsg, - SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL_SEC}; + NegotiationDataV2, NegotiationDataV3, RecoveredSwap, SavedSwap, SavedSwapIo, SavedTradeFee, + SecretHashAlgo, SwapConfirmationsSettings, SwapError, SwapMsg, SwapPubkeys, SwapTxDataMsg, SwapsContext, + TransactionIdentifier, WAIT_CONFIRM_INTERVAL_SEC}; use crate::mm2::lp_dispatcher::{DispatcherContext, LpEvents}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::MakerOrderBuilder; @@ -30,7 +30,7 @@ use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; -use mm2_rpc::data::legacy::OrderConfirmationsSettings; +use mm2_rpc::data::legacy::{OrderConfirmationsSettings, RecoveredSwapAction}; use parking_lot::Mutex as PaMutex; use primitives::hash::{H256, H264}; use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264 as H264Json}; diff --git a/mm2src/mm2_main/src/lp_swap/my_swaps_storage.rs b/mm2src/mm2_main/src/lp_swap/my_swaps_storage.rs index 33640350aa..146f4df436 100644 --- a/mm2src/mm2_main/src/lp_swap/my_swaps_storage.rs +++ b/mm2src/mm2_main/src/lp_swap/my_swaps_storage.rs @@ -1,9 +1,10 @@ -use super::{MyRecentSwapsUuids, MySwapsFilter}; +use super::MyRecentSwapsUuids; use async_trait::async_trait; use common::PagingOptions; use derive_more::Display; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; +use mm2_rpc::data::legacy::MySwapsFilter; use uuid::Uuid; pub type MySwapsResult = Result>; diff --git a/mm2src/mm2_main/src/lp_swap/pubkey_banning.rs b/mm2src/mm2_main/src/lp_swap/pubkey_banning.rs index 5aa8f94103..c169367e1f 100644 --- a/mm2src/mm2_main/src/lp_swap/pubkey_banning.rs +++ b/mm2src/mm2_main/src/lp_swap/pubkey_banning.rs @@ -2,6 +2,7 @@ use super::{SwapEvent, SwapsContext}; use chain::hash::H256; use http::Response; use mm2_core::mm_ctx::MmArc; +use mm2_rpc::data::legacy::{BanPubkeysRequest, UnbanPubkeysRequest}; use rpc::v1::types::H256 as H256Json; use serde_json::{self as json, Value as Json}; use std::collections::hash_map::{Entry, HashMap}; @@ -43,14 +44,8 @@ pub async fn list_banned_pubkeys_rpc(ctx: MmArc) -> Result>, St Ok(try_s!(Response::builder().body(res))) } -#[derive(Deserialize)] -struct BanPubkeysReq { - pubkey: H256Json, - reason: String, -} - pub async fn ban_pubkey_rpc(ctx: MmArc, req: Json) -> Result>, String> { - let req: BanPubkeysReq = try_s!(json::from_value(req)); + let req: BanPubkeysRequest = try_s!(json::from_value(req)); let ctx = try_s!(SwapsContext::from_ctx(&ctx)); let mut banned_pubs = try_s!(ctx.banned_pubkeys.lock()); @@ -66,24 +61,17 @@ pub async fn ban_pubkey_rpc(ctx: MmArc, req: Json) -> Result>, } } -#[derive(Deserialize)] -#[serde(tag = "type", content = "data")] -enum UnbanPubkeysReq { - All, - Few(Vec), -} - pub async fn unban_pubkeys_rpc(ctx: MmArc, req: Json) -> Result>, String> { - let req: UnbanPubkeysReq = try_s!(json::from_value(req["unban_by"].clone())); + let req: UnbanPubkeysRequest = try_s!(json::from_value(req["unban_by"].clone())); let ctx = try_s!(SwapsContext::from_ctx(&ctx)); let mut banned_pubs = try_s!(ctx.banned_pubkeys.lock()); let mut unbanned = HashMap::new(); let mut were_not_banned = vec![]; match req { - UnbanPubkeysReq::All => { + UnbanPubkeysRequest::All => { unbanned = banned_pubs.drain().collect(); }, - UnbanPubkeysReq::Few(pubkeys) => { + UnbanPubkeysRequest::Few(pubkeys) => { for pubkey in pubkeys { match banned_pubs.remove(&pubkey) { Some(removed) => { diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index e4acf1b968..b3053a16b5 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -7,9 +7,9 @@ use super::trade_preimage::{TradePreimageRequest, TradePreimageRpcError, TradePr use super::{broadcast_my_swap_status, broadcast_swap_message, broadcast_swap_msg_every, check_other_coin_balance_for_swap, dex_fee_amount_from_taker_coin, dex_fee_rate, get_locked_amount, recv_swap_msg, swap_topic, wait_for_maker_payment_conf_until, AtomicSwap, LockedAmount, MySwapInfo, - NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, RecoveredSwap, RecoveredSwapAction, SavedSwap, - SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapPubkeys, SwapTxDataMsg, - SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL_SEC}; + NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, RecoveredSwap, SavedSwap, SavedSwapIo, + SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapPubkeys, SwapTxDataMsg, SwapsContext, + TransactionIdentifier, WAIT_CONFIRM_INTERVAL_SEC}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::TakerOrderBuilder; use crate::mm2::lp_swap::swap_v2_common::mark_swap_as_finished; @@ -31,7 +31,7 @@ use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_number::{BigDecimal, MmNumber}; -use mm2_rpc::data::legacy::{MatchBy, OrderConfirmationsSettings, TakerAction}; +use mm2_rpc::data::legacy::{MatchBy, OrderConfirmationsSettings, RecoveredSwapAction, TakerAction}; use parking_lot::Mutex as PaMutex; use primitives::hash::H264; use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264 as H264Json}; diff --git a/mm2src/mm2_main/src/rpc.rs b/mm2src/mm2_main/src/rpc.rs index fc1912e7f1..456cd95ffe 100644 --- a/mm2src/mm2_main/src/rpc.rs +++ b/mm2src/mm2_main/src/rpc.rs @@ -31,7 +31,8 @@ use http::{Method, Request, Response, StatusCode}; use lazy_static::lazy_static; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -use mm2_rpc::mm_protocol::{MmRpcBuilder, MmRpcResponse, MmRpcVersion}; +use mm2_rpc::data::version2::MmRpcVersion; +use mm2_rpc::mm_protocol::{MmRpcBuilder, MmRpcResponse}; use regex::Regex; use serde::Serialize; use serde_json::{self as json, Value as Json}; diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 9af6dc7b4b..f0c38cf7da 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -49,7 +49,8 @@ use futures::Future as Future03; use http::Response; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -use mm2_rpc::mm_protocol::{MmRpcBuilder, MmRpcRequest, MmRpcVersion}; +use mm2_rpc::data::version2::{MmRpcRequest, MmRpcVersion}; +use mm2_rpc::mm_protocol::MmRpcBuilder; use nft::{clear_nft_db, get_nft_list, get_nft_metadata, get_nft_transfers, refresh_nft_metadata, update_nft, withdraw_nft}; use serde::de::DeserializeOwned; @@ -66,7 +67,7 @@ pub async fn process_single_request( client: SocketAddr, local_only: bool, ) -> DispatcherResult>> { - let request: MmRpcRequest = json::from_value(req)?; + let request: MmRpcRequest = json::from_value(req)?; // https://github.com/artemii235/SuperNET/issues/368 let method_name = Some(request.method.as_str()); @@ -97,7 +98,7 @@ pub async fn process_single_request( /// `E` = `WithdrawError` async fn handle_mmrpc( ctx: MmArc, - request: MmRpcRequest, + request: MmRpcRequest, handler: Handler, ) -> DispatcherResult>> where @@ -120,7 +121,7 @@ where Ok(response.serialize_http_response()) } -async fn auth(request: &MmRpcRequest, ctx: &MmArc, client: &SocketAddr) -> DispatcherResult<()> { +async fn auth(request: &MmRpcRequest, ctx: &MmArc, client: &SocketAddr) -> DispatcherResult<()> { if PUBLIC_METHODS.contains(&Some(request.method.as_str())) { return Ok(()); } @@ -136,7 +137,7 @@ async fn auth(request: &MmRpcRequest, ctx: &MmArc, client: &SocketAddr) -> Dispa } } -async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult>> { +async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult>> { if let Some(task_method) = request.method.strip_prefix("task::") { let task_method = task_method.to_string(); return rpc_task_dispatcher(request, ctx, task_method).await; @@ -230,7 +231,7 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult, ctx: MmArc, task_method: String, ) -> DispatcherResult>> { @@ -295,7 +296,7 @@ async fn rpc_task_dispatcher( /// /// `gui_storage_method` is a method name with the `gui_storage::` prefix removed. async fn gui_storage_dispatcher( - request: MmRpcRequest, + request: MmRpcRequest, ctx: MmArc, gui_storage_method: &str, ) -> DispatcherResult>> { @@ -324,7 +325,7 @@ async fn gui_storage_dispatcher( /// `lightning_method` is a method name with the `lightning::` prefix removed. #[cfg(not(target_arch = "wasm32"))] async fn lightning_dispatcher( - request: MmRpcRequest, + request: MmRpcRequest, ctx: MmArc, lightning_method: &str, ) -> DispatcherResult>> { diff --git a/mm2src/mm2_main/src/rpc/lp_commands/lp_commands.rs b/mm2src/mm2_main/src/rpc/lp_commands/lp_commands.rs index ae992c6d3e..e4957e5620 100644 --- a/mm2src/mm2_main/src/rpc/lp_commands/lp_commands.rs +++ b/mm2src/mm2_main/src/rpc/lp_commands/lp_commands.rs @@ -4,6 +4,7 @@ use derive_more::Display; use http::StatusCode; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; +use mm2_rpc::data::version2::{GetPublicKeyHashResponse, GetPublicKeyResponse}; use rpc::v1::types::H160 as H160Json; use serde_json::Value as Json; @@ -21,11 +22,6 @@ impl From for GetPublicKeyError { fn from(_: CryptoCtxError) -> Self { GetPublicKeyError::Internal("public_key not available".to_string()) } } -#[derive(Serialize)] -pub struct GetPublicKeyResponse { - public_key: String, -} - impl HttpStatusCode for GetPublicKeyError { fn status_code(&self) -> StatusCode { match self { @@ -39,11 +35,6 @@ pub async fn get_public_key(ctx: MmArc, _req: Json) -> GetPublicKeyRpcResult GetPublicKeyRpcResult { let public_key_hash = ctx.rmd160().to_owned().into(); Ok(GetPublicKeyHashResponse { public_key_hash }) diff --git a/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs b/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs index 16c65a2c01..75abe3f755 100644 --- a/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs +++ b/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs @@ -28,8 +28,8 @@ use http::Response; use mm2_core::mm_ctx::MmArc; use mm2_metrics::MetricsOps; use mm2_net::p2p::P2PContext; -use mm2_number::construct_detailed; -use mm2_rpc::data::legacy::{BalanceResponse, CoinInitResponse, Mm2RpcResult, MmVersionResponse, Status}; +use mm2_rpc::data::legacy::{BalanceRequest, BalanceResponse, CancelBy, CoinInitResponse, MinTradingVolResponse, + Mm2RpcResult, MmVersionResponse, Status}; use serde_json::{self as json, Value as Json}; use std::borrow::Cow; use std::collections::HashSet; @@ -38,7 +38,7 @@ use uuid::Uuid; use crate::mm2::lp_dispatcher::{dispatch_lp_event, StopCtxEvent}; use crate::mm2::lp_network::subscribe_to_topic; -use crate::mm2::lp_ordermatch::{cancel_orders_by, get_matching_orders, CancelBy}; +use crate::mm2::lp_ordermatch::{cancel_orders_by, get_matching_orders}; use crate::mm2::lp_swap::{active_swaps_using_coins, tx_helper_topic, watcher_topic}; const INTERNAL_SERVER_ERROR_CODE: u16 = 500; @@ -229,7 +229,8 @@ pub fn metrics(ctx: MmArc) -> HyRes { /// Get my_balance of a coin pub async fn my_balance(ctx: MmArc, req: Json) -> Result>, String> { - let ticker = try_s!(req["coin"].as_str().ok_or("No 'coin' field")).to_owned(); + let BalanceRequest { coin: ticker } = try_s!(json::from_value(req)); + let coin = match lp_coinfind(&ctx, &ticker).await { Ok(Some(t)) => t, Ok(None) => return ERR!("No such coin: {}", ticker), @@ -384,15 +385,6 @@ pub async fn get_my_peer_id(ctx: MmArc) -> Result>, String> { Ok(try_s!(Response::builder().body(res))) } -construct_detailed!(DetailedMinTradingVol, min_trading_vol); - -#[derive(Serialize)] -struct MinTradingVolResponse<'a> { - coin: &'a str, - #[serde(flatten)] - volume: DetailedMinTradingVol, -} - /// Get min_trading_vol of a coin pub async fn min_trading_vol(ctx: MmArc, req: Json) -> Result>, String> { let ticker = try_s!(req["coin"].as_str().ok_or("No 'coin' field")).to_owned(); @@ -403,7 +395,7 @@ pub async fn min_trading_vol(ctx: MmArc, req: Json) -> Result>, }; let min_trading_vol = coin.min_trading_vol(); let response = MinTradingVolResponse { - coin: &ticker, + coin: ticker, volume: min_trading_vol.into(), }; let res = json!({ diff --git a/mm2src/mm2_number/src/mm_number_multi_repr.rs b/mm2src/mm2_number/src/mm_number_multi_repr.rs index 501a7626ba..5b0aeae5a5 100644 --- a/mm2src/mm2_number/src/mm_number_multi_repr.rs +++ b/mm2src/mm2_number/src/mm_number_multi_repr.rs @@ -3,10 +3,10 @@ use crate::from_ratio_to_dec; use crate::mm_number::MmNumber; use bigdecimal::BigDecimal; use num_rational::BigRational; -use serde::Serialize; +use serde::{Deserialize, Serialize}; /// MmNumber representation in all available forms. -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct MmNumberMultiRepr { pub decimal: BigDecimal, pub rational: BigRational, diff --git a/mm2src/mm2_rpc/Cargo.toml b/mm2src/mm2_rpc/Cargo.toml index 6f84bb201a..18d9fb5868 100644 --- a/mm2src/mm2_rpc/Cargo.toml +++ b/mm2src/mm2_rpc/Cargo.toml @@ -19,6 +19,7 @@ serde = "1" serde_json = { version = "1", features = ["preserve_order", "raw_value"] } ser_error = { path = "../derives/ser_error", optional = true} ser_error_derive = { path = "../derives/ser_error_derive", optional=true } +skip_serializing_none = { path = "../derives/skip_serializing_none" } uuid = { version = "1.2.2", features = ["fast-rng", "serde", "v4"] } [features] diff --git a/mm2src/mm2_rpc/src/data/legacy.rs b/mm2src/mm2_rpc/src/data/legacy.rs index c16a328847..270588ac70 100644 --- a/mm2src/mm2_rpc/src/data/legacy.rs +++ b/mm2src/mm2_rpc/src/data/legacy.rs @@ -1,20 +1,29 @@ #[path = "legacy/activation.rs"] mod activation; #[path = "legacy/orders.rs"] mod orders; +#[path = "legacy/swaps.rs"] mod swaps; #[path = "legacy/utility.rs"] mod utility; #[path = "legacy/wallet.rs"] mod wallet; pub use activation::{eth::GasStationPricePolicy, utxo::{ElectrumProtocol, UtxoMergeParams}, - CoinInitResponse, EnabledCoin, GetEnabledResponse}; -pub use orders::{AggregatedOrderbookEntry, MatchBy, OrderConfirmationsSettings, OrderType, OrderbookRequest, - OrderbookResponse, RpcOrderbookEntry, SellBuyRequest, SellBuyResponse, TakerAction, - TakerRequestForRpc}; -pub use utility::{MmVersionResponse, Status}; -pub use wallet::BalanceResponse; + CoinInitResponse, EnabledCoin, GetEnabledResponse, SetRequiredConfRequest, SetRequiredNotaRequest}; +pub use orders::{AggregatedOrderbookEntry, BuyRequest, CancelAllOrdersRequest, CancelAllOrdersResponse, CancelBy, + CancelOrderRequest, FilteringOrder, HistoricalOrder, MakerConnectedForRpc, MakerMatchForRpc, + MakerOrderForMyOrdersRpc, MakerOrderForRpc, MakerReservedForRpc, MatchBy, MinTradingVolResponse, + MyOrdersRequest, MyOrdersResponse, OrderConfirmationsSettings, OrderForRpc, OrderStatusRequest, + OrderStatusResponse, OrderType, OrderbookDepthRequest, OrderbookRequest, OrderbookResponse, + OrdersHistoryRequest, OrdersHistoryResponse, PairDepth, PairWithDepth, RpcOrderbookEntry, + SellBuyRequest, SellBuyResponse, SellRequest, SetPriceRequest, TakerAction, TakerConnectForRpc, + TakerMatchForRpc, TakerOrderForRpc, TakerRequestForRpc, UpdateMakerOrderRequest, UuidParseError}; +pub use swaps::{MySwapsFilter, RecoveredSwapAction}; +pub use utility::{BanPubkeysRequest, MmVersionResponse, StopRequest, UnbanPubkeysRequest, VersionRequest}; +pub use wallet::{BalanceRequest, BalanceResponse}; -use common::serde_derive::{Deserialize, Serialize}; +use derive_more::Display; use std::ops::Deref; +use common::serde_derive::{Deserialize, Serialize}; + #[derive(Serialize, Deserialize)] pub struct Mm2RpcResult { pub result: T, @@ -28,3 +37,9 @@ impl Deref for Mm2RpcResult { type Target = T; fn deref(&self) -> &Self::Target { &self.result } } + +#[derive(Serialize, Deserialize, Display)] +#[serde(rename_all = "lowercase")] +pub enum Status { + Success, +} diff --git a/mm2src/mm2_rpc/src/data/legacy/activation.rs b/mm2src/mm2_rpc/src/data/legacy/activation.rs index a43f531b38..f5e4d36e33 100644 --- a/mm2src/mm2_rpc/src/data/legacy/activation.rs +++ b/mm2src/mm2_rpc/src/data/legacy/activation.rs @@ -1,6 +1,8 @@ #[path = "activation/eth.rs"] pub mod eth; #[path = "activation/utxo.rs"] pub mod utxo; +use skip_serializing_none::skip_serializing_none; + use common::serde_derive::{Deserialize, Serialize}; use mm2_number::BigDecimal; @@ -12,6 +14,7 @@ pub struct EnabledCoin { pub type GetEnabledResponse = Vec; +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct CoinInitResponse { pub result: String, @@ -21,6 +24,19 @@ pub struct CoinInitResponse { pub coin: String, pub required_confirmations: u64, pub requires_notarization: bool, - #[serde(skip_serializing_if = "Option::is_none")] pub mature_confirmations: Option, } + +#[derive(Serialize, Deserialize)] +#[serde(tag = "method", rename = "set_required_confirmations")] +pub struct SetRequiredConfRequest { + pub coin: String, + pub confirmations: u64, +} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "method", rename = "set_requires_notarization")] +pub struct SetRequiredNotaRequest { + pub coin: String, + pub requires_notarization: bool, +} diff --git a/mm2src/mm2_rpc/src/data/legacy/orders.rs b/mm2src/mm2_rpc/src/data/legacy/orders.rs index 8f50966da9..da0ebdfc78 100644 --- a/mm2src/mm2_rpc/src/data/legacy/orders.rs +++ b/mm2src/mm2_rpc/src/data/legacy/orders.rs @@ -1,12 +1,116 @@ +use derive_more::Display; use rpc::v1::types::H256 as H256Json; use serde::{Deserialize, Serialize}; -use std::collections::HashSet; +use skip_serializing_none::skip_serializing_none; +use std::collections::{HashMap, HashSet}; use uuid::Uuid; use common::true_f; use mm2_number::{construct_detailed, BigDecimal, BigRational, Fraction, MmNumber}; -#[derive(Deserialize, Serialize, Debug)] +#[derive(Serialize, Deserialize)] +#[serde(tag = "method", rename = "orderbook")] +pub struct OrderbookRequest { + pub base: String, + pub rel: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct OrderbookResponse { + #[serde(rename = "askdepth")] + pub ask_depth: u32, + pub asks: Vec, + pub base: String, + #[serde(rename = "biddepth")] + pub bid_depth: u32, + pub bids: Vec, + pub netid: u16, + #[serde(rename = "numasks")] + pub num_asks: usize, + #[serde(rename = "numbids")] + pub num_bids: usize, + pub rel: String, + pub timestamp: u64, + #[serde(flatten)] + pub total_asks_base: TotalAsksBaseVol, + #[serde(flatten)] + pub total_asks_rel: TotalAsksRelVol, + #[serde(flatten)] + pub total_bids_base: TotalBidsBaseVol, + #[serde(flatten)] + pub total_bids_rel: TotalBidsRelVol, +} + +construct_detailed!(TotalAsksBaseVol, total_asks_base_vol); +construct_detailed!(TotalAsksRelVol, total_asks_rel_vol); +construct_detailed!(TotalBidsBaseVol, total_bids_base_vol); +construct_detailed!(TotalBidsRelVol, total_bids_rel_vol); + +#[skip_serializing_none] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RpcOrderbookEntry { + pub coin: String, + pub address: String, + pub price: BigDecimal, + pub price_rat: BigRational, + pub price_fraction: Fraction, + #[serde(rename = "maxvolume")] + pub max_volume: BigDecimal, + pub max_volume_rat: BigRational, + pub max_volume_fraction: Fraction, + pub min_volume: BigDecimal, + pub min_volume_rat: BigRational, + pub min_volume_fraction: Fraction, + pub pubkey: String, + pub age: u64, + pub uuid: Uuid, + pub is_mine: bool, + #[serde(flatten)] + pub base_max_volume: DetailedBaseMaxVolume, + #[serde(flatten)] + pub base_min_volume: DetailedBaseMinVolume, + #[serde(flatten)] + pub rel_max_volume: DetailedRelMaxVolume, + #[serde(flatten)] + pub rel_min_volume: DetailedRelMinVolume, + #[serde(flatten)] + pub conf_settings: Option, +} + +construct_detailed!(DetailedBaseMaxVolume, base_max_volume); +construct_detailed!(DetailedBaseMinVolume, base_min_volume); +construct_detailed!(DetailedRelMaxVolume, rel_max_volume); +construct_detailed!(DetailedRelMinVolume, rel_min_volume); + +#[derive(Debug, Serialize, Deserialize)] +pub struct AggregatedOrderbookEntry { + #[serde(flatten)] + pub entry: RpcOrderbookEntry, + #[serde(flatten)] + pub base_max_volume_aggr: AggregatedBaseVol, + #[serde(flatten)] + pub rel_max_volume_aggr: AggregatedRelVol, +} + +construct_detailed!(AggregatedBaseVol, base_max_volume_aggr); +construct_detailed!(AggregatedRelVol, rel_max_volume_aggr); + +#[derive(Serialize)] +#[serde(tag = "method", rename = "sell")] +pub struct SellRequest { + #[serde(flatten)] + pub delegate: SellBuyRequest, +} + +#[derive(Serialize)] +#[serde(tag = "method", rename = "buy")] +pub struct BuyRequest { + #[serde(flatten)] + pub delegate: SellBuyRequest, +} + +#[skip_serializing_none] +#[derive(Deserialize, Serialize)] pub struct SellBuyRequest { pub base: String, pub rel: String, @@ -16,6 +120,7 @@ pub struct SellBuyRequest { /// Not used. Deprecated. #[allow(dead_code)] pub duration: Option, + #[serde(skip_serializing_if = "String::is_empty")] pub method: String, #[allow(dead_code)] pub gui: Option, @@ -36,6 +141,7 @@ pub struct SellBuyRequest { pub save_in_history: bool, } +#[skip_serializing_none] #[derive(Serialize, Deserialize)] pub struct SellBuyResponse { #[serde(flatten)] @@ -49,8 +155,10 @@ pub struct SellBuyResponse { construct_detailed!(DetailedMinVolume, min_volume); +#[skip_serializing_none] #[derive(Clone, Debug, Serialize, Deserialize)] pub struct TakerRequestForRpc { + pub uuid: Uuid, pub base: String, pub rel: String, pub base_amount: BigDecimal, @@ -58,7 +166,6 @@ pub struct TakerRequestForRpc { pub rel_amount: BigDecimal, pub rel_amount_rat: BigRational, pub action: TakerAction, - pub uuid: Uuid, pub method: String, pub sender_pubkey: H256Json, pub dest_pub_key: H256Json, @@ -66,13 +173,13 @@ pub struct TakerRequestForRpc { pub conf_settings: Option, } -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Display, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum TakerAction { Buy, Sell, } -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Display, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", content = "data")] pub enum OrderType { FillOrKill, @@ -95,106 +202,296 @@ impl Default for MatchBy { fn default() -> Self { MatchBy::Any } } +#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct OrderConfirmationsSettings { + pub base_confs: u64, + pub base_nota: bool, + pub rel_confs: u64, + pub rel_nota: bool, +} + +impl OrderConfirmationsSettings { + pub fn reversed(&self) -> OrderConfirmationsSettings { + OrderConfirmationsSettings { + base_confs: self.rel_confs, + base_nota: self.rel_nota, + rel_confs: self.base_confs, + rel_nota: self.base_nota, + } + } +} + #[derive(Serialize, Deserialize)] -pub struct OrderbookRequest { +#[serde(tag = "method", rename = "cancel_order")] +pub struct CancelOrderRequest { + pub uuid: Uuid, +} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "method", rename = "cancel_all_orders")] +pub struct CancelAllOrdersRequest { + pub cancel_by: CancelBy, +} + +#[derive(Serialize, Deserialize)] +pub struct CancelAllOrdersResponse { + pub cancelled: Vec, + pub currently_matching: Vec, +} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "type", content = "data")] +pub enum CancelBy { + All, + Pair { base: String, rel: String }, + Coin { ticker: String }, +} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "method", rename = "order_status")] +pub struct OrderStatusRequest { + pub uuid: Uuid, +} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "type", content = "order")] +pub enum OrderStatusResponse { + Maker(MakerOrderForMyOrdersRpc), + Taker(TakerOrderForRpc), +} + +#[skip_serializing_none] +#[derive(Serialize, Deserialize)] +pub struct MakerOrderForRpc { + pub uuid: Uuid, pub base: String, pub rel: String, + pub price: BigDecimal, + pub price_rat: BigRational, + pub max_base_vol: BigDecimal, + pub max_base_vol_rat: BigRational, + pub min_base_vol: BigDecimal, + pub min_base_vol_rat: BigRational, + pub created_at: u64, + pub updated_at: Option, + pub matches: HashMap, + pub started_swaps: Vec, + pub conf_settings: Option, + pub changes_history: Option>, + pub base_orderbook_ticker: Option, + pub rel_orderbook_ticker: Option, +} + +#[skip_serializing_none] +#[derive(Serialize, Deserialize)] +pub struct TakerOrderForRpc { + pub request: TakerRequestForRpc, + pub created_at: u64, + pub matches: HashMap, + pub order_type: OrderType, + pub cancellable: bool, + pub base_orderbook_ticker: Option, + pub rel_orderbook_ticker: Option, +} + +#[skip_serializing_none] +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct HistoricalOrder { + pub max_base_vol: Option, + pub min_base_vol: Option, + pub price: Option, + pub updated_at: Option, + pub conf_settings: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct MakerOrderForMyOrdersRpc { + #[serde(flatten)] + pub order: MakerOrderForRpc, + pub cancellable: bool, + pub available_amount: BigDecimal, } +#[skip_serializing_none] +#[derive(Serialize, Deserialize)] +pub struct TakerMatchForRpc { + pub reserved: MakerReservedForRpc, + pub connect: TakerConnectForRpc, + pub connected: Option, + pub last_updated: u64, +} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "type", content = "order")] +pub enum OrderForRpc { + Maker(MakerOrderForRpc), + Taker(TakerOrderForRpc), +} + +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] -pub struct OrderbookResponse { - #[serde(rename = "askdepth")] - pub ask_depth: u32, - pub asks: Vec, +pub struct MakerMatchForRpc { + pub request: TakerRequestForRpc, + pub reserved: MakerReservedForRpc, + pub connect: Option, + pub connected: Option, + pub last_updated: u64, +} + +#[skip_serializing_none] +#[derive(Debug, Serialize, Deserialize)] +pub struct MakerReservedForRpc { pub base: String, - #[serde(rename = "biddepth")] - pub bid_depth: u32, - pub bids: Vec, - pub netid: u16, - #[serde(rename = "numasks")] - pub num_asks: usize, - #[serde(rename = "numbids")] - pub num_bids: usize, pub rel: String, - pub timestamp: u64, - #[serde(flatten)] - pub total_asks_base: TotalAsksBaseVol, - #[serde(flatten)] - pub total_asks_rel: TotalAsksRelVol, - #[serde(flatten)] - pub total_bids_base: TotalBidsBaseVol, - #[serde(flatten)] - pub total_bids_rel: TotalBidsRelVol, + pub base_amount: BigDecimal, + pub base_amount_rat: BigRational, + pub rel_amount: BigDecimal, + pub rel_amount_rat: BigRational, + pub taker_order_uuid: Uuid, + pub maker_order_uuid: Uuid, + pub sender_pubkey: H256Json, + pub dest_pub_key: H256Json, + pub conf_settings: Option, + pub method: String, } -construct_detailed!(TotalAsksBaseVol, total_asks_base_vol); -construct_detailed!(TotalAsksRelVol, total_asks_rel_vol); -construct_detailed!(TotalBidsBaseVol, total_bids_base_vol); -construct_detailed!(TotalBidsRelVol, total_bids_rel_vol); +#[derive(Debug, Serialize, Deserialize)] +pub struct TakerConnectForRpc { + pub taker_order_uuid: Uuid, + pub maker_order_uuid: Uuid, + pub method: String, + pub sender_pubkey: H256Json, + pub dest_pub_key: H256Json, +} -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RpcOrderbookEntry { - pub coin: String, - pub address: String, - pub price: BigDecimal, - pub price_rat: BigRational, - pub price_fraction: Fraction, - #[serde(rename = "maxvolume")] - pub max_volume: BigDecimal, - pub max_volume_rat: BigRational, - pub max_volume_fraction: Fraction, - pub min_volume: BigDecimal, - pub min_volume_rat: BigRational, - pub min_volume_fraction: Fraction, - pub pubkey: String, - pub age: u64, - pub uuid: Uuid, - pub is_mine: bool, - #[serde(flatten)] - pub base_max_volume: DetailedBaseMaxVolume, - #[serde(flatten)] - pub base_min_volume: DetailedBaseMinVolume, - #[serde(flatten)] - pub rel_max_volume: DetailedRelMaxVolume, - #[serde(flatten)] - pub rel_min_volume: DetailedRelMinVolume, - #[serde(flatten)] - pub conf_settings: Option, +#[derive(Debug, Serialize, Deserialize)] +pub struct MakerConnectedForRpc { + pub taker_order_uuid: Uuid, + pub maker_order_uuid: Uuid, + pub method: String, + pub sender_pubkey: H256Json, + pub dest_pub_key: H256Json, } -construct_detailed!(DetailedBaseMaxVolume, base_max_volume); -construct_detailed!(DetailedBaseMinVolume, base_min_volume); -construct_detailed!(DetailedRelMaxVolume, rel_max_volume); -construct_detailed!(DetailedRelMinVolume, rel_min_volume); +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "my_orders")] +pub struct MyOrdersRequest {} + +#[derive(Serialize, Deserialize)] +pub struct MyOrdersResponse { + pub maker_orders: HashMap, + pub taker_orders: HashMap, +} + +#[skip_serializing_none] +#[derive(Serialize, Deserialize)] +#[serde(tag = "method", rename = "setprice")] +pub struct SetPriceRequest { + pub base: String, + pub rel: String, + pub price: MmNumber, + #[serde(default)] + pub max: bool, + #[serde(default)] + pub volume: MmNumber, + pub min_volume: Option, + #[serde(default = "true_f")] + pub cancel_previous: bool, + pub base_confs: Option, + pub base_nota: Option, + pub rel_confs: Option, + pub rel_nota: Option, + #[serde(default = "true_f")] + pub save_in_history: bool, +} #[derive(Debug, Serialize, Deserialize)] -pub struct AggregatedOrderbookEntry { - #[serde(flatten)] - pub entry: RpcOrderbookEntry, - #[serde(flatten)] - pub base_max_volume_aggr: AggregatedBaseVol, - #[serde(flatten)] - pub rel_max_volume_aggr: AggregatedRelVol, +#[serde(tag = "method", rename = "orderbook_depth")] +pub struct OrderbookDepthRequest { + pub pairs: Vec<(String, String)>, } -construct_detailed!(AggregatedBaseVol, base_max_volume_aggr); -construct_detailed!(AggregatedRelVol, rel_max_volume_aggr); +#[derive(Debug, Deserialize, Serialize)] +pub struct PairWithDepth { + pub pair: (String, String), + pub depth: PairDepth, +} -#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct OrderConfirmationsSettings { - pub base_confs: u64, - pub base_nota: bool, - pub rel_confs: u64, - pub rel_nota: bool, +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct PairDepth { + pub asks: usize, + pub bids: usize, } -impl OrderConfirmationsSettings { - pub fn reversed(&self) -> OrderConfirmationsSettings { - OrderConfirmationsSettings { - base_confs: self.rel_confs, - base_nota: self.rel_nota, - rel_confs: self.base_confs, - rel_nota: self.base_nota, - } - } +#[skip_serializing_none] +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "method", rename = "orders_history_by_filter")] +pub struct OrdersHistoryRequest { + pub order_type: Option, + pub initial_action: Option, + pub base: Option, + pub rel: Option, + pub from_price: Option, + pub to_price: Option, + pub from_volume: Option, + pub to_volume: Option, + pub from_timestamp: Option, + pub to_timestamp: Option, + pub was_taker: Option, + pub status: Option, + #[serde(default)] + pub include_details: bool, +} + +#[derive(Serialize, Deserialize)] +pub struct OrdersHistoryResponse { + pub orders: Vec, + pub details: Vec, + pub found_records: usize, + pub warnings: Vec, } + +#[derive(Debug, Serialize, Deserialize)] +pub struct FilteringOrder { + pub uuid: String, + pub order_type: String, + pub initial_action: String, + pub base: String, + pub rel: String, + pub price: f64, + pub volume: f64, + pub created_at: i64, + pub last_updated: i64, + pub was_taker: i8, + pub status: String, +} + +#[derive(Serialize, Deserialize)] +pub struct UuidParseError { + pub uuid: String, + pub warning: String, +} + +#[skip_serializing_none] +#[derive(Serialize, Deserialize)] +#[serde(tag = "method", rename = "update_maker_order")] +pub struct UpdateMakerOrderRequest { + pub uuid: Uuid, + pub new_price: Option, + pub max: Option, + pub volume_delta: Option, + pub min_volume: Option, + pub base_confs: Option, + pub base_nota: Option, + pub rel_confs: Option, + pub rel_nota: Option, +} + +#[derive(Deserialize, Serialize)] +pub struct MinTradingVolResponse { + pub coin: String, + #[serde(flatten)] + pub volume: DetailedMinTradingVol, +} + +construct_detailed!(DetailedMinTradingVol, min_trading_vol); diff --git a/mm2src/mm2_rpc/src/data/legacy/swaps.rs b/mm2src/mm2_rpc/src/data/legacy/swaps.rs new file mode 100644 index 0000000000..2e6cd17468 --- /dev/null +++ b/mm2src/mm2_rpc/src/data/legacy/swaps.rs @@ -0,0 +1,18 @@ +use derive_more::Display; +use serde::{Deserialize, Serialize}; +use skip_serializing_none::skip_serializing_none; + +#[skip_serializing_none] +#[derive(Debug, Deserialize, Serialize)] +pub struct MySwapsFilter { + pub my_coin: Option, + pub other_coin: Option, + pub from_timestamp: Option, + pub to_timestamp: Option, +} + +#[derive(Debug, Deserialize, Display, PartialEq, Serialize)] +pub enum RecoveredSwapAction { + RefundedMyPayment, + SpentOtherPayment, +} diff --git a/mm2src/mm2_rpc/src/data/legacy/utility.rs b/mm2src/mm2_rpc/src/data/legacy/utility.rs index b6cef85f7c..a18245a1c9 100644 --- a/mm2src/mm2_rpc/src/data/legacy/utility.rs +++ b/mm2src/mm2_rpc/src/data/legacy/utility.rs @@ -1,14 +1,30 @@ -use derive_more::Display; +use rpc::v1::types::H256 as H256Json; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Display)] -#[serde(rename_all = "lowercase")] -pub enum Status { - Success, -} - #[derive(Serialize, Deserialize)] pub struct MmVersionResponse { pub result: String, pub datetime: String, } + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "stop")] +pub struct StopRequest {} + +#[derive(Default, Serialize)] +#[serde(tag = "method", rename = "version")] +pub struct VersionRequest {} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "method", rename = "ban_pubkey")] +pub struct BanPubkeysRequest { + pub pubkey: H256Json, + pub reason: String, +} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "type", content = "data")] +pub enum UnbanPubkeysRequest { + All, + Few(Vec), +} diff --git a/mm2src/mm2_rpc/src/data/legacy/wallet.rs b/mm2src/mm2_rpc/src/data/legacy/wallet.rs index ddcb5c2354..ad51a439fc 100644 --- a/mm2src/mm2_rpc/src/data/legacy/wallet.rs +++ b/mm2src/mm2_rpc/src/data/legacy/wallet.rs @@ -2,7 +2,13 @@ use serde::{Deserialize, Serialize}; use mm2_number::BigDecimal; -#[derive(Serialize, Deserialize)] +#[derive(Deserialize, Serialize)] +#[serde(tag = "method", rename = "my_balance")] +pub struct BalanceRequest { + pub coin: String, +} + +#[derive(Deserialize, Serialize)] pub struct BalanceResponse { pub coin: String, pub balance: BigDecimal, diff --git a/mm2src/mm2_rpc/src/data/mod.rs b/mm2src/mm2_rpc/src/data/mod.rs index d0b036677b..bc746c76d9 100644 --- a/mm2src/mm2_rpc/src/data/mod.rs +++ b/mm2src/mm2_rpc/src/data/mod.rs @@ -1 +1,2 @@ pub mod legacy; +pub mod version2; diff --git a/mm2src/mm2_rpc/src/data/version2.rs b/mm2src/mm2_rpc/src/data/version2.rs new file mode 100644 index 0000000000..045255c7fe --- /dev/null +++ b/mm2src/mm2_rpc/src/data/version2.rs @@ -0,0 +1,84 @@ +#[path = "version2/wallet.rs"] pub mod wallet; + +pub use wallet::{GetPublicKeyHashResponse, GetPublicKeyResponse, GetRawTransactionRequest, GetRawTransactionResponse}; + +use derive_more::Display; +use serde::{Deserialize, Serialize}; +use skip_serializing_none::skip_serializing_none; +use std::collections::{HashMap, HashSet}; +use uuid::Uuid; + +use mm2_number::{MmNumber, MmNumberMultiRepr}; + +use super::legacy::OrderConfirmationsSettings; + +#[skip_serializing_none] +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct MmRpcRequest { + pub mmrpc: MmRpcVersion, + pub userpass: Option, + pub method: M, + #[serde(default)] + pub params: T, + pub id: Option, +} + +#[derive(Clone, Deserialize, Serialize)] +pub enum MmRpcVersion { + #[serde(rename = "2.0")] + V2, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct BestOrdersRequestV2 { + pub coin: String, + pub action: BestOrdersAction, + pub request_by: BestOrdersByRequest, + #[serde(default)] + pub exclude_mine: bool, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(tag = "type", content = "value")] +#[serde(rename_all = "lowercase")] +pub enum BestOrdersByRequest { + Volume(MmNumber), + Number(usize), +} + +#[derive(Clone, Debug, Deserialize, Display, Eq, PartialEq, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum BestOrdersAction { + Buy, + Sell, +} + +#[skip_serializing_none] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct RpcOrderbookEntryV2 { + pub uuid: Uuid, + pub coin: String, + pub address: OrderbookAddress, + pub price: MmNumberMultiRepr, + pub pubkey: String, + pub is_mine: bool, + pub base_max_volume: MmNumberMultiRepr, + pub base_min_volume: MmNumberMultiRepr, + pub rel_max_volume: MmNumberMultiRepr, + pub rel_min_volume: MmNumberMultiRepr, + pub conf_settings: Option, +} + +#[derive(Deserialize, Serialize)] +pub struct BestOrdersV2Response { + pub orders: HashMap>, + pub original_tickers: HashMap>, +} + +#[derive(Clone, Debug, Deserialize, Display, Serialize)] +#[serde(tag = "address_type", content = "address_data")] +pub enum OrderbookAddress { + Transparent(String), + Shielded, +} diff --git a/mm2src/mm2_rpc/src/data/version2/wallet.rs b/mm2src/mm2_rpc/src/data/version2/wallet.rs new file mode 100644 index 0000000000..f4981ff81b --- /dev/null +++ b/mm2src/mm2_rpc/src/data/version2/wallet.rs @@ -0,0 +1,24 @@ +use rpc::v1::types::{Bytes as BytesJson, H160 as H160Json}; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +pub struct GetPublicKeyResponse { + pub public_key: String, +} + +#[derive(Deserialize, Serialize)] +pub struct GetPublicKeyHashResponse { + pub public_key_hash: H160Json, +} + +#[derive(Deserialize, Serialize)] +pub struct GetRawTransactionRequest { + pub coin: String, + pub tx_hash: String, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct GetRawTransactionResponse { + /// Raw bytes of signed transaction in hexadecimal string, this should be return hexadecimal encoded signed transaction for get_raw_transaction + pub tx_hex: BytesJson, +} diff --git a/mm2src/mm2_rpc/src/mm_protocol.rs b/mm2src/mm2_rpc/src/mm_protocol.rs index 9788236a61..c0ff956f3a 100644 --- a/mm2src/mm2_rpc/src/mm_protocol.rs +++ b/mm2src/mm2_rpc/src/mm_protocol.rs @@ -1,27 +1,11 @@ use common::{HttpStatusCode, SerializationError}; use http::{Response, StatusCode}; use mm2_err_handle::prelude::*; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use serde_json as json; use serde_json::Value as Json; -/// Please note there is no standardized `1.0` version, so this enumeration should not be used in the legacy protocol context. -#[derive(Clone, Copy, Deserialize, Serialize)] -pub enum MmRpcVersion { - #[serde(rename = "2.0")] - V2, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -pub struct MmRpcRequest { - pub mmrpc: MmRpcVersion, - pub userpass: Option, - pub method: String, - #[serde(default)] - pub params: Json, - pub id: Option, -} +use super::data::version2::MmRpcVersion; pub struct MmRpcBuilder { version: MmRpcVersion, @@ -137,7 +121,7 @@ impl MmRpcResponse { fn error_to_json(&self, error: impl serde::ser::Error) -> Json { let response: MmRpcResponse<(), _> = MmRpcResponse { - mmrpc: self.mmrpc, + mmrpc: self.mmrpc.clone(), result: MmRpcResult::Err(MmError::new(SerializationError::InternalError(error.to_string()))), id: self.id, };