diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index e7e9d3c5ae..be3b49c297 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -483,15 +483,15 @@ impl NymdClient { /// Delegates specified amount of stake to particular mixnode. pub async fn delegate_to_mixnode( &self, - mix_identity: IdentityKey, - amount: Coin, + mix_identity: &str, + amount: &Coin, ) -> Result where C: SigningCosmWasmClient + Sync, { let fee = self.get_fee(Operation::DelegateToMixnode); - let req = ExecuteMsg::DelegateToMixnode { mix_identity }; + let req = ExecuteMsg::DelegateToMixnode { mix_identity: mix_identity.to_string() }; self.client .execute( self.address(), @@ -499,7 +499,7 @@ impl NymdClient { &req, fee, "Delegating to mixnode from rust!", - vec![cosmwasm_coin_to_cosmos_coin(amount)], + vec![cosmwasm_coin_ptr_to_cosmos_coin(amount)], ) .await } @@ -648,3 +648,11 @@ fn cosmwasm_coin_to_cosmos_coin(coin: Coin) -> CosmosCoin { amount: (coin.amount.u128() as u64).into(), } } + +fn cosmwasm_coin_ptr_to_cosmos_coin(coin: &Coin) -> CosmosCoin { + CosmosCoin { + denom: coin.denom.parse().unwrap(), + // this might be a bit iffy, cosmwasm coin stores value as u128, while cosmos does it as u64 + amount: (coin.amount.u128() as u64).into(), + } +} diff --git a/tauri-wallet/src-tauri/src/coin.rs b/tauri-wallet/src-tauri/src/coin.rs new file mode 100644 index 0000000000..16dc230426 --- /dev/null +++ b/tauri-wallet/src-tauri/src/coin.rs @@ -0,0 +1,161 @@ +// This should be moved out of the wallet, and used as a primary coin type throughout the codebase + +use ::config::defaults::DENOM; +use cosmos_sdk::Coin as CosmosCoin; +use cosmos_sdk::Decimal; +use cosmos_sdk::Denom as CosmosDenom; +use cosmwasm_std::Coin as CosmWasmCoin; +use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; +use std::fmt; +use std::ops::Add; +use std::str::FromStr; +use ts_rs::TS; +use validator_client::nymd::GasPrice; + +use crate::format_err; + +#[derive(TS, Serialize, Deserialize, Clone)] +pub enum Denom { + Major, + Minor, +} + +impl fmt::Display for Denom { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Denom::Major => f.write_str(&DENOM[1..].to_uppercase()), + Denom::Minor => f.write_str(DENOM), + } + } +} + +impl FromStr for Denom { + type Err = String; + + fn from_str(s: &str) -> Result { + if s.to_lowercase() == DENOM.to_lowercase() { + Ok(Denom::Minor) + } else if s.to_lowercase() == DENOM[1..].to_lowercase() { + Ok(Denom::Major) + } else { + Err(format_err!(format!( + "{} is not a valid denomination string", + s + ))) + } + } +} + +#[derive(TS, Serialize, Deserialize, Clone)] +pub struct Coin { + amount: String, + denom: Denom, +} + +// TODO convert to TryFrom +impl From for Coin { + fn from(g: GasPrice) -> Coin { + Coin { + amount: g.amount.to_string(), + denom: Denom::from_str(&g.denom.to_string()).unwrap(), + } + } +} + +impl fmt::Display for Coin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&format!("{} {}", self.amount, self.denom)) + } +} + +// Allows adding minor and major denominations, output will have the LHS denom. +impl Add for Coin { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + let denom = self.denom.clone(); + let lhs = self.to_minor(); + let rhs = rhs.to_minor(); + let lhs_amount = lhs.amount.parse::().unwrap(); + let rhs_amount = rhs.amount.parse::().unwrap(); + let amount = lhs_amount + rhs_amount; + let coin = Coin { + amount: amount.to_string(), + denom: Denom::Minor, + }; + match denom { + Denom::Major => coin.to_major(), + Denom::Minor => coin, + } + } +} + +impl Coin { + pub fn to_major(&self) -> Coin { + match self.denom { + Denom::Major => self.clone(), + Denom::Minor => Coin { + amount: (self.amount.parse::().unwrap() / 1_000_000.).to_string(), + denom: Denom::Major, + }, + } + } + + pub fn to_minor(&self) -> Coin { + match self.denom { + Denom::Minor => self.clone(), + Denom::Major => Coin { + amount: (self.amount.parse::().unwrap() * 1_000_000.).to_string(), + denom: Denom::Minor, + }, + } + } +} + +impl TryFrom for CosmWasmCoin { + type Error = String; + + fn try_from(coin: Coin) -> Result { + match serde_json::to_value(coin) { + Ok(value) => match serde_json::from_value(value) { + Ok(coin) => Ok(coin), + Err(e) => Err(format_err!(e)), + }, + Err(e) => Err(format_err!(e)), + } + } +} + +impl TryFrom for CosmosCoin { + type Error = String; + + fn try_from(coin: Coin) -> Result { + let coin = coin.to_minor(); + match Decimal::from_str(&coin.amount) { + Ok(d) => Ok(CosmosCoin { + amount: d, + denom: CosmosDenom::from_str(&coin.denom.to_string()).unwrap(), + }), + Err(e) => Err(format_err!(e)), + } + } +} + +impl From for Coin { + fn from(c: CosmosCoin) -> Coin { + Coin { + amount: c.amount.to_string(), + denom: Denom::from_str(&c.denom.to_string()).unwrap(), + } + } +} + +impl From for Coin { + fn from(c: CosmWasmCoin) -> Coin { + Coin { + amount: c.amount.to_string(), + denom: Denom::from_str(&c.denom.to_string()).unwrap(), + } + } +} diff --git a/tauri-wallet/src-tauri/src/main.rs b/tauri-wallet/src-tauri/src/main.rs index f62d229dd1..b06ef4d866 100644 --- a/tauri-wallet/src-tauri/src/main.rs +++ b/tauri-wallet/src-tauri/src/main.rs @@ -3,32 +3,28 @@ windows_subsystem = "windows" )] -use ::config::defaults::DENOM; use bip39::{Language, Mnemonic}; use cosmos_sdk::Coin as CosmosCoin; -use cosmos_sdk::Denom as CosmosDenom; -use cosmos_sdk::{AccountId, Decimal}; +use cosmos_sdk::AccountId; use cosmwasm_std::Coin as CosmWasmCoin; use error::BackendError; use mixnet_contract::{Gateway, MixNode}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::convert::{TryFrom, TryInto}; -use std::fmt; -use std::ops::Add; use std::str::FromStr; use std::sync::Arc; use tendermint_rpc::endpoint::broadcast::tx_commit::Response; use tokio::sync::RwLock; use ts_rs::{export, TS}; use validator_client::nymd::fee_helpers::Operation; -use validator_client::nymd::GasPrice; use validator_client::nymd::{NymdClient, SigningNymdClient}; +use std::convert::TryInto; mod coconut; mod config; mod error; mod state; +mod coin; use crate::coconut::{ delete_credential, get_credential, list_credentials, randomise_credential, verify_credential, @@ -36,6 +32,7 @@ use crate::coconut::{ use crate::state::State; use crate::config::Config; +use crate::coin::{Coin, Denom}; #[macro_export] macro_rules! format_err { @@ -44,43 +41,19 @@ macro_rules! format_err { }; } +#[derive(TS, Serialize, Deserialize)] +struct DelegationResult { + source_address: String, + target_address: String, + amount: Option +} + #[derive(TS, Serialize, Deserialize)] struct Balance { coin: Coin, printable_balance: String, } -enum Denom { - Major, - Minor, -} - -impl fmt::Display for Denom { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Denom::Major => f.write_str(&DENOM[1..].to_uppercase()), - Denom::Minor => f.write_str(DENOM), - } - } -} - -impl FromStr for Denom { - type Err = String; - - fn from_str(s: &str) -> Result { - if s.to_lowercase() == DENOM.to_lowercase() { - Ok(Denom::Minor) - } else if s.to_lowercase() == DENOM[1..].to_lowercase() { - Ok(Denom::Major) - } else { - Err(format_err!(format!( - "{} is not a valid denomination string", - s - ))) - } - } -} - #[derive(Deserialize, Serialize, TS)] struct TauriTxResult { code: u32, @@ -109,124 +82,12 @@ impl TauriTxResult { } } -// Proxy types to allow TS generation -#[derive(TS, Serialize, Deserialize, Clone)] -struct Coin { - amount: String, - denom: String, -} - -impl From for Coin { - fn from(g: GasPrice) -> Coin { - Coin { - amount: g.amount.to_string(), - denom: g.denom.to_string(), - } - } -} - -impl fmt::Display for Coin { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&format!("{} {}", self.amount, self.denom)) - } -} - -// Allows adding minor and major denominations, output will have the LHS denom. -impl Add for Coin { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - let denom = Denom::from_str(&self.denom).unwrap(); - let lhs = self.to_minor(); - let rhs = rhs.to_minor(); - let lhs_amount = lhs.amount.parse::().unwrap(); - let rhs_amount = rhs.amount.parse::().unwrap(); - let amount = lhs_amount + rhs_amount; - let coin = Coin { - amount: amount.to_string(), - denom: Denom::Minor.to_string(), - }; - match denom { - Denom::Major => coin.to_major(), - Denom::Minor => coin, - } - } -} - -impl Coin { - fn to_major(&self) -> Coin { - if let Ok(denom) = Denom::from_str(&self.denom) { - match denom { - Denom::Major => self.clone(), - Denom::Minor => Coin { - amount: (self.amount.parse::().unwrap() / 1_000_000.).to_string(), - denom: Denom::Major.to_string(), - }, - } - } else { - unreachable!() - } - } - - fn to_minor(&self) -> Coin { - if let Ok(denom) = Denom::from_str(&self.denom) { - match denom { - Denom::Minor => self.clone(), - Denom::Major => Coin { - amount: (self.amount.parse::().unwrap() * 1_000_000.).to_string(), - denom: Denom::Minor.to_string(), - }, - } - } else { - unreachable!() - } - } -} - -impl TryFrom for CosmWasmCoin { - type Error = String; - - fn try_from(coin: Coin) -> Result { - match serde_json::to_value(coin) { - Ok(value) => match serde_json::from_value(value) { - Ok(coin) => Ok(coin), - Err(e) => Err(format_err!(e)), - }, - Err(e) => Err(format_err!(e)), - } - } -} - -// There is some confusion here over coins and denoms, it feels like we should use types to differentiate between the two -impl TryFrom for CosmosCoin { - type Error = String; - - fn try_from(coin: Coin) -> Result { - let coin = coin.to_minor(); - match Decimal::from_str(&coin.amount) { - Ok(d) => Ok(CosmosCoin { - amount: d, - denom: CosmosDenom::from_str(&coin.denom).unwrap(), - }), - Err(e) => Err(format_err!(e)), - } - } -} - -impl From for Coin { - fn from(c: CosmosCoin) -> Coin { - Coin { - amount: c.amount.to_string(), - denom: c.denom.to_string(), - } - } -} - +// TODO these should be more explicit #[tauri::command] fn major_to_minor(amount: String) -> Result { let coin = Coin { amount, - denom: Denom::Major.to_string(), + denom: Denom::Major, }; Ok(coin.to_minor()) } @@ -235,7 +96,7 @@ fn major_to_minor(amount: String) -> Result { fn minor_to_major(amount: String) -> Result { let coin = Coin { amount, - denom: Denom::Minor.to_string(), + denom: Denom::Minor, }; Ok(coin.to_major()) } @@ -285,7 +146,7 @@ async fn get_balance(state: tauri::State<'_, Arc>>) -> Result { let coin = Coin { amount: coin.amount.to_string(), - denom: coin.denom.to_string(), + denom: Denom::from_str(&coin.denom.to_string())?, }; Ok(Balance { coin: coin.clone(), @@ -353,15 +214,19 @@ async fn delegate_to_mixnode( identity: String, amount: Coin, state: tauri::State<'_, Arc>>, -) -> Result<(), String> { +) -> Result { let r_state = state.read().await; let bond: CosmWasmCoin = match amount.try_into() { Ok(b) => b, Err(e) => return Err(format_err!(e)), }; let client = r_state.client()?; - match client.delegate_to_mixnode(identity, bond).await { - Ok(_result) => Ok(()), + match client.delegate_to_mixnode(&identity, &bond).await { + Ok(_result) => Ok(DelegationResult { + source_address: client.address().to_string(), + target_address: identity, + amount: Some(bond.into()) + }), Err(e) => Err(format_err!(e)), } } @@ -478,7 +343,7 @@ async fn get_fee( let fee = client.get_fee(operation); let mut coin = Coin { amount: "0".to_string(), - denom: "upunk".to_string(), + denom: Denom::Major, }; for f in fee.amount { coin = coin + f.into();