Admin functions, reorganize code
This commit is contained in:
@@ -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::<i64>().unwrap();
|
||||
let rhs_amount = rhs.amount.parse::<i64>().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::<i64>().unwrap();
|
||||
let rhs_amount = rhs.amount.parse::<i64>().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<T: ToString>(amount: T) -> Coin {
|
||||
@@ -212,7 +212,7 @@ impl From<CosmWasmCoin> 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Coin>,
|
||||
}
|
||||
|
||||
#[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<Coin, String> {
|
||||
let coin = Coin::new(amount, &Denom::Major);
|
||||
Ok(coin.to_minor())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn minor_to_major(amount: &str) -> Result<Coin, String> {
|
||||
let coin = Coin::new(amount, &Denom::Minor);
|
||||
Ok(coin.to_major())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn connect_with_mnemonic(
|
||||
mnemonic: String,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<HashMap<&str, String>, 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<RwLock<State>>>) -> Result<Balance, String> {
|
||||
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<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),
|
||||
Err(e) => Err(format_err!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
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),
|
||||
Err(e) => Err(format_err!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
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]
|
||||
async fn bond_mixnode(
|
||||
mixnode: MixNode,
|
||||
bond: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> 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<RwLock<State>>>,
|
||||
) -> Result<DelegationResult, String> {
|
||||
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<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]
|
||||
async fn delegate_to_gateway(
|
||||
identity: &str,
|
||||
amount: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<DelegationResult, String> {
|
||||
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<RwLock<State>>>,
|
||||
) -> Result<DelegationResult, String> {
|
||||
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<RwLock<State>>>,
|
||||
) -> 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<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]
|
||||
async fn send(
|
||||
address: &str,
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn get_fee(
|
||||
operation: Operation,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Coin, String> {
|
||||
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<RwLock<State>>>,
|
||||
) -> Result<HashMap<&str, String>, 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<SigningNymdClient> {
|
||||
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"
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<C>(Arc<RwLock<validator_client::Client<C>>>);
|
||||
|
||||
impl Client<QueryNymdClient> {
|
||||
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<SigningNymdClient> {
|
||||
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<C> Client<C> {
|
||||
pub(crate) async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
self.0.read().await.get_all_nymd_mixnodes().await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_gateways(&self) -> Result<Vec<GatewayBond>, 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!()
|
||||
}
|
||||
}
|
||||
@@ -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<String>,
|
||||
}
|
||||
|
||||
#[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<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;
|
||||
{
|
||||
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<RwLock<State>>>) -> Result<Balance, String> {
|
||||
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<RwLock<State>>>,
|
||||
) -> Result<Account, String> {
|
||||
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<SigningNymdClient> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
@@ -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<StateParams> 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<TauriStateParams> for StateParams {
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
|
||||
fn try_from(p: TauriStateParams) -> Result<StateParams, Self::Error> {
|
||||
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<RwLock<State>>>,
|
||||
) -> Result<TauriStateParams, String> {
|
||||
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<RwLock<State>>>,
|
||||
) -> Result<TauriStateParams, String> {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
@@ -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<RwLock<State>>>,
|
||||
) -> 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<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,
|
||||
bond: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> 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)),
|
||||
}
|
||||
}
|
||||
@@ -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<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 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<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 delegate_to_gateway(
|
||||
identity: &str,
|
||||
amount: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<DelegationResult, String> {
|
||||
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<RwLock<State>>>,
|
||||
) -> Result<DelegationResult, String> {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod account;
|
||||
pub mod admin;
|
||||
pub mod bond;
|
||||
pub mod delegate;
|
||||
pub mod send;
|
||||
@@ -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<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)),
|
||||
}
|
||||
}
|
||||
@@ -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<Coin, String> {
|
||||
let coin = Coin::new(amount, &Denom::Major);
|
||||
Ok(coin.to_minor())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn minor_to_major(amount: &str) -> Result<Coin, String> {
|
||||
let coin = Coin::new(amount, &Denom::Minor);
|
||||
Ok(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),
|
||||
Err(e) => Err(format_err!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[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),
|
||||
Err(e) => Err(format_err!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_fee(
|
||||
operation: Operation,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Coin, String> {
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Denom } from "./denom";
|
||||
|
||||
export interface Account {
|
||||
contract_address: string;
|
||||
client_address: string;
|
||||
denom: Denom;
|
||||
mnemonmic: string | null;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user