use anyhow::Result; use clap::Subcommand; use nssa::AccountId; use crate::{ WalletCore, cli::{SubcommandReturnValue, WalletSubcommand}, helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix, resolve_id_or_label}, program_facades::amm::Amm, }; /// Represents generic CLI subcommand for a wallet working with amm program. #[derive(Subcommand, Debug, Clone)] pub enum AmmProgramAgnosticSubcommand { /// Produce a new pool. /// /// `user_holding_a` and `user_holding_b` must be owned. /// /// Only public execution allowed. New { /// `user_holding_a` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_a_label", conflicts_with = "user_holding_a_key_path", required_unless_present_any = ["user_holding_a_label", "user_holding_a_key_path"] )] user_holding_a: Option, /// User holding A account label (alternative to --user-holding-a). #[arg( long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_key_path" )] user_holding_a_label: Option, /// Key path for user holding A (uses Keycard, alternative to --user-holding-a/label). #[arg( long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_label" )] user_holding_a_key_path: Option, /// `user_holding_b` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_b_label", conflicts_with = "user_holding_b_key_path", required_unless_present_any = ["user_holding_b_label", "user_holding_b_key_path"] )] user_holding_b: Option, /// User holding B account label (alternative to --user-holding-b). #[arg( long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_key_path" )] user_holding_b_label: Option, /// Key path for user holding B (uses Keycard, alternative to --user-holding-b/label). #[arg( long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_label" )] user_holding_b_key_path: Option, /// `user_holding_lp` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_lp_label", conflicts_with = "user_holding_lp_key_path", required_unless_present_any = ["user_holding_lp_label", "user_holding_lp_key_path"] )] user_holding_lp: Option, /// User holding LP account label (alternative to --user-holding-lp). #[arg( long, conflicts_with = "user_holding_lp", conflicts_with = "user_holding_lp_key_path" )] user_holding_lp_label: Option, /// User holding LP key path (alternative to --user-holding-lp) for Keycard. #[arg( long, conflicts_with = "user_holding_lp", conflicts_with = "user_holding_lp_label" )] user_holding_lp_key_path: Option, #[arg(long)] balance_a: u128, #[arg(long)] balance_b: u128, }, /// Swap specifying exact input amount. /// /// The account associated with swapping token must be owned. /// /// Only public execution allowed. SwapExactInput { /// `user_holding_a` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_a_label", conflicts_with = "user_holding_a_key_path", required_unless_present_any = ["user_holding_a_label", "user_holding_a_key_path"] )] user_holding_a: Option, /// User holding A account label (alternative to --user-holding-a). #[arg(long, conflicts_with = "user_holding_a")] user_holding_a_label: Option, /// `user_holding_b` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_b_label", conflicts_with = "user_holding_b_key_path", required_unless_present_any = ["user_holding_b_label", "user_holding_b_key_path"] )] user_holding_b: Option, /// User holding B account label (alternative to --user-holding-b). #[arg( long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_key_path" )] user_holding_b_label: Option, #[arg(long)] amount_in: u128, #[arg(long)] min_amount_out: u128, /// `token_definition` - valid 32 byte base58 string WITHOUT privacy prefix. #[arg(long)] token_definition: String, /// Key path for user token's holding account fpr Token A (uses Keycard). #[arg( long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_label" )] user_holding_a_key_path: Option, /// Key path for user token's holding account fpr Token B (uses Keycard). #[arg( long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_label" )] user_holding_b_key_path: Option, }, /// Swap specifying exact output amount. /// /// The account associated with swapping token must be owned. /// /// Only public execution allowed. SwapExactOutput { /// `user_holding_a` - valid 32 byte base58 string with privacy prefix. #[arg(long)] user_holding_a: String, /// `user_holding_b` - valid 32 byte base58 string with privacy prefix. #[arg(long)] user_holding_b: String, #[arg(long)] exact_amount_out: u128, #[arg(long)] max_amount_in: u128, /// `token_definition` - valid 32 byte base58 string WITHOUT privacy prefix. #[arg(long)] token_definition: String, /// Key path for the input token's holding account (uses Keycard). #[arg(long, conflicts_with = "user_holding_a")] user_holding_a_key_path: Option, /// Key path for the input token's holding account (uses Keycard). #[arg(long, conflicts_with = "user_holding_b")] user_holding_b_key_path: Option, }, /// Add liquidity. /// /// `user_holding_a` and `user_holding_b` must be owned. /// /// Only public execution allowed. AddLiquidity { /// `user_holding_a` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_a_label", conflicts_with = "user_holding_a_key_path", required_unless_present_any = ["user_holding_a_label", "user_holding_a_key_path"] )] user_holding_a: Option, /// User holding A account label (alternative to --user-holding-a). #[arg( long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_key_path" )] user_holding_a_label: Option, /// Key path for user holding A (uses Keycard). #[arg( long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_label" )] user_holding_a_key_path: Option, /// `user_holding_b` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_b_label", conflicts_with = "user_holding_b_key_path", required_unless_present_any = ["user_holding_b_label", "user_holding_b_key_path"] )] user_holding_b: Option, /// User holding B account label (alternative to --user-holding-b). #[arg( long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_key_path" )] user_holding_b_label: Option, /// Key path for user holding B (uses Keycard). #[arg( long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_label" )] user_holding_b_key_path: Option, /// `user_holding_lp` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_lp_label", conflicts_with = "user_holding_lp_key_path", required_unless_present_any = ["user_holding_lp_label", "user_holding_lp_key_path"] )] user_holding_lp: Option, /// User holding LP account label (alternative to --user-holding-lp). #[arg( long, conflicts_with = "user_holding_lp", conflicts_with = "user_holding_lp_key_path" )] user_holding_lp_label: Option, #[arg( long, conflicts_with = "user_holding_lp", conflicts_with = "user_holding_lp_label" )] user_holding_lp_key_path: Option, #[arg(long)] min_amount_lp: u128, #[arg(long)] max_amount_a: u128, #[arg(long)] max_amount_b: u128, }, /// Remove liquidity. /// /// `user_holding_lp` must be owned. /// /// Only public execution allowed. RemoveLiquidity { /// `user_holding_a` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_a_label", conflicts_with = "user_holding_a_key_path", required_unless_present_any = ["user_holding_a_label", "user_holding_a_key_path"] )] user_holding_a: Option, /// User holding A account label (alternative to --user-holding-a). #[arg(long, conflicts_with = "user_holding_a")] user_holding_a_label: Option, /// Key path for user holding a (uses Keycard). #[arg( long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_label" )] user_holding_a_key_path: Option, /// `user_holding_b` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_b_label", conflicts_with = "user_holding_b_key_path", required_unless_present_any = ["user_holding_b_label", "user_holding_b_key_path"] )] user_holding_b: Option, /// User holding B account label (alternative to --user-holding-b). #[arg(long, conflicts_with = "user_holding_b")] user_holding_b_label: Option, /// Key path for user holding B (uses Keycard). #[arg( long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_label" )] user_holding_b_key_path: Option, /// `user_holding_lp` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_lp_label", conflicts_with = "user_holding_lp_key_path", required_unless_present_any = ["user_holding_lp_label", "user_holding_lp_key_path"] )] user_holding_lp: Option, /// User holding LP account label (alternative to --user-holding-lp). #[arg( long, conflicts_with = "user_holding_lp", conflicts_with = "user_holding_lp_key_path" )] user_holding_lp_label: Option, /// Key path for user holding LP (uses Keycard). #[arg( long, conflicts_with = "user_holding_lp", conflicts_with = "user_holding_lp_label" )] user_holding_lp_key_path: Option, #[arg(long)] balance_lp: u128, #[arg(long)] min_amount_a: u128, #[arg(long)] min_amount_b: u128, }, } impl WalletSubcommand for AmmProgramAgnosticSubcommand { async fn handle_subcommand( self, wallet_core: &mut WalletCore, ) -> Result { match self { Self::New { user_holding_a, user_holding_a_label, user_holding_a_key_path, user_holding_b, user_holding_b_label, user_holding_b_key_path, user_holding_lp, user_holding_lp_label, user_holding_lp_key_path, balance_a, balance_b, } => { let user_holding_a = resolve_id_or_label( user_holding_a, user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_a_key_path.as_deref(), )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_b_key_path.as_deref(), )?; let user_holding_lp = resolve_id_or_label( user_holding_lp, user_holding_lp_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_lp_key_path.as_deref(), )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; let (user_holding_b, user_holding_b_privacy) = parse_addr_with_privacy_prefix(&user_holding_b)?; let (user_holding_lp, user_holding_lp_privacy) = parse_addr_with_privacy_prefix(&user_holding_lp)?; let user_holding_a: AccountId = user_holding_a.parse()?; let user_holding_b: AccountId = user_holding_b.parse()?; let user_holding_lp: AccountId = user_holding_lp.parse()?; match ( user_holding_a_privacy, user_holding_b_privacy, user_holding_lp_privacy, ) { ( AccountPrivacyKind::Public, AccountPrivacyKind::Public, AccountPrivacyKind::Public, ) => { Amm(wallet_core) .send_new_definition( user_holding_a, user_holding_b, user_holding_lp, balance_a, balance_b, user_holding_a_key_path.as_deref(), user_holding_b_key_path.as_deref(), ) .await?; Ok(SubcommandReturnValue::Empty) } _ => { // ToDo: Implement after private multi-chain calls is available anyhow::bail!("Only public execution allowed for Amm calls"); } } } Self::SwapExactInput { user_holding_a, user_holding_a_label, user_holding_a_key_path, user_holding_b, user_holding_b_label, user_holding_b_key_path, amount_in, min_amount_out, token_definition, } => { let user_holding_a = resolve_id_or_label( user_holding_a, user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_a_key_path.as_deref(), )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_b_key_path.as_deref(), )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; let (user_holding_b, user_holding_b_privacy) = parse_addr_with_privacy_prefix(&user_holding_b)?; let user_holding_a: AccountId = user_holding_a.parse()?; let user_holding_b: AccountId = user_holding_b.parse()?; match (user_holding_a_privacy, user_holding_b_privacy) { (AccountPrivacyKind::Public, AccountPrivacyKind::Public) => { Amm(wallet_core) .send_swap_exact_input( user_holding_a, user_holding_b, amount_in, min_amount_out, token_definition.parse()?, user_holding_a_key_path.as_deref(), user_holding_b_key_path.as_deref(), ) .await?; Ok(SubcommandReturnValue::Empty) } _ => { // ToDo: Implement after private multi-chain calls is available anyhow::bail!("Only public execution allowed for Amm calls"); } } } Self::SwapExactOutput { user_holding_a, user_holding_b, exact_amount_out, max_amount_in, token_definition, user_holding_a_key_path, user_holding_b_key_path, } => { let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; let (user_holding_b, user_holding_b_privacy) = parse_addr_with_privacy_prefix(&user_holding_b)?; let user_holding_a: AccountId = user_holding_a.parse()?; let user_holding_b: AccountId = user_holding_b.parse()?; match (user_holding_a_privacy, user_holding_b_privacy) { (AccountPrivacyKind::Public, AccountPrivacyKind::Public) => { Amm(wallet_core) .send_swap_exact_output( user_holding_a, user_holding_b, exact_amount_out, max_amount_in, token_definition.parse()?, user_holding_a_key_path.as_deref(), user_holding_b_key_path.as_deref(), ) .await?; Ok(SubcommandReturnValue::Empty) } _ => { // ToDo: Implement after private multi-chain calls is available anyhow::bail!("Only public execution allowed for Amm calls"); } } } Self::AddLiquidity { user_holding_a, user_holding_a_label, user_holding_a_key_path, user_holding_b, user_holding_b_label, user_holding_b_key_path, user_holding_lp, user_holding_lp_label, user_holding_lp_key_path, min_amount_lp, max_amount_a, max_amount_b, } => { let user_holding_a = resolve_id_or_label( user_holding_a, user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_a_key_path.as_deref(), )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_b_key_path.as_deref(), )?; let user_holding_lp = resolve_id_or_label( user_holding_lp, user_holding_lp_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_lp_key_path.as_deref(), )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; let (user_holding_b, user_holding_b_privacy) = parse_addr_with_privacy_prefix(&user_holding_b)?; let (user_holding_lp, user_holding_lp_privacy) = parse_addr_with_privacy_prefix(&user_holding_lp)?; let user_holding_a: AccountId = user_holding_a.parse()?; let user_holding_b: AccountId = user_holding_b.parse()?; let user_holding_lp: AccountId = user_holding_lp.parse()?; match ( user_holding_a_privacy, user_holding_b_privacy, user_holding_lp_privacy, ) { ( AccountPrivacyKind::Public, AccountPrivacyKind::Public, AccountPrivacyKind::Public, ) => { Amm(wallet_core) .send_add_liquidity( user_holding_a, user_holding_b, user_holding_lp, min_amount_lp, max_amount_a, max_amount_b, user_holding_a_key_path.as_deref(), user_holding_b_key_path.as_deref(), ) .await?; Ok(SubcommandReturnValue::Empty) } _ => { // ToDo: Implement after private multi-chain calls is available anyhow::bail!("Only public execution allowed for Amm calls"); } } } Self::RemoveLiquidity { user_holding_a, user_holding_a_label, user_holding_a_key_path, user_holding_b, user_holding_b_label, user_holding_b_key_path, user_holding_lp, user_holding_lp_label, user_holding_lp_key_path, balance_lp, min_amount_a, min_amount_b, } => { let user_holding_a = resolve_id_or_label( user_holding_a, user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_a_key_path.as_deref(), )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_b_key_path.as_deref(), )?; let user_holding_lp = resolve_id_or_label( user_holding_lp, user_holding_lp_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, user_holding_lp_key_path.as_deref(), )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; let (user_holding_b, user_holding_b_privacy) = parse_addr_with_privacy_prefix(&user_holding_b)?; let (user_holding_lp, user_holding_lp_privacy) = parse_addr_with_privacy_prefix(&user_holding_lp)?; let user_holding_a: AccountId = user_holding_a.parse()?; let user_holding_b: AccountId = user_holding_b.parse()?; let user_holding_lp: AccountId = user_holding_lp.parse()?; match ( user_holding_a_privacy, user_holding_b_privacy, user_holding_lp_privacy, ) { ( AccountPrivacyKind::Public, AccountPrivacyKind::Public, AccountPrivacyKind::Public, ) => { Amm(wallet_core) .send_remove_liquidity( user_holding_a, user_holding_b, user_holding_lp, balance_lp, min_amount_a, min_amount_b, user_holding_lp_key_path.as_deref(), ) .await?; Ok(SubcommandReturnValue::Empty) } _ => { // ToDo: Implement after private multi-chain calls is available anyhow::bail!("Only public execution allowed for Amm calls"); } } } } } }