Feature/vesting to wallet (#954)

* Better code structure, proper error handling, VestingSigningClient for wallet

* Add vesting contract queries to wallet

* Address review comments

Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>
This commit is contained in:
Drazen Urch
2021-12-14 12:25:48 +01:00
committed by GitHub
parent 8f152d42f0
commit 305864aa0f
25 changed files with 561 additions and 325 deletions
-4
View File
@@ -29,7 +29,3 @@ fmt-contracts:
fmt-wallet:
cargo fmt --manifest-path nym-wallet/Cargo.toml --all
@@ -636,7 +636,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Client {
rpc_client: HttpClient,
signer: DirectSecp256k1HdWallet,
@@ -52,8 +52,8 @@ pub trait VestingQueryClient {
async fn delegated_vesting(
&self,
block_time: Option<Timestamp>,
vesting_account_address: &str,
block_time: Option<Timestamp>,
) -> Result<Coin, NymdError>;
}
@@ -162,8 +162,8 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
async fn delegated_vesting(
&self,
block_time: Option<Timestamp>,
vesting_account_address: &str,
block_time: Option<Timestamp>,
) -> Result<Coin, NymdError> {
let request = VestingQueryMsg::GetDelegatedVesting {
vesting_account_address: vesting_account_address.to_string(),
@@ -8,7 +8,7 @@ use crate::nymd::fee::helpers::Operation;
use crate::nymd::{cosmwasm_coin_to_cosmos_coin, NymdClient};
use async_trait::async_trait;
use cosmwasm_std::Coin;
use mixnet_contract::{Gateway, IdentityKey, MixNode};
use mixnet_contract::{Gateway, IdentityKey, IdentityKeyRef, MixNode};
use vesting_contract::messages::ExecuteMsg as VestingExecuteMsg;
#[async_trait]
@@ -16,8 +16,8 @@ pub trait VestingSigningClient {
async fn vesting_bond_gateway(
&self,
gateway: Gateway,
pledge: Coin,
owner_signature: &str,
pledge: Coin,
) -> Result<ExecuteResult, NymdError>;
async fn vesting_unbond_gateway(&self) -> Result<ExecuteResult, NymdError>;
@@ -31,8 +31,8 @@ pub trait VestingSigningClient {
async fn vesting_bond_mixnode(
&self,
mix_node: MixNode,
pledge: Coin,
owner_signature: &str,
pledge: Coin,
) -> Result<ExecuteResult, NymdError>;
async fn vesting_unbond_mixnode(&self) -> Result<ExecuteResult, NymdError>;
@@ -51,15 +51,15 @@ pub trait VestingSigningClient {
amount: Coin,
) -> Result<ExecuteResult, NymdError>;
async fn vesting_delegate_to_mixnode(
async fn vesting_delegate_to_mixnode<'a>(
&self,
mix_identity: IdentityKey,
amount: Coin,
mix_identity: IdentityKeyRef<'a>,
amount: &Coin,
) -> Result<ExecuteResult, NymdError>;
async fn vesting_undelegate_from_mixnode(
async fn vesting_undelegate_from_mixnode<'a>(
&self,
mix_identity: IdentityKey,
mix_identity: IdentityKeyRef<'a>,
) -> Result<ExecuteResult, NymdError>;
async fn create_periodic_vesting_account(
@@ -75,8 +75,8 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
async fn vesting_bond_gateway(
&self,
gateway: Gateway,
pledge: Coin,
owner_signature: &str,
pledge: Coin,
) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::BondGateway);
let req = VestingExecuteMsg::BondGateway {
@@ -135,8 +135,8 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
async fn vesting_bond_mixnode(
&self,
mix_node: MixNode,
pledge: Coin,
owner_signature: &str,
pledge: Coin,
) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::BondMixnode);
let req = VestingExecuteMsg::BondMixnode {
@@ -230,13 +230,15 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
)
.await
}
async fn vesting_delegate_to_mixnode(
async fn vesting_delegate_to_mixnode<'a>(
&self,
mix_identity: IdentityKey,
amount: Coin,
mix_identity: IdentityKeyRef<'a>,
amount: &Coin,
) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::DelegateToMixnode);
let req = VestingExecuteMsg::DelegateToMixnode { mix_identity };
let req = VestingExecuteMsg::DelegateToMixnode {
mix_identity: mix_identity.into(),
};
self.client
.execute(
self.address(),
@@ -244,16 +246,18 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
&req,
fee,
"VestingContract::DeledateToMixnode",
vec![cosmwasm_coin_to_cosmos_coin(amount)],
vec![cosmwasm_coin_to_cosmos_coin(amount.to_owned())],
)
.await
}
async fn vesting_undelegate_from_mixnode(
async fn vesting_undelegate_from_mixnode<'a>(
&self,
mix_identity: IdentityKey,
mix_identity: IdentityKeyRef<'a>,
) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::UndelegateFromMixnode);
let req = VestingExecuteMsg::UndelegateFromMixnode { mix_identity };
let req = VestingExecuteMsg::UndelegateFromMixnode {
mix_identity: mix_identity.into(),
};
self.client
.execute(
self.address(),
@@ -10,7 +10,7 @@ use cosmrs::tx::SignDoc;
use cosmrs::{tx, AccountId};
/// Derivation information required to derive a keypair and an address from a mnemonic.
#[derive(Debug)]
#[derive(Debug, Clone)]
struct Secp256k1Derivation {
hd_path: DerivationPath,
prefix: String,
@@ -40,7 +40,7 @@ impl AccountData {
type Secp256k1Keypair = (SigningKey, PublicKey);
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct DirectSecp256k1HdWallet {
/// Base secret
secret: bip39::Mnemonic,
+1
View File
@@ -2709,6 +2709,7 @@ dependencies = [
"cosmwasm-std",
"credentials",
"dirs",
"eyre",
"mixnet-contract",
"rand 0.6.5",
"serde",
+1
View File
@@ -26,6 +26,7 @@ thiserror = "1.0"
tendermint-rpc = "0.23.0"
url = "2.0"
rand = "0.6.5"
eyre = "0.6.5"
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev="e5a1872083abb3d88fa62dda966e7f5408deba58", features = ["rpc", "bip32", "cosmwasm"] }
+10 -14
View File
@@ -1,5 +1,6 @@
// This should be moved out of the wallet, and used as a primary coin type throughout the codebase
use crate::error::BackendError;
use ::config::defaults::DENOM;
use cosmrs::Decimal;
use cosmrs::Denom as CosmosDenom;
@@ -12,8 +13,6 @@ use std::ops::{Add, Sub};
use std::str::FromStr;
use validator_client::nymd::{CosmosCoin, GasPrice};
use crate::format_err;
#[cfg_attr(test, derive(ts_rs::TS))]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub enum Denom {
@@ -33,19 +32,16 @@ impl fmt::Display for Denom {
}
impl FromStr for Denom {
type Err = String;
type Err = BackendError;
fn from_str(s: &str) -> Result<Denom, String> {
fn from_str(s: &str) -> Result<Denom, BackendError> {
let s = s.to_lowercase();
if s == DENOM.to_lowercase() || s == "minor" {
Ok(Denom::Minor)
} else if s == DENOM[1..].to_lowercase() || s == "major" {
Ok(Denom::Major)
} else {
Err(format_err!(format!(
"{} is not a valid denomination string",
s
)))
Err(BackendError::InvalidDenom(s))
}
}
}
@@ -169,9 +165,9 @@ impl Coin {
}
impl TryFrom<Coin> for CosmWasmCoin {
type Error = String;
type Error = BackendError;
fn try_from(coin: Coin) -> Result<CosmWasmCoin, String> {
fn try_from(coin: Coin) -> Result<CosmWasmCoin, Self::Error> {
Ok(CosmWasmCoin::new(
Uint128::try_from(coin.amount.as_str()).unwrap().u128(),
coin.denom.to_string(),
@@ -180,15 +176,15 @@ impl TryFrom<Coin> for CosmWasmCoin {
}
impl TryFrom<Coin> for CosmosCoin {
type Error = String;
type Error = BackendError;
fn try_from(coin: Coin) -> Result<CosmosCoin, String> {
fn try_from(coin: Coin) -> Result<CosmosCoin, BackendError> {
match Decimal::from_str(&coin.amount) {
Ok(d) => Ok(CosmosCoin {
amount: d,
denom: CosmosDenom::from_str(&coin.denom.to_string()).unwrap(),
denom: CosmosDenom::from_str(&coin.denom.to_string())?,
}),
Err(e) => Err(format_err!(e)),
Err(e) => Err(e.into()),
}
}
}
+29 -3
View File
@@ -1,21 +1,47 @@
use serde::{Serialize, Serializer};
use thiserror::Error;
use validator_client::nymd::error::NymdError;
#[derive(Error, Debug)]
pub enum BackendError {
#[error("Error parsing bip39 mnemonic")]
#[error("{source}")]
Bip39Error {
#[from]
source: bip39::Error,
},
#[error("Error parsing into tendermint Url")]
#[error("{source}")]
TendermintError {
#[from]
source: tendermint_rpc::Error,
},
#[error("Error getting balances")]
#[error("{source}")]
NymdError {
#[from]
source: NymdError,
},
#[error("{source}")]
CosmwasmStd {
#[from]
source: cosmwasm_std::StdError,
},
#[error("{source}")]
ErrorReport {
#[from]
source: eyre::Report,
},
#[error("Client has not been initialized yet, connect with mnemonic to initialize")]
ClientNotInitialized,
#[error("No balance available for address {0}")]
NoBalance(String),
#[error("{0} is not a valid denomination string")]
InvalidDenom(String),
}
impl Serialize for BackendError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
+43 -42
View File
@@ -18,47 +18,48 @@ mod state;
mod utils;
use crate::menu::AddDefaultSubmenus;
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::operations::mixnet;
use crate::operations::vesting;
use crate::state::State;
#[cfg(test)]
use crate::coin::{Coin, Denom};
#[macro_export]
macro_rules! format_err {
($e:expr) => {
format!("line {}: {}", line!(), $e)
};
}
fn main() {
tauri::Builder::default()
.manage(Arc::new(RwLock::new(State::default())))
.invoke_handler(tauri::generate_handler![
connect_with_mnemonic,
get_balance,
minor_to_major,
major_to_minor,
owns_gateway,
owns_mixnode,
bond_mixnode,
unbond_mixnode,
bond_gateway,
unbond_gateway,
delegate_to_mixnode,
undelegate_from_mixnode,
send,
create_new_account,
get_approximate_fee,
get_contract_settings,
update_contract_settings,
get_reverse_mix_delegations_paged,
mixnet::account::connect_with_mnemonic,
mixnet::account::create_new_account,
mixnet::account::get_balance,
mixnet::admin::get_contract_settings,
mixnet::admin::update_contract_settings,
mixnet::bond::bond_gateway,
mixnet::bond::bond_mixnode,
mixnet::bond::unbond_gateway,
mixnet::bond::unbond_mixnode,
mixnet::delegate::delegate_to_mixnode,
mixnet::delegate::get_reverse_mix_delegations_paged,
mixnet::delegate::undelegate_from_mixnode,
mixnet::send::send,
utils::get_approximate_fee,
utils::major_to_minor,
utils::minor_to_major,
utils::owns_gateway,
utils::owns_mixnode,
vesting::bond::vesting_bond_gateway,
vesting::bond::vesting_bond_mixnode,
vesting::bond::vesting_unbond_gateway,
vesting::bond::vesting_unbond_mixnode,
vesting::delegate::vesting_delegate_to_mixnode,
vesting::delegate::vesting_undelegate_from_mixnode,
vesting::queries::locked_coins,
vesting::queries::spendable_coins,
vesting::queries::vesting_coins,
vesting::queries::vested_coins,
vesting::queries::vesting_start_time,
vesting::queries::vesting_end_time,
vesting::queries::original_vesting,
vesting::queries::delegated_free,
vesting::queries::delegated_vesting,
])
.menu(Menu::new().add_default_app_submenu_if_macos())
.run(tauri::generate_context!())
@@ -69,15 +70,15 @@ fn main() {
mod test {
ts_rs::export! {
mixnet_contract::MixNode => "../src/types/rust/mixnode.ts",
crate::Coin => "../src/types/rust/coin.ts",
crate::Balance => "../src/types/rust/balance.ts",
crate::coin::Coin => "../src/types/rust/coin.ts",
crate::mixnet::account::Balance => "../src/types/rust/balance.ts",
mixnet_contract::Gateway => "../src/types/rust/gateway.ts",
crate::TauriTxResult => "../src/types/rust/tauritxresult.ts",
crate::TransactionDetails => "../src/types/rust/transactiondetails.ts",
crate::mixnet::send::TauriTxResult => "../src/types/rust/tauritxresult.ts",
crate::mixnet::send::TransactionDetails => "../src/types/rust/transactiondetails.ts",
validator_client::nymd::fee::helpers::Operation => "../src/types/rust/operation.ts",
crate::Denom => "../src/types/rust/denom.ts",
crate::DelegationResult => "../src/types/rust/delegationresult.ts",
crate::Account => "../src/types/rust/account.ts",
crate::TauriContractStateParams => "../src/types/rust/stateparams.ts"
crate::coin::Denom => "../src/types/rust/denom.ts",
crate::utils::DelegationResult => "../src/types/rust/delegationresult.ts",
crate::mixnet::account::Account => "../src/types/rust/account.ts",
crate::mixnet::admin::TauriContractStateParams => "../src/types/rust/stateparams.ts"
}
}
@@ -1,66 +0,0 @@
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,
pledge: Coin,
owner_signature: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), String> {
let r_state = state.read().await;
let pledge: CosmWasmCoin = match pledge.try_into() {
Ok(b) => b,
Err(e) => return Err(format_err!(e)),
};
let client = r_state.client()?;
match client.bond_gateway(gateway, owner_signature, pledge).await {
Ok(_result) => Ok(()),
Err(e) => Err(format_err!(e)),
}
}
#[tauri::command]
pub async fn unbond_gateway(state: tauri::State<'_, Arc<RwLock<State>>>) -> 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<RwLock<State>>>) -> 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,
owner_signature: String,
pledge: Coin,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), String> {
let r_state = state.read().await;
let pledge: CosmWasmCoin = match pledge.try_into() {
Ok(b) => b,
Err(e) => return Err(format_err!(e)),
};
let client = r_state.client()?;
match client.bond_mixnode(mixnode, owner_signature, pledge).await {
Ok(_result) => Ok(()),
Err(e) => Err(format_err!(e)),
}
}
@@ -1,68 +0,0 @@
use crate::coin::Coin;
use crate::format_err;
use crate::state::State;
use cosmwasm_std::Coin as CosmWasmCoin;
use mixnet_contract::PagedDelegatorDelegationsResponse;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::sync::Arc;
use tokio::sync::RwLock;
#[cfg_attr(test, derive(ts_rs::TS))]
#[derive(Serialize, Deserialize)]
pub struct DelegationResult {
source_address: String,
target_address: String,
amount: Option<Coin>,
}
#[tauri::command]
pub async fn delegate_to_mixnode(
identity: &str,
amount: Coin,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, String> {
let r_state = state.read().await;
let delegation: 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, &delegation).await {
Ok(_result) => Ok(DelegationResult {
source_address: client.address().to_string(),
target_address: identity.to_string(),
amount: Some(delegation.into()),
}),
Err(e) => Err(format_err!(e)),
}
}
#[tauri::command]
pub async fn undelegate_from_mixnode(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, String> {
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 get_reverse_mix_delegations_paged(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<PagedDelegatorDelegationsResponse, String> {
let r_state = state.read().await;
let client = r_state.client()?;
client
.get_delegator_delegations_paged(client.address().to_string(), None, None)
.await
.map_err(|err| format_err!(err))
}
@@ -1,7 +1,7 @@
use crate::client;
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};
@@ -30,26 +30,16 @@ pub struct Balance {
pub async fn connect_with_mnemonic(
mnemonic: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Account, String> {
let mnemonic = match Mnemonic::from_str(&mnemonic) {
Ok(mnemonic) => mnemonic,
Err(e) => return Err(BackendError::from(e).to_string()),
};
let client;
{
) -> Result<Account, BackendError> {
let mnemonic = Mnemonic::from_str(&mnemonic)?;
let client = {
let r_state = state.read().await;
client = _connect_with_mnemonic(mnemonic, &r_state.config());
}
_connect_with_mnemonic(mnemonic, &r_state.config())
};
let contract_address = match client.mixnet_contract_address() {
Ok(address) => address.to_string(),
Err(e) => return Err(format_err!(e)),
};
let contract_address = client.mixnet_contract_address()?.to_string();
let client_address = client.address().to_string();
let denom = match client.denom() {
Ok(denom) => denom,
Err(e) => return Err(format_err!(e)),
};
let denom = client.denom()?;
let account = Account {
contract_address,
@@ -65,10 +55,10 @@ pub async fn connect_with_mnemonic(
}
#[tauri::command]
pub async fn get_balance(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<Balance, String> {
let r_state = state.read().await;
let client = r_state.client()?;
match client.get_balance(client.address()).await {
pub async fn get_balance(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Balance, BackendError> {
match client!(state).get_balance(client!(state).address()).await {
Ok(Some(coin)) => {
let coin = Coin::new(
&coin.amount.to_string(),
@@ -79,18 +69,17 @@ pub async fn get_balance(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<
printable_balance: coin.to_major().to_string(),
})
}
Ok(None) => Err(format!(
"No balance available for address {}",
client.address()
Ok(None) => Err(BackendError::NoBalance(
client!(state).address().to_string(),
)),
Err(e) => Err(BackendError::from(e).to_string()),
Err(e) => Err(BackendError::from(e)),
}
}
#[tauri::command]
pub async fn create_new_account(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Account, String> {
) -> Result<Account, BackendError> {
let rand_mnemonic = random_mnemonic();
let mut client = connect_with_mnemonic(rand_mnemonic.to_string(), state).await?;
client.mnemonic = Some(rand_mnemonic.to_string());
@@ -1,4 +1,5 @@
use crate::format_err;
use crate::client;
use crate::error::BackendError;
use crate::state::State;
use cosmwasm_std::Uint128;
use mixnet_contract::ContractStateParams;
@@ -30,7 +31,7 @@ impl From<ContractStateParams> for TauriContractStateParams {
}
impl TryFrom<TauriContractStateParams> for ContractStateParams {
type Error = Box<dyn std::error::Error>;
type Error = BackendError;
fn try_from(p: TauriContractStateParams) -> Result<ContractStateParams, Self::Error> {
Ok(ContractStateParams {
@@ -46,31 +47,18 @@ impl TryFrom<TauriContractStateParams> for ContractStateParams {
#[tauri::command]
pub async fn get_contract_settings(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<TauriContractStateParams, String> {
let r_state = state.read().await;
let client = r_state.client()?;
match client.get_contract_settings().await {
Ok(params) => Ok(params.into()),
Err(e) => Err(format_err!(e)),
}
) -> Result<TauriContractStateParams, BackendError> {
Ok(client!(state).get_contract_settings().await?.into())
}
#[tauri::command]
pub async fn update_contract_settings(
params: TauriContractStateParams,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<TauriContractStateParams, String> {
let r_state = state.read().await;
let client = r_state.client()?;
let mixnet_contract_settings_params: ContractStateParams = match params.try_into() {
Ok(mixnet_contract_settings_params) => mixnet_contract_settings_params,
Err(e) => return Err(format_err!(e)),
};
match client
) -> Result<TauriContractStateParams, BackendError> {
let mixnet_contract_settings_params: ContractStateParams = params.try_into()?;
client!(state)
.update_contract_settings(mixnet_contract_settings_params.clone())
.await
{
Ok(_) => Ok(mixnet_contract_settings_params.into()),
Err(e) => Err(format_err!(e)),
}
.await?;
Ok(mixnet_contract_settings_params.into())
}
@@ -0,0 +1,50 @@
use crate::client;
use crate::coin::Coin;
use crate::error::BackendError;
use crate::state::State;
use crate::{Gateway, MixNode};
use std::convert::TryInto;
use std::sync::Arc;
use tokio::sync::RwLock;
#[tauri::command]
pub async fn bond_gateway(
gateway: Gateway,
pledge: Coin,
owner_signature: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state)
.bond_gateway(gateway, owner_signature, pledge.try_into()?)
.await?;
Ok(())
}
#[tauri::command]
pub async fn unbond_gateway(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state).unbond_gateway().await?;
Ok(())
}
#[tauri::command]
pub async fn unbond_mixnode(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state).unbond_mixnode().await?;
Ok(())
}
#[tauri::command]
pub async fn bond_mixnode(
mixnode: MixNode,
owner_signature: String,
pledge: Coin,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state)
.bond_mixnode(mixnode, owner_signature, pledge.try_into()?)
.await?;
Ok(())
}
@@ -0,0 +1,51 @@
use crate::client;
use crate::coin::Coin;
use crate::error::BackendError;
use crate::state::State;
use crate::utils::DelegationResult;
use cosmwasm_std::Coin as CosmWasmCoin;
use mixnet_contract::PagedDelegatorDelegationsResponse;
use std::convert::TryInto;
use std::sync::Arc;
use tokio::sync::RwLock;
#[tauri::command]
pub async fn delegate_to_mixnode(
identity: &str,
amount: Coin,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, BackendError> {
let delegation: CosmWasmCoin = amount.try_into()?;
client!(state)
.delegate_to_mixnode(identity, &delegation)
.await?;
Ok(DelegationResult::new(
&client!(state).address().to_string(),
identity,
Some(delegation.into()),
))
}
#[tauri::command]
pub async fn undelegate_from_mixnode(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, BackendError> {
client!(state).remove_mixnode_delegation(identity).await?;
Ok(DelegationResult::new(
&client!(state).address().to_string(),
identity,
None,
))
}
#[tauri::command]
pub async fn get_reverse_mix_delegations_paged(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<PagedDelegatorDelegationsResponse, BackendError> {
Ok(
client!(state)
.get_delegator_delegations_paged(client!(state).address().to_string(), None, None)
.await?,
)
}
@@ -0,0 +1,5 @@
pub mod account;
pub mod admin;
pub mod bond;
pub mod delegate;
pub mod send;
@@ -1,5 +1,6 @@
use crate::client;
use crate::coin::Coin;
use crate::format_err;
use crate::error::BackendError;
use crate::state::State;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
@@ -47,26 +48,18 @@ pub async fn send(
amount: Coin,
memo: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<TauriTxResult, String> {
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)),
}
) -> Result<TauriTxResult, BackendError> {
let address = AccountId::from_str(address)?;
let cosmos_amount: CosmosCoin = amount.clone().try_into()?;
let result = client!(state)
.send(&address, vec![cosmos_amount], memo)
.await?;
Ok(TauriTxResult::new(
result,
TransactionDetails {
from_address: client!(state).address().to_string(),
to_address: address.to_string(),
amount,
},
))
}
+2 -5
View File
@@ -1,5 +1,2 @@
pub mod account;
pub mod admin;
pub mod bond;
pub mod delegate;
pub mod send;
pub mod mixnet;
pub mod vesting;
@@ -0,0 +1,51 @@
use crate::client;
use crate::coin::Coin;
use crate::error::BackendError;
use crate::state::State;
use crate::{Gateway, MixNode};
use std::convert::TryInto;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::VestingSigningClient;
#[tauri::command]
pub async fn vesting_bond_gateway(
gateway: Gateway,
pledge: Coin,
owner_signature: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state)
.vesting_bond_gateway(gateway, &owner_signature, pledge.try_into()?)
.await?;
Ok(())
}
#[tauri::command]
pub async fn vesting_unbond_gateway(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state).vesting_unbond_gateway().await?;
Ok(())
}
#[tauri::command]
pub async fn vesting_unbond_mixnode(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state).vesting_unbond_mixnode().await?;
Ok(())
}
#[tauri::command]
pub async fn vesting_bond_mixnode(
mixnode: MixNode,
owner_signature: String,
pledge: Coin,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state)
.vesting_bond_mixnode(mixnode, &owner_signature, pledge.try_into()?)
.await?;
Ok(())
}
@@ -0,0 +1,42 @@
use crate::client;
use crate::coin::Coin;
use crate::error::BackendError;
use crate::state::State;
use crate::utils::DelegationResult;
use cosmwasm_std::Coin as CosmWasmCoin;
use std::convert::TryInto;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::VestingSigningClient;
#[tauri::command]
pub async fn vesting_delegate_to_mixnode(
identity: &str,
amount: Coin,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, BackendError> {
let delegation: CosmWasmCoin = amount.try_into()?;
client!(state)
.vesting_delegate_to_mixnode(identity, &delegation)
.await?;
Ok(DelegationResult::new(
&client!(state).address().to_string(),
identity,
Some(delegation.into()),
))
}
#[tauri::command]
pub async fn vesting_undelegate_from_mixnode(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, BackendError> {
client!(state)
.vesting_undelegate_from_mixnode(identity)
.await?;
Ok(DelegationResult::new(
&client!(state).address().to_string(),
identity,
None,
))
}
@@ -0,0 +1,3 @@
pub mod bond;
pub mod delegate;
pub mod queries;
@@ -0,0 +1,146 @@
use crate::client;
use crate::coin::Coin;
use crate::error::BackendError;
use crate::state::State;
use cosmwasm_std::Timestamp;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::VestingQueryClient;
#[tauri::command]
pub async fn locked_coins(
address: &str,
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Coin, BackendError> {
Ok(
client!(state)
.locked_coins(address, block_time.map(Timestamp::from_seconds))
.await?
.into(),
)
}
#[tauri::command]
pub async fn spendable_coins(
vesting_account_address: &str,
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Coin, BackendError> {
Ok(
client!(state)
.spendable_coins(
vesting_account_address,
block_time.map(Timestamp::from_seconds),
)
.await?
.into(),
)
}
#[tauri::command]
pub async fn vested_coins(
vesting_account_address: &str,
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Coin, BackendError> {
Ok(
client!(state)
.vested_coins(
vesting_account_address,
block_time.map(Timestamp::from_seconds),
)
.await?
.into(),
)
}
#[tauri::command]
pub async fn vesting_coins(
vesting_account_address: &str,
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Coin, BackendError> {
Ok(
client!(state)
.vesting_coins(
vesting_account_address,
block_time.map(Timestamp::from_seconds),
)
.await?
.into(),
)
}
#[tauri::command]
pub async fn vesting_start_time(
vesting_account_address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<u64, BackendError> {
Ok(
client!(state)
.vesting_start_time(vesting_account_address)
.await?
.seconds(),
)
}
#[tauri::command]
pub async fn vesting_end_time(
vesting_account_address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<u64, BackendError> {
Ok(
client!(state)
.vesting_end_time(vesting_account_address)
.await?
.seconds(),
)
}
#[tauri::command]
pub async fn original_vesting(
vesting_account_address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Coin, BackendError> {
Ok(
client!(state)
.original_vesting(vesting_account_address)
.await?
.into(),
)
}
#[tauri::command]
pub async fn delegated_free(
vesting_account_address: &str,
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Coin, BackendError> {
Ok(
client!(state)
.delegated_free(
vesting_account_address,
block_time.map(Timestamp::from_seconds),
)
.await?
.into(),
)
}
#[tauri::command]
pub async fn delegated_vesting(
block_time: Option<u64>,
vesting_account_address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Coin, BackendError> {
Ok(
client!(state)
.delegated_vesting(
vesting_account_address,
block_time.map(Timestamp::from_seconds),
)
.await?
.into(),
)
}
+13 -4
View File
@@ -1,4 +1,5 @@
use crate::config::Config;
use crate::error::BackendError;
use validator_client::nymd::{NymdClient, SigningNymdClient};
#[derive(Default)]
@@ -8,10 +9,11 @@ pub struct State {
}
impl State {
pub fn client(&self) -> Result<&NymdClient<SigningNymdClient>, String> {
self.signing_client.as_ref().ok_or_else(|| {
"Client has not been initialized yet, connect with mnemonic to initialize".to_string()
})
pub fn client(&self) -> Result<&NymdClient<SigningNymdClient>, BackendError> {
self
.signing_client
.as_ref()
.ok_or(BackendError::ClientNotInitialized)
}
pub fn config(&self) -> Config {
@@ -22,3 +24,10 @@ impl State {
self.signing_client = Some(signing_client)
}
}
#[macro_export]
macro_rules! client {
($state:ident) => {
$state.read().await.client()?
};
}
+45 -24
View File
@@ -1,40 +1,46 @@
use crate::client;
use crate::coin::{Coin, Denom};
use crate::format_err;
use crate::error::BackendError;
use crate::state::State;
use crate::Operation;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
#[tauri::command]
pub fn major_to_minor(amount: &str) -> Result<Coin, String> {
pub fn major_to_minor(amount: &str) -> Coin {
let coin = Coin::new(amount, &Denom::Major);
Ok(coin.to_minor())
coin.to_minor()
}
#[tauri::command]
pub fn minor_to_major(amount: &str) -> Result<Coin, String> {
pub fn minor_to_major(amount: &str) -> Coin {
let coin = Coin::new(amount, &Denom::Minor);
Ok(coin.to_major())
coin.to_major()
}
#[tauri::command]
pub async fn owns_mixnode(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<bool, String> {
let r_state = state.read().await;
let client = r_state.client()?;
match client.owns_mixnode(client.address()).await {
Ok(o) => Ok(o.is_some()),
Err(e) => Err(format_err!(e)),
}
pub async fn owns_mixnode(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<bool, BackendError> {
Ok(
client!(state)
.owns_mixnode(client!(state).address())
.await?
.is_some(),
)
}
#[tauri::command]
pub async fn owns_gateway(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<bool, String> {
let r_state = state.read().await;
let client = r_state.client()?;
match client.owns_gateway(client.address()).await {
Ok(o) => Ok(o.is_some()),
Err(e) => Err(format_err!(e)),
}
pub async fn owns_gateway(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<bool, BackendError> {
Ok(
client!(state)
.owns_gateway(client!(state).address())
.await?
.is_some(),
)
}
// NOTE: this uses OUTDATED defaults that might have no resemblance with the reality
@@ -43,14 +49,29 @@ pub async fn owns_gateway(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result
pub async fn get_approximate_fee(
operation: Operation,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Coin, String> {
let r_state = state.read().await;
let client = r_state.client()?;
let approximate_fee = operation.default_fee(client.gas_price());
) -> Result<Coin, BackendError> {
let approximate_fee = operation.default_fee(client!(state).gas_price());
let mut coin = Coin::new("0", &Denom::Major);
for f in approximate_fee.amount {
coin = coin + f.into();
}
Ok(coin)
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[derive(Serialize, Deserialize)]
pub struct DelegationResult {
source_address: String,
target_address: String,
amount: Option<Coin>,
}
impl DelegationResult {
pub fn new(source_address: &str, target_address: &str, amount: Option<Coin>) -> DelegationResult {
DelegationResult {
source_address: source_address.to_string(),
target_address: target_address.to_string(),
amount,
}
}
}