diff --git a/tauri-wallet/src-tauri/src/coin.rs b/tauri-wallet/src-tauri/src/coin.rs index 11a365f6b8..8a7862b80f 100644 --- a/tauri-wallet/src-tauri/src/coin.rs +++ b/tauri-wallet/src-tauri/src/coin.rs @@ -11,7 +11,7 @@ use std::fmt; use std::ops::{Add, Sub}; use std::str::FromStr; use ts_rs::TS; -use validator_client::nymd::{GasPrice, CosmosCoin}; +use validator_client::nymd::{CosmosCoin, GasPrice}; use crate::format_err; @@ -96,25 +96,25 @@ impl Add for Coin { // Allows adding minor and major denominations, output will have the LHS denom. impl Sub for Coin { - type Output = Self; - - fn sub(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, - } + type Output = Self; + + fn sub(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 major(amount: T) -> Coin { @@ -212,7 +212,7 @@ impl From for Coin { #[cfg(test)] mod test { - use crate::{Coin, Denom}; + use crate::coin::{Coin, Denom}; use cosmrs::Coin as CosmosCoin; use cosmrs::Decimal; use cosmrs::Denom as CosmosDenom; @@ -273,7 +273,7 @@ mod test { "1000000000000000", "10000000000000000", "100000000000000000", - "1000000000000000000" + "1000000000000000000", ] } @@ -333,10 +333,10 @@ mod test { #[test] fn test_add() { - assert_eq!(Coin::minor("1") + Coin::minor("1"), Coin::minor("2")); - assert_eq!(Coin::major("1") + Coin::major("1"), Coin::major("2")); - assert_eq!(Coin::minor("1") + Coin::major("1"), Coin::minor("1000001")); - assert_eq!(Coin::major("1") + Coin::minor("1"), Coin::major("1.000001")); + assert_eq!(Coin::minor("1") + Coin::minor("1"), Coin::minor("2")); + assert_eq!(Coin::major("1") + Coin::major("1"), Coin::major("2")); + assert_eq!(Coin::minor("1") + Coin::major("1"), Coin::minor("1000001")); + assert_eq!(Coin::major("1") + Coin::minor("1"), Coin::major("1.000001")); } #[test] @@ -345,5 +345,5 @@ mod test { assert_eq!(Coin::major("1") - Coin::major("1"), Coin::major("0")); assert_eq!(Coin::minor("1") - Coin::major("1"), Coin::minor("-999999")); assert_eq!(Coin::major("1") - Coin::minor("1"), Coin::major("0.999999")); -} + } } diff --git a/tauri-wallet/src-tauri/src/main.rs b/tauri-wallet/src-tauri/src/main.rs index d1b201d33c..1b6a7c3ebd 100644 --- a/tauri-wallet/src-tauri/src/main.rs +++ b/tauri-wallet/src-tauri/src/main.rs @@ -3,30 +3,30 @@ windows_subsystem = "windows" )] -use bip39::{Language, Mnemonic}; -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::TryInto; -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 ts_rs::export; use validator_client::nymd::fee_helpers::Operation; -use validator_client::nymd::{NymdClient, SigningNymdClient, AccountId, CosmosCoin}; mod coin; mod config; mod error; +mod operations; mod state; +mod utils; + +use crate::operations::account::*; +use crate::operations::admin::*; +use crate::operations::bond::*; +use crate::operations::delegate::*; +use crate::operations::send::*; +use crate::utils::*; use crate::state::State; +#[cfg(test)] use crate::coin::{Coin, Denom}; -use crate::config::Config; #[macro_export] macro_rules! format_err { @@ -35,346 +35,6 @@ 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, -} - -#[derive(Deserialize, Serialize, TS)] -struct TauriTxResult { - code: u32, - gas_wanted: u64, - gas_used: u64, - block_height: u64, - details: TransactionDetails, -} - -#[derive(Deserialize, Serialize, TS)] -struct TransactionDetails { - from_address: String, - to_address: String, - amount: Coin, -} - -impl TauriTxResult { - fn new(t: Response, details: TransactionDetails) -> TauriTxResult { - TauriTxResult { - code: t.check_tx.code.value(), - gas_wanted: t.check_tx.gas_wanted.value(), - gas_used: t.check_tx.gas_used.value(), - block_height: t.height.value(), - details, - } - } -} - -// TODO these should be more explicit -#[tauri::command] -fn major_to_minor(amount: &str) -> Result { - let coin = Coin::new(amount, &Denom::Major); - Ok(coin.to_minor()) -} - -#[tauri::command] -fn minor_to_major(amount: &str) -> Result { - let coin = Coin::new(amount, &Denom::Minor); - Ok(coin.to_major()) -} - -#[tauri::command] -async fn connect_with_mnemonic( - mnemonic: String, - state: tauri::State<'_, Arc>>, -) -> Result, String> { - let mnemonic = match Mnemonic::from_str(&mnemonic) { - Ok(mnemonic) => mnemonic, - Err(e) => return Err(BackendError::from(e).to_string()), - }; - let client; - { - let r_state = state.read().await; - client = _connect_with_mnemonic(mnemonic, &r_state.config()); - } - - let mut ret = HashMap::new(); - ret.insert( - "contract_address", - match client.contract_address() { - Ok(address) => address.to_string(), - Err(e) => format_err!(e), - }, - ); - ret.insert("client_address", client.address().to_string()); - ret.insert( - "denom", - match client.denom() { - Ok(denom) => denom.to_string(), - Err(e) => format_err!(e), - }, - ); - let mut w_state = state.write().await; - w_state.set_client(client); - - Ok(ret) -} - -#[tauri::command] -async fn get_balance(state: tauri::State<'_, Arc>>) -> Result { - let r_state = state.read().await; - let client = r_state.client()?; - match client.get_balance(client.address()).await { - Ok(Some(coin)) => { - let coin = Coin::new( - &coin.amount.to_string(), - &Denom::from_str(&coin.denom.to_string())?, - ); - Ok(Balance { - coin: coin.clone(), - printable_balance: coin.to_major().to_string(), - }) - } - Ok(None) => Err(format!( - "No balance available for address {}", - client.address() - )), - Err(e) => Err(BackendError::from(e).to_string()), - } -} - -#[tauri::command] -async fn owns_mixnode(state: tauri::State<'_, Arc>>) -> Result { - let r_state = state.read().await; - let client = r_state.client()?; - match client.owns_mixnode(client.address()).await { - Ok(o) => Ok(o), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn owns_gateway(state: tauri::State<'_, Arc>>) -> Result { - let r_state = state.read().await; - let client = r_state.client()?; - match client.owns_gateway(client.address()).await { - Ok(o) => Ok(o), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn unbond_mixnode(state: tauri::State<'_, Arc>>) -> Result<(), String> { - let r_state = state.read().await; - let client = r_state.client()?; - match client.unbond_mixnode().await { - Ok(_result) => Ok(()), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn bond_mixnode( - mixnode: MixNode, - bond: Coin, - state: tauri::State<'_, Arc>>, -) -> Result<(), String> { - let r_state = state.read().await; - let bond: CosmWasmCoin = match bond.try_into() { - Ok(b) => b, - Err(e) => return Err(format_err!(e)), - }; - let client = r_state.client()?; - match client.bond_mixnode(mixnode, bond).await { - Ok(_result) => Ok(()), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn delegate_to_mixnode( - identity: &str, - amount: Coin, - state: tauri::State<'_, Arc>>, -) -> 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(DelegationResult { - source_address: client.address().to_string(), - target_address: identity.to_string(), - amount: Some(bond.into()), - }), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn undelegate_from_mixnode( - identity: &str, - state: tauri::State<'_, Arc>>, -) -> Result { - let r_state = state.read().await; - let client = r_state.client()?; - match client.remove_mixnode_delegation(identity).await { - Ok(_result) => Ok(DelegationResult { - source_address: client.address().to_string(), - target_address: identity.to_string(), - amount: None, - }), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn delegate_to_gateway( - identity: &str, - amount: Coin, - state: tauri::State<'_, Arc>>, -) -> 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_gateway(identity, &bond).await { - Ok(_result) => Ok(DelegationResult { - source_address: client.address().to_string(), - target_address: identity.to_string(), - amount: Some(bond.into()), - }), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn undelegate_from_gateway( - identity: &str, - state: tauri::State<'_, Arc>>, -) -> Result { - let r_state = state.read().await; - let client = r_state.client()?; - match client.remove_gateway_delegation(identity).await { - Ok(_result) => Ok(DelegationResult { - source_address: client.address().to_string(), - target_address: identity.to_string(), - amount: None, - }), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn bond_gateway( - gateway: Gateway, - bond: Coin, - state: tauri::State<'_, Arc>>, -) -> Result<(), String> { - let r_state = state.read().await; - let bond: CosmWasmCoin = match bond.try_into() { - Ok(b) => b, - Err(e) => return Err(format_err!(e)), - }; - let client = r_state.client()?; - match client.bond_gateway(gateway, bond).await { - Ok(_result) => Ok(()), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn unbond_gateway(state: tauri::State<'_, Arc>>) -> Result<(), String> { - let r_state = state.read().await; - let client = r_state.client()?; - match client.unbond_gateway().await { - Ok(_result) => Ok(()), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn send( - address: &str, - amount: Coin, - memo: String, - state: tauri::State<'_, Arc>>, -) -> Result { - let address = match AccountId::from_str(address) { - Ok(addy) => addy, - Err(e) => return Err(format_err!(e)), - }; - let cosmos_amount: CosmosCoin = match amount.clone().try_into() { - Ok(b) => b, - Err(e) => return Err(format_err!(e)), - }; - let r_state = state.read().await; - let client = r_state.client()?; - match client.send(&address, vec![cosmos_amount], memo).await { - Ok(result) => Ok(TauriTxResult::new( - result, - TransactionDetails { - from_address: client.address().to_string(), - to_address: address.to_string(), - amount, - }, - )), - Err(e) => Err(format_err!(e)), - } -} - -#[tauri::command] -async fn get_fee( - operation: Operation, - state: tauri::State<'_, Arc>>, -) -> Result { - let r_state = state.read().await; - let client = r_state.client()?; - let fee = client.get_fee(operation); - let mut coin = Coin::new("0", &Denom::Major); - for f in fee.amount { - coin = coin + f.into(); - } - - Ok(coin) -} - -#[tauri::command] -async fn create_new_account( - state: tauri::State<'_, Arc>>, -) -> Result, String> { - let mnemonic = random_mnemonic(); - let mut client = connect_with_mnemonic(mnemonic.to_string(), state).await?; - client.insert("mnemonic", mnemonic.to_string()); - Ok(client) -} - -fn random_mnemonic() -> Mnemonic { - let mut rng = rand::thread_rng(); - Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap() -} - -fn _connect_with_mnemonic(mnemonic: Mnemonic, config: &Config) -> NymdClient { - match NymdClient::connect_with_mnemonic( - config.get_nymd_validator_url().unwrap(), - Some(AccountId::from_str(&config.get_mixnet_contract_address()).unwrap()), - mnemonic, - ) { - Ok(client) => client, - Err(e) => panic!("{}", e), - } -} - fn main() { tauri::Builder::default() .manage(Arc::new(RwLock::new(State::default()))) @@ -395,7 +55,9 @@ fn main() { undelegate_from_gateway, send, create_new_account, - get_fee + get_fee, + get_state_params, + update_state_params ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); @@ -410,5 +72,7 @@ export! { TransactionDetails => "../src/types/rust/transactiondetails.ts", Operation => "../src/types/rust/operation.ts", Denom => "../src/types/rust/denom.ts", - DelegationResult => "../src/types/rust/delegationresult.ts" + DelegationResult => "../src/types/rust/delegationresult.ts", + Account => "../src/types/rust/account.ts", + TauriStateParams => "../src/types/rust/stateparams.ts" } diff --git a/tauri-wallet/src-tauri/src/nymd_client.rs b/tauri-wallet/src-tauri/src/nymd_client.rs deleted file mode 100644 index 08e0ac5929..0000000000 --- a/tauri-wallet/src-tauri/src/nymd_client.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::config::Config; -use config::defaults::DEFAULT_VALIDATOR_API_PORT; -use mixnet_contract::{GatewayBond, MixNodeBond}; -use std::sync::Arc; -use tokio::sync::RwLock; -use validator_client::nymd::{CosmWasmClient, QueryNymdClient, SigningNymdClient, NymdClient}; -use validator_client::ValidatorClientError; - -#[derive(Clone)] -pub(crate) struct Client(Arc>>); - -impl Client { - pub(crate) fn new_query(config: &Config) -> Self { - // the api address is irrelevant here as **WE ARE THE API** - let api_url = format!("http://localhost:{}", DEFAULT_VALIDATOR_API_PORT) - .parse() - .unwrap(); - let nymd_url = config.get_nymd_validator_url(); - - let mixnet_contract = config - .get_mixnet_contract_address() - .parse() - .expect("the mixnet contract address is invalid!"); - - let client_config = validator_client::Config::new(nymd_url, api_url, Some(mixnet_contract)); - let inner = - validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"); - - Client(Arc::new(RwLock::new(inner))) - } -} - -impl Client { - pub(crate) fn new_signing(config: &Config) -> Self { - // the api address is irrelevant here as **WE ARE THE API** - let api_url = format!("http://localhost:{}", DEFAULT_VALIDATOR_API_PORT) - .parse() - .unwrap(); - let nymd_url = config.get_nymd_validator_url(); - - let mixnet_contract = config - .get_mixnet_contract_address() - .parse() - .expect("the mixnet contract address is invalid!"); - let mnemonic = config - .get_mnemonic() - .parse() - .expect("the mnemonic is invalid!"); - - let client_config = validator_client::Config::new(nymd_url, api_url, Some(mixnet_contract)); - let inner = validator_client::Client::new_signing(client_config, mnemonic) - .expect("Failed to connect to nymd!"); - - Client(Arc::new(RwLock::new(inner))) - } -} - -impl Client { - pub(crate) async fn get_mixnodes(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync, - { - self.0.read().await.get_all_nymd_mixnodes().await - } - - pub(crate) async fn get_gateways(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync, - { - self.0.read().await.get_all_nymd_gateways().await - } - - #[allow(dead_code)] - pub(crate) async fn some_rewarding_stuff_here(&self) { - todo!() - } -} diff --git a/tauri-wallet/src-tauri/src/operations/account.rs b/tauri-wallet/src-tauri/src/operations/account.rs new file mode 100644 index 0000000000..95416fcb0d --- /dev/null +++ b/tauri-wallet/src-tauri/src/operations/account.rs @@ -0,0 +1,113 @@ +use crate::coin::{Coin, Denom}; +use crate::config::Config; +use crate::error::BackendError; +use crate::format_err; +use crate::state::State; +use bip39::{Language, Mnemonic}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; +use std::sync::Arc; +use tokio::sync::RwLock; +use ts_rs::TS; +use validator_client::nymd::{AccountId, NymdClient, SigningNymdClient}; + +#[derive(TS, Serialize, Deserialize)] +pub struct Account { + contract_address: String, + client_address: String, + denom: Denom, + mnemonmic: Option, +} + +#[derive(TS, Serialize, Deserialize)] +pub struct Balance { + coin: Coin, + printable_balance: String, +} + +#[tauri::command] +pub async fn connect_with_mnemonic( + mnemonic: String, + state: tauri::State<'_, Arc>>, +) -> Result { + let mnemonic = match Mnemonic::from_str(&mnemonic) { + Ok(mnemonic) => mnemonic, + Err(e) => return Err(BackendError::from(e).to_string()), + }; + let client; + { + let r_state = state.read().await; + client = _connect_with_mnemonic(mnemonic, &r_state.config()); + } + + let contract_address = match client.contract_address() { + Ok(address) => address.to_string(), + Err(e) => return Err(format_err!(e)), + }; + let client_address = client.address().to_string(); + let denom = match client.denom() { + Ok(denom) => denom, + Err(e) => return Err(format_err!(e)), + }; + + let account = Account { + contract_address, + client_address, + denom: Denom::from_str(&denom.to_string())?, + mnemonmic: None, + }; + + let mut w_state = state.write().await; + w_state.set_client(client); + + Ok(account) +} + +#[tauri::command] +pub async fn get_balance(state: tauri::State<'_, Arc>>) -> Result { + let r_state = state.read().await; + let client = r_state.client()?; + match client.get_balance(client.address()).await { + Ok(Some(coin)) => { + let coin = Coin::new( + &coin.amount.to_string(), + &Denom::from_str(&coin.denom.to_string())?, + ); + Ok(Balance { + coin: coin.clone(), + printable_balance: coin.to_major().to_string(), + }) + } + Ok(None) => Err(format!( + "No balance available for address {}", + client.address() + )), + Err(e) => Err(BackendError::from(e).to_string()), + } +} + +#[tauri::command] +pub async fn create_new_account( + state: tauri::State<'_, Arc>>, +) -> Result { + let mnemonic = random_mnemonic(); + let mut client = connect_with_mnemonic(mnemonic.to_string(), state).await?; + client.mnemonmic = Some(mnemonic.to_string()); + Ok(client) +} + +fn random_mnemonic() -> Mnemonic { + let mut rng = rand::thread_rng(); + Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap() +} + +fn _connect_with_mnemonic(mnemonic: Mnemonic, config: &Config) -> NymdClient { + match NymdClient::connect_with_mnemonic( + config.get_nymd_validator_url().unwrap(), + Some(AccountId::from_str(&config.get_mixnet_contract_address()).unwrap()), + mnemonic, + ) { + Ok(client) => client, + Err(e) => panic!("{}", e), + } +} diff --git a/tauri-wallet/src-tauri/src/operations/admin.rs b/tauri-wallet/src-tauri/src/operations/admin.rs new file mode 100644 index 0000000000..8e4f832936 --- /dev/null +++ b/tauri-wallet/src-tauri/src/operations/admin.rs @@ -0,0 +1,84 @@ +use crate::format_err; +use crate::state::State; +use cosmwasm_std::Decimal; +use cosmwasm_std::Uint128; +use mixnet_contract::StateParams; +use serde::{Deserialize, Serialize}; +use std::convert::{TryFrom, TryInto}; +use std::str::FromStr; +use std::sync::Arc; +use tokio::sync::RwLock; +use ts_rs::TS; + +#[derive(Serialize, Deserialize, TS)] +pub struct TauriStateParams { + epoch_length: u32, + minimum_mixnode_bond: String, + minimum_gateway_bond: String, + mixnode_bond_reward_rate: String, + gateway_bond_reward_rate: String, + mixnode_delegation_reward_rate: String, + gateway_delegation_reward_rate: String, + mixnode_active_set_size: u32, +} + +impl From for TauriStateParams { + fn from(p: StateParams) -> TauriStateParams { + TauriStateParams { + epoch_length: p.epoch_length, + minimum_mixnode_bond: p.minimum_mixnode_bond.to_string(), + minimum_gateway_bond: p.minimum_gateway_bond.to_string(), + mixnode_bond_reward_rate: p.mixnode_bond_reward_rate.to_string(), + gateway_bond_reward_rate: p.gateway_bond_reward_rate.to_string(), + mixnode_delegation_reward_rate: p.mixnode_delegation_reward_rate.to_string(), + gateway_delegation_reward_rate: p.gateway_delegation_reward_rate.to_string(), + mixnode_active_set_size: p.mixnode_active_set_size, + } + } +} + +impl TryFrom for StateParams { + type Error = Box; + + fn try_from(p: TauriStateParams) -> Result { + Ok(StateParams { + epoch_length: p.epoch_length, + minimum_mixnode_bond: Uint128::try_from(p.minimum_mixnode_bond.as_str())?, + minimum_gateway_bond: Uint128::try_from(p.minimum_gateway_bond.as_str())?, + mixnode_bond_reward_rate: Decimal::from_str(p.mixnode_bond_reward_rate.as_str())?, + gateway_bond_reward_rate: Decimal::from_str(p.gateway_bond_reward_rate.as_str())?, + mixnode_delegation_reward_rate: Decimal::from_str(p.mixnode_delegation_reward_rate.as_str())?, + gateway_delegation_reward_rate: Decimal::from_str(p.gateway_delegation_reward_rate.as_str())?, + mixnode_active_set_size: p.mixnode_active_set_size, + }) + } +} + +#[tauri::command] +pub async fn get_state_params( + state: tauri::State<'_, Arc>>, +) -> Result { + let r_state = state.read().await; + let client = r_state.client()?; + match client.get_state_params().await { + Ok(params) => Ok(params.into()), + Err(e) => Err(format_err!(e)), + } +} + +#[tauri::command] +pub async fn update_state_params( + params: TauriStateParams, + state: tauri::State<'_, Arc>>, +) -> Result { + let r_state = state.read().await; + let client = r_state.client()?; + let state_params: StateParams = match params.try_into() { + Ok(state_params) => state_params, + Err(e) => return Err(format_err!(e)), + }; + match client.update_state_params(state_params.clone()).await { + Ok(_) => Ok(state_params.into()), + Err(e) => Err(format_err!(e)), + } +} diff --git a/tauri-wallet/src-tauri/src/operations/bond.rs b/tauri-wallet/src-tauri/src/operations/bond.rs new file mode 100644 index 0000000000..b7046feef2 --- /dev/null +++ b/tauri-wallet/src-tauri/src/operations/bond.rs @@ -0,0 +1,64 @@ +use crate::coin::Coin; +use crate::format_err; +use crate::state::State; +use crate::{Gateway, MixNode}; +use cosmwasm_std::Coin as CosmWasmCoin; +use std::convert::TryInto; +use std::sync::Arc; +use tokio::sync::RwLock; + +#[tauri::command] +pub async fn bond_gateway( + gateway: Gateway, + bond: Coin, + state: tauri::State<'_, Arc>>, +) -> Result<(), String> { + let r_state = state.read().await; + let bond: CosmWasmCoin = match bond.try_into() { + Ok(b) => b, + Err(e) => return Err(format_err!(e)), + }; + let client = r_state.client()?; + match client.bond_gateway(gateway, bond).await { + Ok(_result) => Ok(()), + Err(e) => Err(format_err!(e)), + } +} + +#[tauri::command] +pub async fn unbond_gateway(state: tauri::State<'_, Arc>>) -> Result<(), String> { + let r_state = state.read().await; + let client = r_state.client()?; + match client.unbond_gateway().await { + Ok(_result) => Ok(()), + Err(e) => Err(format_err!(e)), + } +} + +#[tauri::command] +pub async fn unbond_mixnode(state: tauri::State<'_, Arc>>) -> Result<(), String> { + let r_state = state.read().await; + let client = r_state.client()?; + match client.unbond_mixnode().await { + Ok(_result) => Ok(()), + Err(e) => Err(format_err!(e)), + } +} + +#[tauri::command] +pub async fn bond_mixnode( + mixnode: MixNode, + bond: Coin, + state: tauri::State<'_, Arc>>, +) -> Result<(), String> { + let r_state = state.read().await; + let bond: CosmWasmCoin = match bond.try_into() { + Ok(b) => b, + Err(e) => return Err(format_err!(e)), + }; + let client = r_state.client()?; + match client.bond_mixnode(mixnode, bond).await { + Ok(_result) => Ok(()), + Err(e) => Err(format_err!(e)), + } +} diff --git a/tauri-wallet/src-tauri/src/operations/delegate.rs b/tauri-wallet/src-tauri/src/operations/delegate.rs new file mode 100644 index 0000000000..9438b5e7c6 --- /dev/null +++ b/tauri-wallet/src-tauri/src/operations/delegate.rs @@ -0,0 +1,94 @@ +use crate::coin::Coin; +use crate::format_err; +use crate::state::State; +use cosmwasm_std::Coin as CosmWasmCoin; +use serde::{Deserialize, Serialize}; +use std::convert::TryInto; +use std::sync::Arc; +use tokio::sync::RwLock; +use ts_rs::TS; + +#[derive(TS, Serialize, Deserialize)] +pub struct DelegationResult { + source_address: String, + target_address: String, + amount: Option, +} + +#[tauri::command] +pub async fn delegate_to_mixnode( + identity: &str, + amount: Coin, + state: tauri::State<'_, Arc>>, +) -> 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(DelegationResult { + source_address: client.address().to_string(), + target_address: identity.to_string(), + amount: Some(bond.into()), + }), + Err(e) => Err(format_err!(e)), + } +} + +#[tauri::command] +pub async fn undelegate_from_mixnode( + identity: &str, + state: tauri::State<'_, Arc>>, +) -> Result { + let r_state = state.read().await; + let client = r_state.client()?; + match client.remove_mixnode_delegation(identity).await { + Ok(_result) => Ok(DelegationResult { + source_address: client.address().to_string(), + target_address: identity.to_string(), + amount: None, + }), + Err(e) => Err(format_err!(e)), + } +} + +#[tauri::command] +pub async fn delegate_to_gateway( + identity: &str, + amount: Coin, + state: tauri::State<'_, Arc>>, +) -> 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_gateway(identity, &bond).await { + Ok(_result) => Ok(DelegationResult { + source_address: client.address().to_string(), + target_address: identity.to_string(), + amount: Some(bond.into()), + }), + Err(e) => Err(format_err!(e)), + } +} + +#[tauri::command] +pub async fn undelegate_from_gateway( + identity: &str, + state: tauri::State<'_, Arc>>, +) -> Result { + let r_state = state.read().await; + let client = r_state.client()?; + match client.remove_gateway_delegation(identity).await { + Ok(_result) => Ok(DelegationResult { + source_address: client.address().to_string(), + target_address: identity.to_string(), + amount: None, + }), + Err(e) => Err(format_err!(e)), + } +} diff --git a/tauri-wallet/src-tauri/src/operations/mod.rs b/tauri-wallet/src-tauri/src/operations/mod.rs new file mode 100644 index 0000000000..47f4788d6c --- /dev/null +++ b/tauri-wallet/src-tauri/src/operations/mod.rs @@ -0,0 +1,5 @@ +pub mod account; +pub mod admin; +pub mod bond; +pub mod delegate; +pub mod send; diff --git a/tauri-wallet/src-tauri/src/operations/send.rs b/tauri-wallet/src-tauri/src/operations/send.rs new file mode 100644 index 0000000000..bf223a9c39 --- /dev/null +++ b/tauri-wallet/src-tauri/src/operations/send.rs @@ -0,0 +1,69 @@ +use crate::coin::Coin; +use crate::format_err; +use crate::state::State; +use serde::{Deserialize, Serialize}; +use std::convert::TryInto; +use std::str::FromStr; +use std::sync::Arc; +use tendermint_rpc::endpoint::broadcast::tx_commit::Response; +use tokio::sync::RwLock; +use ts_rs::TS; +use validator_client::nymd::{AccountId, CosmosCoin}; + +#[derive(Deserialize, Serialize, TS)] +pub struct TauriTxResult { + code: u32, + gas_wanted: u64, + gas_used: u64, + block_height: u64, + details: TransactionDetails, +} + +#[derive(Deserialize, Serialize, TS)] +pub struct TransactionDetails { + from_address: String, + to_address: String, + amount: Coin, +} + +impl TauriTxResult { + fn new(t: Response, details: TransactionDetails) -> TauriTxResult { + TauriTxResult { + code: t.check_tx.code.value(), + gas_wanted: t.check_tx.gas_wanted.value(), + gas_used: t.check_tx.gas_used.value(), + block_height: t.height.value(), + details, + } + } +} + +#[tauri::command] +pub async fn send( + address: &str, + amount: Coin, + memo: String, + state: tauri::State<'_, Arc>>, +) -> Result { + let address = match AccountId::from_str(address) { + Ok(addy) => addy, + Err(e) => return Err(format_err!(e)), + }; + let cosmos_amount: CosmosCoin = match amount.clone().try_into() { + Ok(b) => b, + Err(e) => return Err(format_err!(e)), + }; + let r_state = state.read().await; + let client = r_state.client()?; + match client.send(&address, vec![cosmos_amount], memo).await { + Ok(result) => Ok(TauriTxResult::new( + result, + TransactionDetails { + from_address: client.address().to_string(), + to_address: address.to_string(), + amount, + }, + )), + Err(e) => Err(format_err!(e)), + } +} diff --git a/tauri-wallet/src-tauri/src/utils.rs b/tauri-wallet/src-tauri/src/utils.rs new file mode 100644 index 0000000000..21a048a60c --- /dev/null +++ b/tauri-wallet/src-tauri/src/utils.rs @@ -0,0 +1,54 @@ +use crate::coin::{Coin, Denom}; +use crate::format_err; +use crate::state::State; +use crate::Operation; +use std::sync::Arc; +use tokio::sync::RwLock; + +#[tauri::command] +pub fn major_to_minor(amount: &str) -> Result { + let coin = Coin::new(amount, &Denom::Major); + Ok(coin.to_minor()) +} + +#[tauri::command] +pub fn minor_to_major(amount: &str) -> Result { + let coin = Coin::new(amount, &Denom::Minor); + Ok(coin.to_major()) +} + +#[tauri::command] +pub async fn owns_mixnode(state: tauri::State<'_, Arc>>) -> Result { + let r_state = state.read().await; + let client = r_state.client()?; + match client.owns_mixnode(client.address()).await { + Ok(o) => Ok(o), + Err(e) => Err(format_err!(e)), + } +} + +#[tauri::command] +pub async fn owns_gateway(state: tauri::State<'_, Arc>>) -> Result { + let r_state = state.read().await; + let client = r_state.client()?; + match client.owns_gateway(client.address()).await { + Ok(o) => Ok(o), + Err(e) => Err(format_err!(e)), + } +} + +#[tauri::command] +pub async fn get_fee( + operation: Operation, + state: tauri::State<'_, Arc>>, +) -> Result { + let r_state = state.read().await; + let client = r_state.client()?; + let fee = client.get_fee(operation); + let mut coin = Coin::new("0", &Denom::Major); + for f in fee.amount { + coin = coin + f.into(); + } + + Ok(coin) +} diff --git a/tauri-wallet/src/types/rust/account.ts b/tauri-wallet/src/types/rust/account.ts new file mode 100644 index 0000000000..d826983913 --- /dev/null +++ b/tauri-wallet/src/types/rust/account.ts @@ -0,0 +1,8 @@ +import { Denom } from "./denom"; + +export interface Account { + contract_address: string; + client_address: string; + denom: Denom; + mnemonmic: string | null; +} \ No newline at end of file diff --git a/tauri-wallet/src/types/rust/stateparams.ts b/tauri-wallet/src/types/rust/stateparams.ts new file mode 100644 index 0000000000..f0c18aa83e --- /dev/null +++ b/tauri-wallet/src/types/rust/stateparams.ts @@ -0,0 +1,10 @@ +export interface TauriStateParams { + epoch_length: number; + minimum_mixnode_bond: string; + minimum_gateway_bond: string; + mixnode_bond_reward_rate: string; + gateway_bond_reward_rate: string; + mixnode_delegation_reward_rate: string; + gateway_delegation_reward_rate: string; + mixnode_active_set_size: number; +} \ No newline at end of file