Introduced concept of denom details

No longer exposing plain 'DENOM'

Denom registration + conversion

Generating typescript type for DecCoin

'New' API on 'send'

Further WIP work on transforming usages of MajorCurrencyAmount into DecCoin

Further replacements of MajorCurrencyAmount into DecCoin

Attempt at dec-coinifying get_all_mix_delegations

Finished purge of MajorCurrencyAmount

Display for Fee

More unification for conversion methods

Fixed up tests and made clippy happier

Minor post-merge fixes

Removed explicit Arc and RwLock from all tauri commands

Fixed conversion to display coin

More type-restrictive exported denom type

Regenerated rust => ts types
This commit is contained in:
Jędrzej Stuczyński
2022-06-01 13:32:38 +01:00
parent e1b5407613
commit 895edea531
111 changed files with 1713 additions and 1794 deletions
+2 -2
View File
@@ -8,7 +8,7 @@ use url::Url;
use crate::error::Result;
use crate::{MNEMONIC, NYMD_URL};
use network_defaults::{DEFAULT_NETWORK, DENOM, VOUCHER_INFO};
use network_defaults::{DEFAULT_NETWORK, MIX_DENOM, VOUCHER_INFO};
use validator_client::nymd::traits::CoconutBandwidthSigningClient;
use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient};
@@ -34,7 +34,7 @@ impl Client {
encryption_key: String,
fee: Option<Fee>,
) -> Result<String> {
let amount = Coin::new(amount as u128, DENOM.to_string());
let amount = Coin::new(amount as u128, MIX_DENOM.base.to_string());
Ok(self
.nymd_client
.deposit(
@@ -26,7 +26,7 @@ use cosmrs::rpc::{self, HttpClient, Order};
use cosmrs::tendermint::abci::Code as AbciCode;
use cosmrs::tendermint::abci::Transaction;
use cosmrs::tendermint::{abci, block, chain};
use cosmrs::{tx, AccountId, Coin as CosmosCoin, Denom, Tx};
use cosmrs::{tx, AccountId, Coin as CosmosCoin, Tx};
use prost::Message;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
@@ -121,7 +121,7 @@ pub trait CosmWasmClient: rpc::Client {
async fn get_balance(
&self,
address: &AccountId,
search_denom: Denom,
search_denom: String,
) -> Result<Option<Coin>, NymdError> {
let path = Some("/cosmos.bank.v1beta1.Query/Balance".parse().unwrap());
@@ -1,8 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nymd::Coin;
use cosmrs::tx;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
pub mod gas_price;
@@ -14,6 +16,40 @@ pub enum Fee {
Auto(Option<f32>),
}
impl Display for Fee {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Fee::Manual(fee) => {
write!(f, "Fee in manual mode with ")?;
for fee in &fee.amount {
write!(f, "{}{} paid in fees, ", fee.amount, fee.denom)?;
}
write!(f, "{} set as gas limit, ", fee.gas_limit)?;
if let Some(payer) = &fee.payer {
write!(f, "{payer} set as payer, ")?;
}
if let Some(granter) = &fee.granter {
write!(f, "{granter} set as granter")?;
}
Ok(())
}
Fee::Auto(Some(multiplier)) => {
write!(f, "Fee in auto mode with {multiplier} simulated multiplier")
}
Fee::Auto(None) => write!(f, "Fee in auto mode with no custom simulated multiplier"),
}
}
}
impl Fee {
pub fn try_get_manual_amount(&self) -> Option<Vec<Coin>> {
match self {
Fee::Manual(tx_fee) => Some(tx_fee.amount.iter().cloned().map(Into::into).collect()),
Fee::Auto(_) => None,
}
}
}
impl From<tx::Fee> for Fee {
fn from(fee: tx::Fee) -> Self {
Fee::Manual(fee)
@@ -134,7 +134,7 @@ impl NymdClient<SigningNymdClient> {
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
let denom = network.denom();
let denom = network.mix_denom().base;
let client_address = signer
.try_derive_accounts()?
.into_iter()
@@ -176,7 +176,7 @@ impl NymdClient<SigningNymdClient> {
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
let prefix = network.bech32_prefix();
let denom = network.denom();
let denom = network.mix_denom().base;
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic)?;
let client_address = wallet
.try_derive_accounts()?
@@ -338,7 +338,7 @@ impl<C> NymdClient<C> {
pub async fn get_balance(
&self,
address: &AccountId,
denom: Denom,
denom: String,
) -> Result<Option<Coin>, NymdError>
where
C: CosmWasmClient + Sync,
@@ -33,7 +33,7 @@ pub struct Delegation {
pub node_identity: IdentityKey,
pub amount: Coin,
pub block_height: u64,
pub proxy: Option<Addr>, // proxy address used to delegate the funds on behalf of anouther address
pub proxy: Option<Addr>, // proxy address used to delegate the funds on behalf of another address
}
impl Eq for Delegation {}
@@ -37,6 +37,16 @@ pub enum DelegationEvent {
Undelegate(PendingUndelegate),
}
impl DelegationEvent {
pub fn delegation_amount(&self) -> Option<Coin> {
match self {
DelegationEvent::Delegate(delegation) => Some(delegation.amount.clone()),
// I think it would be nice to also expose an amount here to know how much we're undelegating
DelegationEvent::Undelegate(_) => None,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct PendingUndelegate {
mix_identity: IdentityKey,
@@ -1,6 +1,6 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{Coin, Timestamp};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -11,7 +11,7 @@ pub mod events;
pub mod messages;
pub fn one_ucoin() -> Coin {
Coin::new(1, DENOM)
Coin::new(1, MIX_DENOM.base)
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
@@ -28,8 +28,8 @@ pub enum Period {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct PledgeData {
amount: Coin,
block_time: Timestamp,
pub amount: Coin,
pub block_time: Timestamp,
}
impl PledgeData {
@@ -48,9 +48,9 @@ impl PledgeData {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct OriginalVestingResponse {
amount: Coin,
number_of_periods: usize,
period_duration: u64,
pub amount: Coin,
pub number_of_periods: usize,
pub period_duration: u64,
}
impl OriginalVestingResponse {
+22 -9
View File
@@ -5,7 +5,8 @@ use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt, str::FromStr};
use crate::{
DefaultNetworkDetails, ValidatorDetails, MAINNET_DEFAULTS, QA_DEFAULTS, SANDBOX_DEFAULTS,
DefaultNetworkDetails, DenomDetails, DenomDetailsOwned, ValidatorDetails, MAINNET_DEFAULTS,
QA_DEFAULTS, SANDBOX_DEFAULTS,
};
use thiserror::Error;
@@ -24,7 +25,7 @@ pub enum Network {
}
impl Network {
fn details(&self) -> &DefaultNetworkDetails<'_> {
fn details(&self) -> &DefaultNetworkDetails {
match self {
Self::QA => &QA_DEFAULTS,
Self::SANDBOX => &SANDBOX_DEFAULTS,
@@ -36,8 +37,12 @@ impl Network {
self.details().bech32_prefix
}
pub fn denom(&self) -> &str {
self.details().denom
pub fn mix_denom(&self) -> &DenomDetails {
&self.details().mix_denom
}
pub fn stake_denom(&self) -> &DenomDetails {
&self.details().stake_denom
}
pub fn mixnet_contract_address(&self) -> &str {
@@ -97,18 +102,20 @@ impl fmt::Display for Network {
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct NetworkDetails {
bech32_prefix: String,
denom: String,
mix_denom: DenomDetailsOwned,
stake_denom: DenomDetailsOwned,
mixnet_contract_address: String,
vesting_contract_address: String,
bandwidth_claim_contract_address: String,
validators: Vec<ValidatorDetails>,
}
impl From<&DefaultNetworkDetails<'_>> for NetworkDetails {
fn from(details: &DefaultNetworkDetails<'_>) -> Self {
impl From<&DefaultNetworkDetails> for NetworkDetails {
fn from(details: &DefaultNetworkDetails) -> Self {
NetworkDetails {
bech32_prefix: details.bech32_prefix.into(),
denom: details.denom.into(),
mix_denom: details.mix_denom.into(),
stake_denom: details.stake_denom.into(),
mixnet_contract_address: details.mixnet_contract_address.into(),
vesting_contract_address: details.vesting_contract_address.into(),
bandwidth_claim_contract_address: details.bandwidth_claim_contract_address.into(),
@@ -117,6 +124,12 @@ impl From<&DefaultNetworkDetails<'_>> for NetworkDetails {
}
}
impl NetworkDetails {
pub fn base_mix_denom(&self) -> &str {
&self.mix_denom.base
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct SupportedNetworks {
networks: HashMap<Network, NetworkDetails>,
@@ -141,7 +154,7 @@ impl SupportedNetworks {
pub fn denom(&self, network: Network) -> Option<&str> {
self.networks
.get(&network)
.map(|network_details| network_details.denom.as_str())
.map(|network_details| network_details.base_mix_denom())
}
pub fn mixnet_contract_address(&self, network: Network) -> Option<&str> {
+79 -41
View File
@@ -17,24 +17,24 @@ pub mod sandbox;
cfg_if::cfg_if! {
if #[cfg(network = "mainnet")] {
pub const DEFAULT_NETWORK: all::Network = all::Network::MAINNET;
pub const DENOM: &str = mainnet::DENOM;
pub const STAKE_DENOM: &str = mainnet::STAKE_DENOM;
pub const MIX_DENOM: DenomDetails = mainnet::MIX_DENOM;
pub const STAKE_DENOM: DenomDetails = mainnet::STAKE_DENOM;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS;
} else if #[cfg(network = "qa")] {
pub const DEFAULT_NETWORK: all::Network = all::Network::QA;
pub const DENOM: &str = qa::DENOM;
pub const STAKE_DENOM: &str = qa::STAKE_DENOM;
pub const MIX_DENOM: DenomDetails = qa::MIX_DENOM;
pub const STAKE_DENOM: DenomDetails = qa::STAKE_DENOM;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_ERC20_CONTRACT_ADDRESS;
} else if #[cfg(network = "sandbox")] {
pub const DEFAULT_NETWORK: all::Network = all::Network::SANDBOX;
pub const DENOM: &str = sandbox::DENOM;
pub const STAKE_DENOM: &str = sandbox::STAKE_DENOM;
pub const MIX_DENOM: DenomDetails = sandbox::MIX_DENOM;
pub const STAKE_DENOM: DenomDetails = sandbox::STAKE_DENOM;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_ERC20_CONTRACT_ADDRESS;
@@ -45,47 +45,49 @@ cfg_if::cfg_if! {
// future. If we do this, and also get rid of the references we could potentially unify with
// `NetworkDetails`.
#[derive(Debug)]
pub struct DefaultNetworkDetails<'a> {
bech32_prefix: &'a str,
denom: &'a str,
mixnet_contract_address: &'a str,
vesting_contract_address: &'a str,
bandwidth_claim_contract_address: &'a str,
coconut_bandwidth_contract_address: &'a str,
multisig_contract_address: &'a str,
rewarding_validator_address: &'a str,
pub struct DefaultNetworkDetails {
bech32_prefix: &'static str,
mix_denom: DenomDetails,
stake_denom: DenomDetails,
mixnet_contract_address: &'static str,
vesting_contract_address: &'static str,
bandwidth_claim_contract_address: &'static str,
coconut_bandwidth_contract_address: &'static str,
multisig_contract_address: &'static str,
rewarding_validator_address: &'static str,
validators: Vec<ValidatorDetails>,
}
static MAINNET_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
Lazy::new(|| DefaultNetworkDetails {
bech32_prefix: mainnet::BECH32_PREFIX,
denom: mainnet::DENOM,
mixnet_contract_address: mainnet::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: mainnet::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
coconut_bandwidth_contract_address: mainnet::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
multisig_contract_address: mainnet::MULTISIG_CONTRACT_ADDRESS,
rewarding_validator_address: mainnet::REWARDING_VALIDATOR_ADDRESS,
validators: mainnet::validators(),
});
static MAINNET_DEFAULTS: Lazy<DefaultNetworkDetails> = Lazy::new(|| DefaultNetworkDetails {
bech32_prefix: mainnet::BECH32_PREFIX,
mix_denom: mainnet::MIX_DENOM,
stake_denom: mainnet::STAKE_DENOM,
mixnet_contract_address: mainnet::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: mainnet::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
coconut_bandwidth_contract_address: mainnet::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
multisig_contract_address: mainnet::MULTISIG_CONTRACT_ADDRESS,
rewarding_validator_address: mainnet::REWARDING_VALIDATOR_ADDRESS,
validators: mainnet::validators(),
});
static SANDBOX_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
Lazy::new(|| DefaultNetworkDetails {
bech32_prefix: sandbox::BECH32_PREFIX,
denom: sandbox::DENOM,
mixnet_contract_address: sandbox::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: sandbox::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
coconut_bandwidth_contract_address: sandbox::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
multisig_contract_address: sandbox::MULTISIG_CONTRACT_ADDRESS,
rewarding_validator_address: sandbox::REWARDING_VALIDATOR_ADDRESS,
validators: sandbox::validators(),
});
static SANDBOX_DEFAULTS: Lazy<DefaultNetworkDetails> = Lazy::new(|| DefaultNetworkDetails {
bech32_prefix: sandbox::BECH32_PREFIX,
mix_denom: sandbox::MIX_DENOM,
stake_denom: sandbox::STAKE_DENOM,
mixnet_contract_address: sandbox::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: sandbox::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
coconut_bandwidth_contract_address: sandbox::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
multisig_contract_address: sandbox::MULTISIG_CONTRACT_ADDRESS,
rewarding_validator_address: sandbox::REWARDING_VALIDATOR_ADDRESS,
validators: sandbox::validators(),
});
static QA_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> = Lazy::new(|| DefaultNetworkDetails {
static QA_DEFAULTS: Lazy<DefaultNetworkDetails> = Lazy::new(|| DefaultNetworkDetails {
bech32_prefix: qa::BECH32_PREFIX,
denom: qa::DENOM,
mix_denom: qa::MIX_DENOM,
stake_denom: qa::STAKE_DENOM,
mixnet_contract_address: qa::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: qa::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
@@ -95,6 +97,42 @@ static QA_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> = Lazy::new(|| DefaultN
validators: qa::validators(),
});
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct DenomDetails {
pub base: &'static str,
pub display: &'static str,
// i.e. display_amount * 10^display_exponent = base_amount
pub display_exponent: u32,
}
impl DenomDetails {
pub const fn new(base: &'static str, display: &'static str, display_exponent: u32) -> Self {
DenomDetails {
base,
display,
display_exponent,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct DenomDetailsOwned {
pub base: String,
pub display: String,
// i.e. display_amount * 10^display_exponent = base_amount
pub display_exponent: u32,
}
impl From<DenomDetails> for DenomDetailsOwned {
fn from(details: DenomDetails) -> Self {
DenomDetailsOwned {
base: details.base.to_owned(),
display: details.display.to_owned(),
display_exponent: details.display_exponent,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct ValidatorDetails {
// it is assumed those values are always valid since they're being provided in our defaults file
+4 -2
View File
@@ -1,11 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ValidatorDetails;
use crate::{DenomDetails, ValidatorDetails};
pub(crate) const BECH32_PREFIX: &str = "n";
pub const DENOM: &str = "unym";
pub const STAKE_DENOM: &str = "unyx";
pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6);
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
+4 -2
View File
@@ -1,11 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ValidatorDetails;
use crate::{DenomDetails, ValidatorDetails};
pub(crate) const BECH32_PREFIX: &str = "n";
pub const DENOM: &str = "unym";
pub const STAKE_DENOM: &str = "unyx";
pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6);
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
"n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep";
+4 -2
View File
@@ -1,11 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ValidatorDetails;
use crate::{DenomDetails, ValidatorDetails};
pub(crate) const BECH32_PREFIX: &str = "nymt";
pub const DENOM: &str = "unymt";
pub const STAKE_DENOM: &str = "unyxt";
pub const MIX_DENOM: DenomDetails = DenomDetails::new("unymt", "nymt", 6);
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyxt", "nyxt", 6);
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt14ejqjyq8um4p3xfqj74yld5waqljf88fn549lh";
+16 -4
View File
@@ -1,4 +1,4 @@
use crate::currency::{CurrencyDenom, MajorCurrencyAmount};
use crate::currency::{DecCoin, Denom};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -9,13 +9,16 @@ use serde::{Deserialize, Serialize};
)]
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct Account {
// TODO: discuss with @MS whether it makes sense:
// 1. why are we restricting to single denom here? What if user holds both stake and mix currencies?
// 2. what's the `contract_address`? is it mixnet? vesting? coconut? why does it relate to an account anyway?
pub contract_address: String,
pub client_address: String,
pub denom: CurrencyDenom,
pub denom: Denom,
}
impl Account {
pub fn new(contract_address: String, client_address: String, denom: CurrencyDenom) -> Self {
pub fn new(contract_address: String, client_address: String, denom: Denom) -> Self {
Account {
contract_address,
client_address,
@@ -53,6 +56,15 @@ pub struct AccountEntry {
)]
#[derive(Serialize, Deserialize)]
pub struct Balance {
pub amount: MajorCurrencyAmount,
pub amount: DecCoin,
pub printable_balance: String,
}
impl Balance {
pub fn new(amount: DecCoin) -> Self {
Balance {
printable_balance: amount.to_string(),
amount,
}
}
}
+397 -408
View File
@@ -1,22 +1,26 @@
use crate::error::TypesError;
use cosmrs::Denom as CosmosDenom;
use cosmwasm_std::Coin as CosmWasmCoin;
use config::defaults::all::Network;
use config::defaults::DenomDetails;
use cosmwasm_std::Fraction;
use cosmwasm_std::{Decimal, Uint128};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt::{Display, Formatter};
use std::ops::{Add, Mul};
use std::str::FromStr;
use strum::{Display, EnumString, EnumVariantNames};
use validator_client::nymd::{Coin, CosmosCoin};
use validator_client::nymd::Coin;
#[cfg(feature = "generate-ts")]
use ts_rs::{Dependency, TS};
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
ts(export_to = "ts-packages/types/src/types/rust/CurrencyDenom.ts")
)]
#[cfg_attr(feature = "generate-ts", ts(rename_all = "UPPERCASE"))]
#[cfg_attr(feature = "generate-ts", ts(rename_all = "lowercase"))]
#[derive(
Display,
Serialize,
@@ -28,9 +32,8 @@ use validator_client::nymd::{Coin, CosmosCoin};
PartialEq,
JsonSchema,
)]
#[serde(rename_all = "UPPERCASE")]
#[strum(serialize_all = "UPPERCASE")]
// TODO: this shouldn't be an enum...
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum CurrencyDenom {
#[strum(ascii_case_insensitive)]
Nym,
@@ -42,474 +45,460 @@ pub enum CurrencyDenom {
Nyxt,
}
impl CurrencyDenom {
pub fn parse(value: &str) -> Result<CurrencyDenom, TypesError> {
let mut denom = value.to_string();
if denom.starts_with('u') {
denom = denom[1..].to_string();
pub type Denom = String;
#[derive(Debug, Default)]
pub struct RegisteredCoins(HashMap<Denom, CoinMetadata>);
impl RegisteredCoins {
pub fn default_denoms(network: Network) -> Self {
let mut network_coins = HashMap::new();
network_coins.insert(
network.mix_denom().base.into(),
(*network.mix_denom()).into(),
);
network_coins.insert(
network.stake_denom().base.into(),
(*network.stake_denom()).into(),
);
RegisteredCoins(network_coins)
}
pub fn insert(&mut self, denom: Denom, metadata: CoinMetadata) -> Option<CoinMetadata> {
self.0.insert(denom, metadata)
}
pub fn remove(&mut self, denom: &Denom) -> Option<CoinMetadata> {
self.0.remove(denom)
}
pub fn attempt_convert_to_base_coin(&self, coin: DecCoin) -> Result<Coin, TypesError> {
// check if this is already in the base denom
if self.0.contains_key(&coin.denom) {
// if we're converting a base DecCoin it CANNOT fail, unless somebody is providing
// bullshit data on purpose : )
return coin.try_into();
} else {
// TODO: this kinda suggests we may need a better data structure
for registered_coin in self.0.values() {
if let Some(exponent) = registered_coin.get_exponent(&coin.denom) {
let amount = try_convert_decimal_to_u128(coin.try_scale_up_value(exponent)?)?;
return Ok(Coin::new(amount, &registered_coin.base));
}
}
}
match CurrencyDenom::from_str(&denom) {
Ok(res) => Ok(res),
Err(_e) => Err(TypesError::InvalidDenom(value.to_string())),
Err(TypesError::UnknownCoinDenom(coin.denom))
}
pub fn attempt_convert_to_display_dec_coin(&self, coin: Coin) -> Result<DecCoin, TypesError> {
for registered_coin in self.0.values() {
if let Some(exponent) = registered_coin.get_exponent(&coin.denom) {
// if this fails it means we haven't registered our display denom which honestly should never be the case
// unless somebody is rocking their own custom network
let display_exponent = registered_coin
.get_exponent(&registered_coin.display)
.ok_or_else(|| TypesError::UnknownCoinDenom(coin.denom.clone()))?;
return match exponent.cmp(&display_exponent) {
Ordering::Greater => {
// we need to scale up, unlikely to ever be needed but included for completion sake,
// for example if we decided to created knym with exponent 9 and wanted to convert to nym with exponent 6
Ok(DecCoin::new_scaled_up(
coin.amount,
&registered_coin.display,
exponent - display_exponent,
)?)
}
// we're already in the display denom
Ordering::Equal => Ok(coin.into()),
Ordering::Less => {
// we need to scale down, the most common case, for example we're in base unym with exponent 0 and want to convert to nym with exponent 6
Ok(DecCoin::new_scaled_down(
coin.amount,
&registered_coin.display,
display_exponent - exponent,
)?)
}
};
}
}
Err(TypesError::UnknownCoinDenom(coin.denom))
}
}
impl TryFrom<CosmosDenom> for CurrencyDenom {
type Error = TypesError;
// TODO: should this live here?
// attempts to replicate cosmos-sdk's coin metadata
// https://docs.cosmos.network/master/architecture/adr-024-coin-metadata.html
// this way we could more easily handle multiple coin types simultaneously (like nym/nyx/nymt/nyx + local currencies)
#[derive(Debug)]
pub struct DenomUnit {
pub denom: Denom,
pub exponent: u32,
// pub aliases: Vec<String>,
}
fn try_from(value: CosmosDenom) -> Result<Self, Self::Error> {
CurrencyDenom::parse(&value.to_string())
impl DenomUnit {
pub fn new(denom: Denom, exponent: u32) -> Self {
DenomUnit { denom, exponent }
}
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
ts(export_to = "ts-packages/types/src/types/rust/CurrencyStringMajorAmount.ts")
)]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct MajorAmountString(String); // see https://github.com/Aleph-Alpha/ts-rs/issues/51 for exporting type aliases
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
ts(export_to = "ts-packages/types/src/types/rust/Currency.ts")
)]
// #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct MajorCurrencyAmount {
// temporarly going back to original impl to speed up merge
pub amount: MajorAmountString,
pub denom: CurrencyDenom,
// // temporary...
// #[cfg_attr(feature = "generate-ts", ts(skip))]
// pub coin: Coin,
#[derive(Debug)]
pub struct CoinMetadata {
pub denom_units: Vec<DenomUnit>,
pub base: Denom,
pub display: Denom,
}
// impl JsonSchema for MajorCurrencyAmount {
// fn schema_name() -> String {
// todo!()
// }
//
// fn json_schema(gen: &mut SchemaGenerator) -> Schema {
// todo!()
// }
// }
impl CoinMetadata {
pub fn new(denom_units: Vec<DenomUnit>, base: Denom, display: Denom) -> Self {
CoinMetadata {
denom_units,
base,
display,
}
}
pub fn get_exponent(&self, denom: &str) -> Option<u32> {
self.denom_units
.iter()
.find(|denom_unit| denom_unit.denom == denom)
.map(|denom_unit| denom_unit.exponent)
}
}
impl From<DenomDetails> for CoinMetadata {
fn from(denom_details: DenomDetails) -> Self {
CoinMetadata::new(
vec![
DenomUnit::new(denom_details.base.into(), 0),
DenomUnit::new(denom_details.display.into(), denom_details.display_exponent),
],
denom_details.base.into(),
denom_details.display.into(),
)
}
}
// tries to semi-replicate cosmos-sdk's DecCoin for being able to handle tokens with decimal amounts
// https://github.com/cosmos/cosmos-sdk/blob/v0.45.4/types/dec_coin.go
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct DecCoin {
//
pub denom: Denom,
// Decimal is already serialized to string and using string in its schema, so lets also go straight to string for ts_rs
// todo: is `Decimal` the correct type to use? Do we want to depend on cosmwasm_std here?
pub amount: Decimal,
}
impl MajorCurrencyAmount {
pub fn new(amount: &str, denom: CurrencyDenom) -> MajorCurrencyAmount {
MajorCurrencyAmount {
amount: MajorAmountString(amount.to_string()),
denom,
// I had to implement it manually to correctly set dependencies
#[cfg(feature = "generate-ts")]
impl TS for DecCoin {
const EXPORT_TO: Option<&'static str> = Some("ts-packages/types/src/types/rust/DecCoin.ts");
fn decl() -> String {
format!("type {} = {};", Self::name(), Self::inline())
}
fn name() -> String {
"DecCoin".into()
}
fn inline() -> String {
"{ denom: CurrencyDenom, amount: string }".into()
}
fn dependencies() -> Vec<Dependency> {
vec![Dependency::from_ty::<CurrencyDenom>()
.expect("TS was incorrectly defined on `CurrencyDenom`")]
}
fn transparent() -> bool {
false
}
}
impl DecCoin {
pub fn new_base<S: Into<String>>(amount: impl Into<Uint128>, denom: S) -> Self {
DecCoin {
denom: denom.into(),
amount: Decimal::from_atomics(amount, 0).unwrap(),
}
}
pub fn zero(denom: &CurrencyDenom) -> MajorCurrencyAmount {
MajorCurrencyAmount::new("0", denom.clone())
pub fn zero<S: Into<String>>(denom: S) -> Self {
DecCoin {
denom: denom.into(),
amount: Decimal::zero(),
}
}
//
// pub fn from_cosmrs_coin(coin: &CosmosCoin) -> Result<MajorCurrencyAmount, TypesError> {
// MajorCurrencyAmount::from_cosmrs_decimal_and_denom(coin.amount, coin.denom.to_string())
// }
//
// pub fn from_minor_uint128_and_denom(
// amount_minor: Uint128,
// denom_minor: &str,
// ) -> Result<MajorCurrencyAmount, TypesError> {
// MajorCurrencyAmount::from_minor_decimal_and_denom(
// Decimal::from_atomics(amount_minor, 0)?,
// denom_minor,
// )
// }
//
// pub fn from_minor_decimal_and_denom(
// amount_minor: Decimal,
// denom_minor: &str,
// ) -> Result<MajorCurrencyAmount, TypesError> {
// if !(denom_minor.starts_with('u') || denom_minor.starts_with('U')) {
// return Err(TypesError::InvalidDenom(denom_minor.to_string()));
// }
// let major = amount_minor / Uint128::from(1_000_000u64);
// if let Ok(denom) = CurrencyDenom::from_str(&denom_minor[1..].to_string()) {
// return Ok(MajorCurrencyAmount {
// amount: MajorAmountString(major.to_string()),
// denom,
// });
// }
// Err(TypesError::InvalidDenom(denom_minor.to_string()))
// }
// pub fn from_decimal_and_denom(
// amount: Decimal,
// denom: String,
// ) -> Result<MajorCurrencyAmount, TypesError> {
// if denom.starts_with('u') || denom.starts_with('U') {
// return MajorCurrencyAmount::from_minor_decimal_and_denom(amount, &denom);
// }
// if let Ok(denom) = CurrencyDenom::from_str(denom.as_str()) {
// return Ok(MajorCurrencyAmount {
// amount: MajorAmountString(amount.to_string()),
// denom,
// });
// }
// Err(TypesError::InvalidDenom(denom))
// }
// pub fn from_cosmrs_decimal_and_denom(
// amount: CosmosDecimal,
// denom: String,
// ) -> Result<MajorCurrencyAmount, TypesError> {
// if denom.starts_with('u') || denom.starts_with('U') {
// return match Decimal::from_str(&amount.to_string()) {
// Ok(amount) => MajorCurrencyAmount::from_minor_decimal_and_denom(amount, &denom),
// Err(_e) => Err(TypesError::InvalidAmount(amount.to_string())),
// };
// }
//
// if let Ok(denom) = CurrencyDenom::from_str(denom.as_str()) {
// return Ok(MajorCurrencyAmount {
// amount: MajorAmountString(amount.to_string()),
// denom,
// });
// }
// Err(TypesError::InvalidDenom(denom))
// }
//
// pub fn into_cosmos_coin(self) -> CosmosCoin {
// self.coin.into()
// }
//
// pub fn to_minor_uint128(&self) -> Result<Uint128, TypesError> {
// if self.amount.0.contains('.') {
// // has a decimal point (Cosmos assumes "." is the decimal separator)
// let parts = self.amount.0.split('.');
// let str = parts.collect_vec();
// if str.is_empty() || str.len() > 2 {
// return Err(TypesError::InvalidAmount("Amount is invalid".to_string()));
// }
// if str.len() == 2 {
// // has a decimal, so check decimal places first
// if str[1].len() > 6 {
// return Err(TypesError::InvalidDenom(
// "Amount is invalid, only 6 decimal places of precision are allowed"
// .to_string(),
// ));
// }
//
// // so multiple whole part by 1e6 and add decimal part
// let whole_part = Uint128::from_str(str[0])? * Uint128::from(1_000_000u64);
//
// // TODO: has Rust got anything that deals with fixed point values, or parsing from format strings? Leading zeroes are causing issues
// return match format!("0.{}", str[1]).parse::<f64>() {
// Ok(decimal_part_float) => {
// // this makes an assumption that 6 decimal places of f64 can never lose precision
// let truncated = (decimal_part_float * 1_000_000.).trunc() as u32;
// let decimal_part = Uint128::from(truncated);
// let sum = whole_part + decimal_part;
// Ok(sum)
// }
// Err(_e) => Err(TypesError::InvalidAmount(
// "Amount decimal part is invalid".to_string(),
// )),
// };
// }
// }
//
// let major = Uint128::from_str(&self.amount.0)?;
// let scaled = major * Uint128::new(1_000_000u128);
// Ok(scaled)
// }
// pub fn denom_to_string(&self) -> String {
// self.denom.to_string()
// }
pub fn new_scaled_up<S: Into<String>>(
base_amount: impl Into<Uint128>,
denom: S,
exponent: u32,
) -> Result<Self, TypesError> {
let base_amount = Decimal::from_atomics(base_amount, 0).unwrap();
Ok(DecCoin {
denom: denom.into(),
amount: try_scale_up_decimal(base_amount, exponent)?,
})
}
pub fn new_scaled_down<S: Into<String>>(
base_amount: impl Into<Uint128>,
denom: S,
exponent: u32,
) -> Result<Self, TypesError> {
let base_amount = Decimal::from_atomics(base_amount, 0).unwrap();
Ok(DecCoin {
denom: denom.into(),
amount: try_scale_down_decimal(base_amount, exponent)?,
})
}
pub fn try_scale_down_value(&self, exponent: u32) -> Result<Decimal, TypesError> {
try_scale_down_decimal(self.amount, exponent)
}
pub fn try_scale_up_value(&self, exponent: u32) -> Result<Decimal, TypesError> {
try_scale_up_decimal(self.amount, exponent)
}
}
impl Display for MajorCurrencyAmount {
// TODO: should thoese live here?
pub fn try_scale_down_decimal(dec: Decimal, exponent: u32) -> Result<Decimal, TypesError> {
let rhs = 10u128
.checked_pow(exponent)
.ok_or(TypesError::UnsupportedExponent(exponent))?;
let denominator = dec
.denominator()
.checked_mul(rhs.into())
.map_err(|_| TypesError::UnsupportedExponent(exponent))?;
Ok(Decimal::from_ratio(dec.numerator(), denominator))
}
pub fn try_scale_up_decimal(dec: Decimal, exponent: u32) -> Result<Decimal, TypesError> {
let rhs = 10u128
.checked_pow(exponent)
.ok_or(TypesError::UnsupportedExponent(exponent))?;
let denominator = dec
.denominator()
.checked_div(rhs.into())
.map_err(|_| TypesError::UnsupportedExponent(exponent))?;
Ok(Decimal::from_ratio(dec.numerator(), denominator))
}
pub fn try_convert_decimal_to_u128(dec: Decimal) -> Result<u128, TypesError> {
let whole = dec.numerator() / dec.denominator();
// unwrap is fine as we're not dividing by zero here
let fractional = (dec.numerator()).checked_rem(dec.denominator()).unwrap();
// we cannot convert as we'd lose our decimal places
// (for example if somebody attempted to represent our gas price (WHICH YOU SHOULDN'T DO) as DecCoin)
if fractional != Uint128::zero() {
return Err(TypesError::LossyCoinConversion);
}
Ok(whole.u128())
}
// TODO: adjust the implementation to as required by @MS or @FT
impl Display for DecCoin {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.amount.0, self.denom)
write!(f, "{}{}", self.amount, self.denom)
}
}
// TODO: cleanup after merge
impl From<CosmosCoin> for MajorCurrencyAmount {
fn from(c: CosmosCoin) -> Self {
MajorCurrencyAmount::from(Coin::from(c))
}
}
impl From<CosmWasmCoin> for MajorCurrencyAmount {
fn from(c: CosmWasmCoin) -> Self {
MajorCurrencyAmount::from(Coin::from(c))
}
}
impl From<Coin> for MajorCurrencyAmount {
impl From<Coin> for DecCoin {
fn from(coin: Coin) -> Self {
// current assumption: MajorCurrencyAmount is represented as decimal with 6 decimal points
// unwrap is fine as we haven't exceeded decimal range since our coins are at max 1B in value
// (this is a weak assumption, but for solving this merge conflict it's good enough temporary workaround)
let amount = Decimal::from_atomics(coin.amount, 6).unwrap();
MajorCurrencyAmount {
amount: MajorAmountString(amount.to_string()),
denom: CurrencyDenom::parse(&coin.denom).expect("this will go away after the merge..."),
}
DecCoin::new_base(coin.amount, coin.denom)
}
}
// temporary...
impl From<MajorCurrencyAmount> for CosmosCoin {
fn from(c: MajorCurrencyAmount) -> CosmosCoin {
let c: Coin = c.into();
c.into()
}
}
// this conversion assumes same denomination
impl TryFrom<DecCoin> for Coin {
type Error = TypesError;
impl From<MajorCurrencyAmount> for CosmWasmCoin {
fn from(c: MajorCurrencyAmount) -> CosmWasmCoin {
let c: Coin = c.into();
c.into()
}
}
impl From<MajorCurrencyAmount> for Coin {
fn from(c: MajorCurrencyAmount) -> Coin {
let decimal: Decimal = c
.amount
.0
.parse()
.expect("stringified amount should have been a valid decimal");
// again, temporary
let exp = Uint128::new(1000000);
let val = decimal.mul(exp);
// again, terrible assumption for denom, but it works temporarily...
Coin {
amount: val.u128(),
denom: format!("u{}", c.denom).to_lowercase(),
}
}
}
impl Add for MajorCurrencyAmount {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
// again, temporary workaround to help with merge
(Coin::from(self).try_add(&Coin::from(rhs)))
.expect("provided coins had different denoms")
.into()
fn try_from(value: DecCoin) -> Result<Self, Self::Error> {
Ok(Coin {
amount: try_convert_decimal_to_u128(value.try_scale_down_value(0)?)?,
denom: value.denom,
})
}
}
#[cfg(test)]
mod test {
use super::*;
use cosmrs::Coin as CosmosCoin;
use cosmrs::Decimal as CosmosDecimal;
use cosmrs::Denom as CosmosDenom;
use cosmwasm_std::Coin as CosmWasmCoin;
use cosmwasm_std::Decimal as CosmWasmDecimal;
use serde_json::json;
use std::convert::TryFrom;
use std::str::FromStr;
use std::string::ToString;
#[test]
fn json_to_major_currency_amount() {
let nym = json!({
"amount": "1",
"denom": "NYM"
});
let nymt = json!({
"amount": "1",
"denom": "NYMT"
});
fn dec_value_scale_down() {
let dec = DecCoin {
denom: "foo".to_string(),
amount: "1234007000".parse().unwrap(),
};
let test_nym_amount = MajorCurrencyAmount::new("1", CurrencyDenom::Nym);
let test_nymt_amount = MajorCurrencyAmount::new("1", CurrencyDenom::Nymt);
let nym_amount = serde_json::from_value::<MajorCurrencyAmount>(nym).unwrap();
let nymt_amount = serde_json::from_value::<MajorCurrencyAmount>(nymt).unwrap();
assert_eq!(nym_amount, test_nym_amount);
assert_eq!(nymt_amount, test_nymt_amount);
}
#[test]
fn minor_amount_json_to_major_currency_amount() {
let one_micro_nym = json!({
"amount": "0.000001",
"denom": "NYM"
});
let expected_nym_amount = MajorCurrencyAmount::new("0.000001", CurrencyDenom::Nym);
let actual_nym_amount =
serde_json::from_value::<MajorCurrencyAmount>(one_micro_nym).unwrap();
assert_eq!(expected_nym_amount, actual_nym_amount);
}
#[test]
fn denom_from_str() {
assert_eq!(CurrencyDenom::from_str("nym").unwrap(), CurrencyDenom::Nym);
assert_eq!(
CurrencyDenom::from_str("nymt").unwrap(),
CurrencyDenom::Nymt
);
assert_eq!(CurrencyDenom::from_str("NYM").unwrap(), CurrencyDenom::Nym);
assert_eq!(
CurrencyDenom::from_str("NYMT").unwrap(),
CurrencyDenom::Nymt
);
assert_eq!(CurrencyDenom::from_str("NyM").unwrap(), CurrencyDenom::Nym);
assert_eq!(
CurrencyDenom::from_str("NYmt").unwrap(),
CurrencyDenom::Nymt
);
assert!(matches!(
CurrencyDenom::from_str("foo").unwrap_err(),
strum::ParseError::VariantNotFound,
));
// denominations must all be major
assert!(matches!(
CurrencyDenom::from_str("unym").unwrap_err(),
strum::ParseError::VariantNotFound,
));
assert!(matches!(
CurrencyDenom::from_str("unymt").unwrap_err(),
strum::ParseError::VariantNotFound,
));
}
#[test]
fn to_string() {
assert_eq!(
MajorCurrencyAmount::new("1", CurrencyDenom::Nym).to_string(),
"1 NYM"
"1234007000".parse::<Decimal>().unwrap(),
dec.try_scale_down_value(0).unwrap()
);
assert_eq!(
MajorCurrencyAmount::new("1", CurrencyDenom::Nymt).to_string(),
"1 NYMT"
"123400700".parse::<Decimal>().unwrap(),
dec.try_scale_down_value(1).unwrap()
);
assert_eq!(
MajorCurrencyAmount::new("1000000000000", CurrencyDenom::Nym).to_string(),
"1000000000000 NYM"
"12340070".parse::<Decimal>().unwrap(),
dec.try_scale_down_value(2).unwrap()
);
assert_eq!(
"123400.7".parse::<Decimal>().unwrap(),
dec.try_scale_down_value(4).unwrap()
);
let dec = DecCoin {
denom: "foo".to_string(),
amount: "10000000000".parse().unwrap(),
};
assert_eq!(
"100".parse::<Decimal>().unwrap(),
dec.try_scale_down_value(8).unwrap()
);
assert_eq!(
"1".parse::<Decimal>().unwrap(),
dec.try_scale_down_value(10).unwrap()
);
assert_eq!(
"0.01".parse::<Decimal>().unwrap(),
dec.try_scale_down_value(12).unwrap()
);
}
#[test]
fn minor_coin_to_major_currency() {
let cosmos_coin = CosmosCoin {
amount: CosmosDecimal::from(1u64),
denom: CosmosDenom::from_str("unym").unwrap(),
fn dec_value_scale_up() {
let dec = DecCoin {
denom: "foo".to_string(),
amount: "1234.56".parse().unwrap(),
};
let c = MajorCurrencyAmount::from(cosmos_coin);
assert_eq!(c, MajorCurrencyAmount::new("0.000001", CurrencyDenom::Nym));
}
#[test]
fn minor_cosmwasm_coin_to_major_currency() {
let coin = CosmWasmCoin {
amount: Uint128::from(1u64),
denom: "unym".to_string(),
};
println!(
"from_atomics = {}",
CosmWasmDecimal::from_atomics(coin.amount.clone(), 6)
.unwrap()
.to_string()
assert_eq!(
"1234.56".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(0).unwrap()
);
let c: MajorCurrencyAmount = coin.into();
assert_eq!(c, MajorCurrencyAmount::new("0.000001", CurrencyDenom::Nym));
}
#[test]
fn minor_cosmwasm_coin_to_major_currency_2() {
let coin = CosmWasmCoin {
amount: Uint128::from(1_000_000u64),
denom: "unym".to_string(),
};
println!(
"from_atomics = {:?}",
CosmWasmDecimal::from_atomics(coin.amount.clone(), 6)
.unwrap()
.to_string()
assert_eq!(
"12345.6".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(1).unwrap()
);
assert_eq!(
"123456".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(2).unwrap()
);
assert_eq!(
"1234560".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(3).unwrap()
);
assert_eq!(
"12345600".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(4).unwrap()
);
let c: MajorCurrencyAmount = coin.into();
assert_eq!(c, MajorCurrencyAmount::new("1", CurrencyDenom::Nym));
}
#[test]
fn major_currency_to_minor_cosmos_coin() {
let expected_cosmos_coin = CosmosCoin {
amount: CosmosDecimal::from(1u64),
denom: CosmosDenom::from_str("unym").unwrap(),
let dec = DecCoin {
denom: "foo".to_string(),
amount: "0.00000123".parse().unwrap(),
};
let c = MajorCurrencyAmount::new("0.000001", CurrencyDenom::Nym);
let minor_cosmos_coin = c.into();
assert_eq!(expected_cosmos_coin, minor_cosmos_coin);
assert_eq!("unym", minor_cosmos_coin.denom.to_string());
assert_eq!(
"0.0000123".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(1).unwrap()
);
assert_eq!(
"0.000123".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(2).unwrap()
);
assert_eq!(
"123".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(8).unwrap()
);
assert_eq!(
"1230".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(9).unwrap()
);
assert_eq!(
"12300".parse::<Decimal>().unwrap(),
dec.try_scale_up_value(10).unwrap()
);
}
#[test]
fn major_currency_to_minor_cosmos_coin_2() {
let expected_cosmos_coin = CosmosCoin {
amount: CosmosDecimal::from(1000000u64),
denom: CosmosDenom::from_str("unym").unwrap(),
fn coin_to_dec_coin() {
let coin = Coin::new(123, "foo");
let dec = DecCoin::from(coin.clone());
assert_eq!(coin.denom, dec.denom);
assert_eq!(dec.amount, Decimal::from_atomics(coin.amount, 0).unwrap());
}
#[test]
fn dec_coin_to_coin() {
let dec = DecCoin {
denom: "foo".to_string(),
amount: "123".parse().unwrap(),
};
let c = MajorCurrencyAmount::new("1", CurrencyDenom::Nym);
let minor_cosmos_coin = c.into();
assert_eq!(expected_cosmos_coin, minor_cosmos_coin);
assert_eq!("unym", minor_cosmos_coin.denom.to_string());
let coin = Coin::try_from(dec.clone()).unwrap();
assert_eq!(dec.denom, coin.denom);
assert_eq!(coin.amount, 123u128);
}
#[test]
fn minor_cosmos_coin_to_major_currency_string() {
// check minor cosmos coin is converted to major value
let cosmos_coin = CosmosCoin {
amount: CosmosDecimal::from(1u64),
denom: CosmosDenom::from_str("unym").unwrap(),
};
let c = MajorCurrencyAmount::from(cosmos_coin);
assert_eq!(c.to_string(), "0.000001 NYM");
fn converting_to_display() {
let reg = RegisteredCoins::default_denoms(Network::MAINNET);
let values = vec![
(1u128, "0.000001"),
(10u128, "0.00001"),
(100u128, "0.0001"),
(1000u128, "0.001"),
(10000u128, "0.01"),
(100000u128, "0.1"),
(1000000u128, "1"),
(1234567u128, "1.234567"),
(123456700u128, "123.4567"),
];
for (raw, expected) in values {
let coin = Coin::new(raw, Network::MAINNET.mix_denom().base);
let display = reg.attempt_convert_to_display_dec_coin(coin).unwrap();
assert_eq!(Network::MAINNET.mix_denom().display, display.denom);
assert_eq!(expected, display.amount.to_string());
}
}
#[test]
fn denom_to_string() {
let c = MajorCurrencyAmount::new("1", CurrencyDenom::Nym);
let denom = c.denom.to_string();
assert_eq!(denom, "NYM".to_string());
}
fn converting_to_base() {
let reg = RegisteredCoins::default_denoms(Network::MAINNET);
let values = vec![
(1u128, "0.000001"),
(10u128, "0.00001"),
(100u128, "0.0001"),
(1000u128, "0.001"),
(10000u128, "0.01"),
(100000u128, "0.1"),
(1000000u128, "1"),
(1234567u128, "1.234567"),
(123456700u128, "123.4567"),
];
fn amounts() -> Vec<&'static str> {
vec![
"1",
"10",
"100",
"1000",
"10000",
"100000",
"10000000",
"100000000",
"1000000000",
"10000000000",
"100000000000",
"1000000000000",
"10000000000000",
"100000000000000",
"1000000000000000",
"10000000000000000",
"100000000000000000",
"1000000000000000000",
]
for (expected, raw_display) in values {
let coin = DecCoin {
denom: Network::MAINNET.mix_denom().display.into(),
amount: raw_display.parse().unwrap(),
};
let base = reg.attempt_convert_to_base_coin(coin).unwrap();
assert_eq!(Network::MAINNET.mix_denom().base, base.denom);
assert_eq!(expected, base.amount);
}
}
}
+41 -106
View File
@@ -1,13 +1,10 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use log::error;
use crate::currency::{DecCoin, RegisteredCoins};
use crate::error::TypesError;
use mixnet_contract_common::mixnode::DelegationEvent as ContractDelegationEvent;
use mixnet_contract_common::mixnode::PendingUndelegate as ContractPendingUndelegate;
use mixnet_contract_common::Delegation as MixnetContractDelegation;
use crate::currency::MajorCurrencyAmount;
use crate::error::TypesError;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
@@ -18,31 +15,22 @@ use crate::error::TypesError;
pub struct Delegation {
pub owner: String,
pub node_identity: String,
pub amount: MajorCurrencyAmount,
pub amount: DecCoin,
pub block_height: u64,
pub proxy: Option<String>, // proxy address used to delegate the funds on behalf of anouther address
pub proxy: Option<String>, // proxy address used to delegate the funds on behalf of another address
}
impl TryFrom<MixnetContractDelegation> for Delegation {
type Error = TypesError;
fn try_from(value: MixnetContractDelegation) -> Result<Self, Self::Error> {
let MixnetContractDelegation {
owner,
node_identity,
amount,
block_height,
proxy,
} = value;
let amount: MajorCurrencyAmount = amount.into();
impl Delegation {
pub fn from_mixnet_contract(
delegation: MixnetContractDelegation,
reg: &RegisteredCoins,
) -> Result<Self, TypesError> {
Ok(Delegation {
owner: owner.into_string(),
node_identity,
amount,
block_height,
proxy: proxy.map(|p| p.into_string()),
owner: delegation.owner.to_string(),
node_identity: delegation.node_identity,
amount: reg.attempt_convert_to_display_dec_coin(delegation.amount.into())?,
block_height: delegation.block_height,
proxy: delegation.proxy.map(|d| d.to_string()),
})
}
}
@@ -54,7 +42,7 @@ impl TryFrom<MixnetContractDelegation> for Delegation {
)]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
pub struct DelegationRecord {
pub amount: MajorCurrencyAmount,
pub amount: DecCoin,
pub block_height: u64,
pub delegated_on_iso_datetime: String,
}
@@ -68,16 +56,16 @@ pub struct DelegationRecord {
pub struct DelegationWithEverything {
pub owner: String,
pub node_identity: String,
pub amount: MajorCurrencyAmount,
pub total_delegation: Option<MajorCurrencyAmount>,
pub pledge_amount: Option<MajorCurrencyAmount>,
pub amount: DecCoin,
pub total_delegation: Option<DecCoin>,
pub pledge_amount: Option<DecCoin>,
pub block_height: u64,
pub delegated_on_iso_datetime: String,
pub profit_margin_percent: Option<u8>,
pub avg_uptime_percent: Option<u8>,
pub stake_saturation: Option<f32>,
pub proxy: Option<String>,
pub accumulated_rewards: Option<MajorCurrencyAmount>,
pub accumulated_rewards: Option<DecCoin>,
pub pending_events: Vec<DelegationEvent>,
pub history: Vec<DelegationRecord>,
}
@@ -91,34 +79,7 @@ pub struct DelegationWithEverything {
pub struct DelegationResult {
source_address: String,
target_address: String,
amount: Option<MajorCurrencyAmount>,
}
impl DelegationResult {
pub fn new(
source_address: &str,
target_address: &str,
amount: Option<MajorCurrencyAmount>,
) -> DelegationResult {
DelegationResult {
source_address: source_address.to_string(),
target_address: target_address.to_string(),
amount,
}
}
}
impl TryFrom<MixnetContractDelegation> for DelegationResult {
type Error = TypesError;
fn try_from(delegation: MixnetContractDelegation) -> Result<Self, Self::Error> {
let amount: MajorCurrencyAmount = delegation.amount.clone().into();
Ok(DelegationResult {
source_address: delegation.owner().to_string(),
target_address: delegation.node_identity(),
amount: Some(amount),
})
}
amount: Option<DecCoin>,
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
@@ -142,33 +103,31 @@ pub struct DelegationEvent {
pub kind: DelegationEventKind,
pub node_identity: String,
pub address: String,
pub amount: Option<MajorCurrencyAmount>,
pub amount: Option<DecCoin>,
pub block_height: u64,
}
impl TryFrom<ContractDelegationEvent> for DelegationEvent {
type Error = TypesError;
fn try_from(event: ContractDelegationEvent) -> Result<Self, Self::Error> {
match event {
ContractDelegationEvent::Delegate(delegation) => {
let amount: MajorCurrencyAmount = delegation.amount.into();
Ok(DelegationEvent {
kind: DelegationEventKind::Delegate,
block_height: delegation.block_height,
address: delegation.owner.into_string(),
node_identity: delegation.node_identity,
amount: Some(amount),
})
}
ContractDelegationEvent::Undelegate(pending_undelegate) => Ok(DelegationEvent {
impl DelegationEvent {
pub fn from_mixnet_contract(
event: ContractDelegationEvent,
reg: &RegisteredCoins,
) -> Result<Self, TypesError> {
Ok(match event {
ContractDelegationEvent::Delegate(delegation) => DelegationEvent {
kind: DelegationEventKind::Delegate,
block_height: delegation.block_height,
address: delegation.owner.into_string(),
node_identity: delegation.node_identity,
amount: Some(reg.attempt_convert_to_display_dec_coin(delegation.amount.into())?),
},
ContractDelegationEvent::Undelegate(pending_undelegate) => DelegationEvent {
kind: DelegationEventKind::Undelegate,
block_height: pending_undelegate.block_height(),
address: pending_undelegate.delegate().into_string(),
node_identity: pending_undelegate.mix_identity(),
amount: None,
}),
}
},
})
}
}
@@ -196,30 +155,6 @@ impl From<ContractPendingUndelegate> for PendingUndelegate {
}
}
pub fn from_contract_delegation_events(
events: Vec<ContractDelegationEvent>,
) -> Result<Vec<DelegationEvent>, TypesError> {
let (events, errors): (Vec<_>, Vec<_>) = events
.into_iter()
.map(|delegation_event| delegation_event.try_into())
.partition(Result::is_ok);
if errors.is_empty() {
let events = events
.into_iter()
.filter_map(|e| e.ok())
.collect::<Vec<DelegationEvent>>();
return Ok(events);
}
let errors = errors
.into_iter()
.filter_map(|e| e.err())
.collect::<Vec<TypesError>>();
error!("Failed to convert delegations: {:?}", errors);
Err(TypesError::DelegationsInvalid)
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
@@ -228,6 +163,6 @@ pub fn from_contract_delegation_events(
#[derive(Deserialize, Serialize)]
pub struct DelegationsSummaryResponse {
pub delegations: Vec<DelegationWithEverything>,
pub total_delegations: MajorCurrencyAmount,
pub total_rewards: MajorCurrencyAmount,
pub total_delegations: DecCoin,
pub total_rewards: DecCoin,
}
+7
View File
@@ -4,6 +4,7 @@ use thiserror::Error;
use validator_client::validator_api::error::ValidatorAPIError;
use validator_client::{nymd::error::NymdError, ValidatorClientError};
// TODO: ask @MS why this even exists
#[derive(Error, Debug)]
pub enum TypesError {
#[error("{source}")]
@@ -61,6 +62,12 @@ pub enum TypesError {
InvalidGatewayBond(),
#[error("Invalid delegations")]
DelegationsInvalid,
#[error("Attempted to use too huge currency exponent ({0})")]
UnsupportedExponent(u32),
#[error("Attempted to convert coin that would have resulted in loss of precision")]
LossyCoinConversion,
#[error("The provided coin has an unknown denomination - {0}")]
UnknownCoinDenom(String),
}
impl Serialize for TypesError {
+8 -4
View File
@@ -1,9 +1,7 @@
use crate::currency::DecCoin;
use serde::{Deserialize, Serialize};
use validator_client::nymd::Fee;
use crate::currency::MajorCurrencyAmount;
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
@@ -12,7 +10,13 @@ use crate::currency::MajorCurrencyAmount;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeeDetails {
// expected to be used by the wallet in order to display detailed fee information to the user
pub amount: Option<MajorCurrencyAmount>,
pub amount: Option<DecCoin>,
#[cfg_attr(feature = "generate-ts", ts(skip))]
pub fee: Fee,
}
impl FeeDetails {
pub fn new(amount: Option<DecCoin>, fee: Fee) -> Self {
FeeDetails { amount, fee }
}
}
+26 -60
View File
@@ -1,48 +1,29 @@
use crate::currency::MajorCurrencyAmount;
use crate::error::TypesError;
use cosmrs::tx::Gas as CosmrsGas;
use serde::{Deserialize, Serialize};
use validator_client::nymd::cosmwasm_client::types::GasInfo as ValidatorClientGasInfo;
use validator_client::nymd::GasPrice;
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
ts(export_to = "ts-packages/types/src/types/rust/Gas.ts")
)]
#[derive(Deserialize, Serialize, Clone)]
#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
pub struct Gas {
/// units of gas used
pub gas_units: u64,
//
// /// gas units converted to fee as major coin amount
// pub amount: MajorCurrencyAmount,
}
impl Gas {
pub fn from_cosmrs_gas(value: CosmrsGas, _denom_minor: &str) -> Result<Gas, TypesError> {
Ok(Gas {
gas_units: value.value(),
})
// // TODO: use simulator struct to do conversion to fee
// let value_u128 = Uint128::from(value.value());
// let amount = Decimal::new(value_u128) * Decimal::from_str("0.0025")?;
// Ok(Gas {
// gas_units: value.value(),
// amount: MajorCurrencyAmount::from_minor_decimal_and_denom(amount, denom_minor)?,
// })
pub fn from_u64(value: u64) -> Gas {
Gas { gas_units: value }
}
pub fn from_u64(value: u64, _denom_minor: &str) -> Result<Gas, TypesError> {
Ok(Gas { gas_units: value })
// todo!()
// // TODO: use simulator struct to do conversion to fee
// let value_u128 = Uint128::from(value);
// let amount = Decimal::new(value_u128) * Decimal::from_str("0.0025")?;
// Ok(Gas {
// gas_units: value,
// amount: MajorCurrencyAmount::from_minor_decimal_and_denom(amount, denom_minor)?,
// })
}
impl From<CosmrsGas> for Gas {
fn from(gas: CosmrsGas) -> Self {
Gas {
gas_units: gas.value(),
}
}
}
@@ -51,44 +32,29 @@ impl Gas {
feature = "generate-ts",
ts(export_to = "ts-packages/types/src/types/rust/GasInfo.ts")
)]
#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
pub struct GasInfo {
/// GasWanted is the maximum units of work we allow this tx to perform.
pub gas_wanted: u64,
pub gas_wanted: Gas,
/// GasUsed is the amount of gas actually consumed.
pub gas_used: u64,
pub gas_used: Gas,
}
/// gas units converted to fee as major coin amount
pub fee: MajorCurrencyAmount,
impl From<ValidatorClientGasInfo> for GasInfo {
fn from(info: ValidatorClientGasInfo) -> Self {
GasInfo {
gas_wanted: info.gas_wanted.into(),
gas_used: info.gas_used.into(),
}
}
}
impl GasInfo {
pub fn from_validator_client_gas_info(
value: ValidatorClientGasInfo,
denom_minor: &str,
) -> Result<GasInfo, TypesError> {
// terrible workaround, but I don't want to break the current flow (just yet)
let gas_price = GasPrice::new_with_default_price(denom_minor)?;
let fee = (&gas_price) * value.gas_used;
Ok(GasInfo {
gas_wanted: value.gas_wanted.value(),
gas_used: value.gas_used.value(),
fee: fee.into(),
})
}
pub fn from_u64(
gas_wanted: u64,
gas_used: u64,
denom_minor: &str,
) -> Result<GasInfo, TypesError> {
// terrible workaround, but I don't want to break the current flow (just yet)
let gas_price = GasPrice::new_with_default_price(denom_minor)?;
let fee = (&gas_price) * CosmrsGas::from(gas_used);
Ok(GasInfo {
gas_wanted,
gas_used,
fee: fee.into(),
})
pub fn from_u64(gas_wanted: u64, gas_used: u64) -> GasInfo {
GasInfo {
gas_wanted: Gas::from_u64(gas_wanted),
gas_used: Gas::from_u64(gas_used),
}
}
}
+10 -33
View File
@@ -1,4 +1,4 @@
use crate::currency::MajorCurrencyAmount;
use crate::currency::{DecCoin, RegisteredCoins};
use crate::error::TypesError;
use mixnet_contract_common::{
Gateway as MixnetContractGateway, GatewayBond as MixnetContractGatewayBond,
@@ -54,7 +54,7 @@ impl From<MixnetContractGateway> for Gateway {
)]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct GatewayBond {
pub pledge_amount: MajorCurrencyAmount,
pub pledge_amount: DecCoin,
pub owner: String,
pub block_height: u64,
pub gateway: Gateway,
@@ -63,38 +63,15 @@ pub struct GatewayBond {
impl GatewayBond {
pub fn from_mixnet_contract_gateway_bond(
bond: Option<MixnetContractGatewayBond>,
) -> Result<Option<GatewayBond>, TypesError> {
match bond {
Some(bond) => {
let bond: GatewayBond = bond.try_into()?;
Ok(Some(bond))
}
None => Ok(None),
}
}
}
impl TryFrom<MixnetContractGatewayBond> for GatewayBond {
type Error = TypesError;
fn try_from(value: MixnetContractGatewayBond) -> Result<Self, Self::Error> {
let MixnetContractGatewayBond {
pledge_amount,
owner,
block_height,
gateway,
proxy,
} = value;
let pledge_amount: MajorCurrencyAmount = pledge_amount.into();
bond: MixnetContractGatewayBond,
reg: &RegisteredCoins,
) -> Result<GatewayBond, TypesError> {
Ok(GatewayBond {
pledge_amount,
owner: owner.into_string(),
block_height,
gateway: gateway.into(),
proxy: proxy.map(|p| p.into_string()),
pledge_amount: reg.attempt_convert_to_display_dec_coin(bond.pledge_amount.into())?,
owner: bond.owner.to_string(),
block_height: bond.block_height,
gateway: bond.gateway.into(),
proxy: bond.proxy.map(|p| p.into_string()),
})
}
}
+26 -54
View File
@@ -1,11 +1,11 @@
use crate::currency::MajorCurrencyAmount;
use crate::currency::{DecCoin, RegisteredCoins};
use crate::error::TypesError;
use mixnet_contract_common::{
Coin as CosmWasmCoin, MixNode as MixnetContractMixNode,
MixNodeBond as MixnetContractMixNodeBond,
MixNode as MixnetContractMixNode, MixNodeBond as MixnetContractMixNodeBond,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use validator_client::nymd::Coin;
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
@@ -58,67 +58,39 @@ impl From<MixnetContractMixNode> for MixNode {
)]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct MixNodeBond {
pub pledge_amount: MajorCurrencyAmount,
pub total_delegation: MajorCurrencyAmount,
pub pledge_amount: DecCoin,
pub total_delegation: DecCoin,
pub owner: String,
pub layer: String,
pub block_height: u64,
pub mix_node: MixNode,
pub proxy: Option<String>,
pub accumulated_rewards: Option<MajorCurrencyAmount>,
pub accumulated_rewards: Option<DecCoin>,
}
impl MixNodeBond {
pub fn from_mixnet_contract_mixnode_bond(
bond: Option<MixnetContractMixNodeBond>,
) -> Result<Option<MixNodeBond>, TypesError> {
match bond {
Some(bond) => {
let bond: MixNodeBond = bond.try_into()?;
Ok(Some(bond))
}
None => Ok(None),
}
}
}
impl TryFrom<MixnetContractMixNodeBond> for MixNodeBond {
type Error = TypesError;
fn try_from(value: MixnetContractMixNodeBond) -> Result<Self, Self::Error> {
let MixnetContractMixNodeBond {
pledge_amount,
total_delegation,
owner,
layer,
block_height,
mix_node,
proxy,
accumulated_rewards,
} = value;
if pledge_amount.denom != total_delegation.denom {
return Err(TypesError::InvalidDenom(
"The pledge and delegation denominations do not match".to_string(),
));
}
let denom = total_delegation.denom.clone();
let pledge_amount: MajorCurrencyAmount = pledge_amount.into();
let total_delegation: MajorCurrencyAmount = total_delegation.into();
let accumulated_rewards: Option<MajorCurrencyAmount> =
accumulated_rewards.map(|r| CosmWasmCoin::new(r.u128(), denom).into());
bond: MixnetContractMixNodeBond,
reg: &RegisteredCoins,
) -> Result<MixNodeBond, TypesError> {
let denom = bond.pledge_amount.denom.clone();
Ok(MixNodeBond {
pledge_amount,
total_delegation,
owner: owner.into_string(),
layer: layer.into(),
block_height,
mix_node: mix_node.into(),
proxy: proxy.map(|p| p.into_string()),
accumulated_rewards,
pledge_amount: reg.attempt_convert_to_display_dec_coin(bond.pledge_amount.into())?,
total_delegation: reg
.attempt_convert_to_display_dec_coin(bond.total_delegation.into())?,
owner: bond.owner.into_string(),
layer: bond.layer.into(),
block_height: bond.block_height,
mix_node: bond.mix_node.into(),
proxy: bond.proxy.map(|p| p.to_string()),
accumulated_rewards: bond
.accumulated_rewards
.map(|reward| {
// here we're making an assumption that rewards always use the same denom as the pledge
// (which I think is a reasonable assumption)
reg.attempt_convert_to_display_dec_coin(Coin::new(reward.u128(), denom))
})
.transpose()?,
})
}
}
+37 -43
View File
@@ -1,6 +1,6 @@
use crate::currency::MajorCurrencyAmount;
use crate::currency::DecCoin;
use crate::error::TypesError;
use crate::gas::GasInfo;
use crate::gas::{Gas, GasInfo};
use serde::{Deserialize, Serialize};
use validator_client::nymd::cosmwasm_client::types::ExecuteResult;
use validator_client::nymd::TxResponse;
@@ -15,31 +15,23 @@ pub struct SendTxResult {
pub block_height: u64,
pub code: u32,
pub details: TransactionDetails,
pub gas_used: u64,
pub gas_wanted: u64,
pub gas_used: Gas,
pub gas_wanted: Gas,
pub tx_hash: String,
// pub fee: MajorCurrencyAmount,
pub fee: Option<DecCoin>,
}
impl SendTxResult {
pub fn new(
t: TxResponse,
details: TransactionDetails,
_denom_minor: &str,
) -> Result<SendTxResult, TypesError> {
Ok(SendTxResult {
pub fn new(t: TxResponse, details: TransactionDetails, fee: Option<DecCoin>) -> SendTxResult {
SendTxResult {
block_height: t.height.value(),
code: t.tx_result.code.value(),
details,
gas_used: t.tx_result.gas_used.value(),
gas_wanted: t.tx_result.gas_wanted.value(),
gas_used: t.tx_result.gas_used.into(),
gas_wanted: t.tx_result.gas_wanted.into(),
tx_hash: t.hash.to_string(),
// that is completely wrong: fee is what you told the validator to use beforehand
// fee: MajorCurrencyAmount::from_decimal_and_denom(
// Decimal::new(Uint128::from(t.tx_result.gas_used.value())),
// denom_minor.to_string(),
// )?,
})
fee,
}
}
}
@@ -50,11 +42,21 @@ impl SendTxResult {
)]
#[derive(Deserialize, Serialize, Debug)]
pub struct TransactionDetails {
pub amount: MajorCurrencyAmount,
pub amount: DecCoin,
pub from_address: String,
pub to_address: String,
}
impl TransactionDetails {
pub fn new(amount: DecCoin, from_address: String, to_address: String) -> Self {
TransactionDetails {
amount,
from_address,
to_address,
}
}
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
@@ -66,18 +68,16 @@ pub struct TransactionExecuteResult {
pub data_json: String,
pub transaction_hash: String,
pub gas_info: GasInfo,
pub fee: MajorCurrencyAmount,
pub fee: Option<DecCoin>,
}
impl TransactionExecuteResult {
pub fn from_execute_result(
value: ExecuteResult,
denom_minor: &str,
fee: Option<DecCoin>,
) -> Result<TransactionExecuteResult, TypesError> {
let gas_info = GasInfo::from_validator_client_gas_info(value.gas_info, denom_minor)?;
let fee = gas_info.fee.clone();
Ok(TransactionExecuteResult {
gas_info,
gas_info: value.gas_info.into(),
transaction_hash: value.transaction_hash.to_string(),
data_json: ::serde_json::to_string_pretty(&value.data)?,
logs_json: ::serde_json::to_string_pretty(&value.logs)?,
@@ -97,30 +97,24 @@ pub struct RpcTransactionResponse {
pub tx_result_json: String,
pub block_height: u64,
pub transaction_hash: String,
pub gas_info: GasInfo,
// pub fee: MajorCurrencyAmount,
pub gas_used: Gas,
pub gas_wanted: Gas,
pub fee: Option<DecCoin>,
}
impl RpcTransactionResponse {
pub fn from_tx_response(
value: &TxResponse,
denom_minor: &str,
t: &TxResponse,
fee: Option<DecCoin>,
) -> Result<RpcTransactionResponse, TypesError> {
Ok(RpcTransactionResponse {
index: value.index,
gas_info: GasInfo::from_u64(
value.tx_result.gas_wanted.value(),
value.tx_result.gas_used.value(),
denom_minor,
)?,
transaction_hash: value.hash.to_string(),
tx_result_json: ::serde_json::to_string_pretty(&value.tx_result)?,
block_height: value.height.value(),
// wrong
// fee: MajorCurrencyAmount::from_decimal_and_denom(
// Decimal::new(Uint128::from(value.tx_result.gas_used.value())),
// denom_minor.to_string(),
// )?,
index: t.index,
gas_used: t.tx_result.gas_used.into(),
gas_wanted: t.tx_result.gas_wanted.into(),
transaction_hash: t.hash.to_string(),
tx_result_json: ::serde_json::to_string_pretty(&t.tx_result)?,
block_height: t.height.value(),
fee,
})
}
}
+35 -45
View File
@@ -1,10 +1,10 @@
use crate::currency::MajorCurrencyAmount;
use crate::currency::{DecCoin, RegisteredCoins};
use crate::error::TypesError;
use serde::{Deserialize, Serialize};
use vesting_contract::vesting::Account as VestingAccount;
use vesting_contract::vesting::VestingPeriod as VestingVestingPeriod;
use vesting_contract_common::OriginalVestingResponse as VestingOriginalVestingResponse;
use vesting_contract_common::PledgeData as VestingPledgeData;
use vesting_contract::vesting::Account as ContractVestingAccount;
use vesting_contract::vesting::VestingPeriod as ContractVestingPeriod;
use vesting_contract_common::OriginalVestingResponse as ContractOriginalVestingResponse;
use vesting_contract_common::PledgeData as ContractPledgeData;
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
@@ -13,25 +13,19 @@ use vesting_contract_common::PledgeData as VestingPledgeData;
)]
#[derive(Serialize, Deserialize, Debug)]
pub struct PledgeData {
pub amount: MajorCurrencyAmount,
pub amount: DecCoin,
pub block_time: u64,
}
impl TryFrom<VestingPledgeData> for PledgeData {
type Error = TypesError;
fn try_from(data: VestingPledgeData) -> Result<Self, Self::Error> {
let amount: MajorCurrencyAmount = data.amount().into();
Ok(Self {
amount,
block_time: data.block_time().seconds(),
})
}
}
impl PledgeData {
pub fn and_then(data: VestingPledgeData) -> Option<Self> {
data.try_into().ok()
pub fn from_vesting_contract(
pledge: ContractPledgeData,
reg: &RegisteredCoins,
) -> Result<Self, TypesError> {
Ok(PledgeData {
amount: reg.attempt_convert_to_display_dec_coin(pledge.amount.into())?,
block_time: pledge.block_time.seconds(),
})
}
}
@@ -42,20 +36,20 @@ impl PledgeData {
)]
#[derive(Serialize, Deserialize, Debug)]
pub struct OriginalVestingResponse {
amount: MajorCurrencyAmount,
amount: DecCoin,
number_of_periods: usize,
period_duration: u64,
}
impl TryFrom<VestingOriginalVestingResponse> for OriginalVestingResponse {
type Error = TypesError;
fn try_from(data: VestingOriginalVestingResponse) -> Result<Self, Self::Error> {
let amount = data.amount().into();
Ok(Self {
amount,
number_of_periods: data.number_of_periods(),
period_duration: data.period_duration(),
impl OriginalVestingResponse {
pub fn from_vesting_contract(
res: ContractOriginalVestingResponse,
reg: &RegisteredCoins,
) -> Result<Self, TypesError> {
Ok(OriginalVestingResponse {
amount: reg.attempt_convert_to_display_dec_coin(res.amount.into())?,
number_of_periods: res.number_of_periods,
period_duration: res.period_duration,
})
}
}
@@ -71,24 +65,20 @@ pub struct VestingAccountInfo {
staking_address: Option<String>,
start_time: u64,
periods: Vec<VestingPeriod>,
amount: MajorCurrencyAmount,
amount: DecCoin,
}
impl TryFrom<VestingAccount> for VestingAccountInfo {
type Error = TypesError;
fn try_from(account: VestingAccount) -> Result<Self, Self::Error> {
let mut periods = Vec::new();
for period in account.periods() {
periods.push(period.into());
}
let amount: MajorCurrencyAmount = account.coin().into();
Ok(Self {
impl VestingAccountInfo {
pub fn from_vesting_contract(
account: ContractVestingAccount,
reg: &RegisteredCoins,
) -> Result<Self, TypesError> {
Ok(VestingAccountInfo {
owner_address: account.owner_address().to_string(),
staking_address: account.staking_address().map(|a| a.to_string()),
start_time: account.start_time().seconds(),
periods,
amount,
periods: account.periods().into_iter().map(Into::into).collect(),
amount: reg.attempt_convert_to_display_dec_coin(account.coin.into())?,
})
}
}
@@ -104,8 +94,8 @@ pub struct VestingPeriod {
period_seconds: u64,
}
impl From<VestingVestingPeriod> for VestingPeriod {
fn from(period: VestingVestingPeriod) -> Self {
impl From<ContractVestingPeriod> for VestingPeriod {
fn from(period: ContractVestingPeriod) -> Self {
Self {
start_time: period.start_time,
period_seconds: period.period_seconds,
+3 -3
View File
@@ -62,7 +62,7 @@ pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respon
pub mod tests {
use super::*;
use bandwidth_claim_contract::payment::PagedPaymentResponse;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_binary};
@@ -91,11 +91,11 @@ pub mod tests {
// Contract balance should match what we initialized it as
assert_eq!(
coins(0, DENOM),
coins(0, MIX_DENOM.base),
vec![deps
.as_ref()
.querier
.query_balance(env.contract.address, DENOM)
.query_balance(env.contract.address, MIX_DENOM.base)
.unwrap()]
);
}
+7 -8
View File
@@ -63,11 +63,10 @@ mod tests {
use super::*;
use crate::support::tests::helpers::*;
use coconut_bandwidth_contract_common::deposit::DepositData;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, Addr};
use cw_multi_test::Executor;
use serde::de::Unexpected::Str;
#[test]
fn initialize_contract() {
@@ -84,20 +83,20 @@ mod tests {
// Contract balance should be 0
assert_eq!(
coins(0, DENOM),
coins(0, MIX_DENOM.base),
vec![deps
.as_ref()
.querier
.query_balance(env.contract.address, DENOM)
.query_balance(env.contract.address, MIX_DENOM.base)
.unwrap()]
);
}
#[test]
fn deposit_and_release() {
let init_funds = coins(10, DENOM);
let deposit_funds = coins(1, DENOM);
let release_funds = coins(2, DENOM);
let init_funds = coins(10, MIX_DENOM.base);
let deposit_funds = coins(1, MIX_DENOM.base);
let release_funds = coins(2, MIX_DENOM.base);
let mut app = mock_app(&init_funds);
let multisig_addr = String::from(MULTISIG_CONTRACT);
let pool_addr = String::from(POOL_CONTRACT);
@@ -157,7 +156,7 @@ mod tests {
&[],
)
.unwrap();
let pool_bal = app.wrap().query_balance(pool_addr, DENOM).unwrap();
let pool_bal = app.wrap().query_balance(pool_addr, MIX_DENOM.base).unwrap();
assert_eq!(pool_bal, deposit_funds[0]);
}
}
+2 -2
View File
@@ -5,7 +5,7 @@ use cosmwasm_std::StdError;
use cw_controllers::AdminError;
use thiserror::Error;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
/// Custom errors for contract failure conditions.
///
@@ -22,7 +22,7 @@ pub enum ContractError {
#[error("No coin was sent for voucher")]
NoCoin,
#[error("Wrong coin denomination, you must send {}", DENOM)]
#[error("Wrong coin denomination, you must send {}", MIX_DENOM.base)]
WrongDenom,
#[error("There aren't enough funds in the contract")]
@@ -4,7 +4,6 @@
#[cfg(test)]
pub mod helpers {
pub const OWNER: &str = "admin0001";
pub const SOMEBODY: &str = "somebody";
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
pub const POOL_CONTRACT: &str = "mix pool contract address";
@@ -11,7 +11,7 @@ use coconut_bandwidth_contract_common::events::{
DEPOSITED_FUNDS_EVENT_TYPE, DEPOSIT_ENCRYPTION_KEY, DEPOSIT_IDENTITY_KEY, DEPOSIT_INFO,
DEPOSIT_VALUE,
};
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
pub(crate) fn deposit_funds(
_deps: DepsMut<'_>,
@@ -25,7 +25,7 @@ pub(crate) fn deposit_funds(
if info.funds.len() > 1 {
return Err(ContractError::MultipleDenoms);
}
if info.funds[0].denom != DENOM {
if info.funds[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom);
}
@@ -45,10 +45,12 @@ pub(crate) fn release_funds(
info: MessageInfo,
funds: Coin,
) -> Result<Response, ContractError> {
if funds.denom != DENOM {
if funds.denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom);
}
let current_balance = deps.querier.query_balance(env.contract.address, DENOM)?;
let current_balance = deps
.querier
.query_balance(env.contract.address, MIX_DENOM.base)?;
if funds.amount > current_balance.amount {
return Err(ContractError::NotEnoughFunds);
}
@@ -90,7 +92,7 @@ mod tests {
Err(ContractError::NoCoin)
);
let coin = Coin::new(1000000, DENOM);
let coin = Coin::new(1000000, MIX_DENOM.base);
let second_coin = Coin::new(1000000, "some_denom");
let info = mock_info("requester", &[coin, second_coin.clone()]);
@@ -120,7 +122,7 @@ mod tests {
verification_key.clone(),
encryption_key.clone(),
);
let coin = Coin::new(deposit_value, DENOM);
let coin = Coin::new(deposit_value, MIX_DENOM.base);
let info = mock_info("requester", &[coin]);
let tx = deposit_funds(deps.as_mut(), env.clone(), info, data).unwrap();
@@ -169,7 +171,7 @@ mod tests {
let mut deps = helpers::init_contract();
let env = mock_env();
let invalid_admin = "invalid admin";
let funds = Coin::new(1, DENOM);
let funds = Coin::new(1, MIX_DENOM.base);
let err = release_funds(
deps.as_mut(),
@@ -205,7 +207,7 @@ mod tests {
fn valid_release() {
let mut deps = helpers::init_contract();
let env = mock_env();
let coin = Coin::new(1, DENOM);
let coin = Coin::new(1, MIX_DENOM.base);
deps.querier
.update_balance(env.contract.address.clone(), vec![coin.clone()]);
+3 -2
View File
@@ -475,7 +475,8 @@ pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respons
pub mod tests {
use super::*;
use crate::support::tests;
use config::defaults::{DEFAULT_NETWORK, DENOM};
use config::defaults::DEFAULT_NETWORK;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_binary};
use mixnet_contract_common::PagedMixnodeResponse;
@@ -507,7 +508,7 @@ pub mod tests {
// Contract balance should match what we initialized it as
assert_eq!(
coins(0, DENOM),
coins(0, MIX_DENOM.base),
tests::queries::query_contract_balance(env.contract.address, deps)
);
}
+4 -4
View File
@@ -200,7 +200,7 @@ pub(crate) fn query_mixnode_delegations_paged(
pub(crate) mod tests {
use super::*;
use crate::support::tests::test_helpers;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{coin, Addr, Storage};
use rand::Rng;
@@ -385,7 +385,7 @@ pub(crate) mod tests {
let delegation = Delegation::new(
delegation_owner.clone(),
node_identity.clone(),
coin(1234, DENOM),
coin(1234, MIX_DENOM.base),
1234,
None,
);
@@ -433,7 +433,7 @@ pub(crate) mod tests {
let delegation = Delegation::new(
delegation_owner2,
node_identity1.clone(),
coin(1234, DENOM),
coin(1234, MIX_DENOM.base),
1234,
None,
);
@@ -460,7 +460,7 @@ pub(crate) mod tests {
let delegation = Delegation::new(
delegation_owner1.clone(),
node_identity2,
coin(1234, DENOM),
coin(1234, MIX_DENOM.base),
1234,
None,
);
+3 -3
View File
@@ -77,7 +77,7 @@ mod tests {
mod reverse_mix_delegations {
use super::*;
use crate::support::tests::test_helpers;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::{coin, Order};
use mixnet_contract_common::Delegation;
@@ -87,7 +87,7 @@ mod tests {
let mut deps = test_helpers::init_contract();
let node_identity: IdentityKey = "foo".into();
let delegation_owner = Addr::unchecked("bar");
let delegation = coin(12345, DENOM);
let delegation = coin(12345, MIX_DENOM.base);
let dummy_data = Delegation::new(
delegation_owner.clone(),
@@ -125,7 +125,7 @@ mod tests {
let node_identity2: IdentityKey = "foo2".into();
let delegation_owner1 = Addr::unchecked("bar");
let delegation_owner2 = Addr::unchecked("bar2");
let delegation = coin(12345, DENOM);
let delegation = coin(12345, MIX_DENOM.base);
assert!(test_helpers::read_delegation(
deps.as_ref().storage,
@@ -6,7 +6,7 @@ use super::storage::{self, PENDING_DELEGATION_EVENTS};
use crate::error::ContractError;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnodes_storage;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{
coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, Event, MessageInfo, Order,
Response, Storage, Uint128, WasmMsg,
@@ -85,7 +85,7 @@ fn validate_delegation_stake(mut delegation: Vec<Coin>) -> Result<Coin, Contract
}
// check that the denomination is correct
if delegation[0].denom != DENOM {
if delegation[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom {});
}
@@ -389,7 +389,7 @@ pub(crate) fn try_reconcile_undelegation(
.as_ref()
.unwrap_or(&pending_undelegate.delegate())
.to_string(),
amount: coins(total_funds.u128(), DENOM),
amount: coins(total_funds.u128(), MIX_DENOM.base),
})
} else {
None
@@ -401,7 +401,7 @@ pub(crate) fn try_reconcile_undelegation(
let msg = Some(VestingContractExecuteMsg::TrackUndelegation {
owner: pending_undelegate.delegate().as_str().to_string(),
mix_identity: pending_undelegate.mix_identity(),
amount: Coin::new(total_funds.u128(), DENOM),
amount: Coin::new(total_funds.u128(), MIX_DENOM.base),
});
wasm_msg = Some(wasm_execute(proxy, &msg, vec![one_ucoin()])?);
@@ -492,7 +492,7 @@ mod tests {
assert_eq!(
Err(ContractError::MultipleDenoms),
validate_delegation_stake(vec![
coin(123, DENOM),
coin(123, MIX_DENOM.base),
coin(123, "BTC"),
coin(123, "DOGE")
])
@@ -511,16 +511,16 @@ mod tests {
fn stake_coin_must_have_value_greater_than_zero() {
assert_eq!(
Err(ContractError::EmptyDelegation),
validate_delegation_stake(coins(0, DENOM))
validate_delegation_stake(coins(0, MIX_DENOM.base))
)
}
#[test]
fn stake_can_have_any_positive_value() {
// this might change in the future, but right now an arbitrary (positive) value can be delegated
assert!(validate_delegation_stake(coins(1, DENOM)).is_ok());
assert!(validate_delegation_stake(coins(123, DENOM)).is_ok());
assert!(validate_delegation_stake(coins(10000000000, DENOM)).is_ok());
assert!(validate_delegation_stake(coins(1, MIX_DENOM.base)).is_ok());
assert!(validate_delegation_stake(coins(123, MIX_DENOM.base)).is_ok());
assert!(validate_delegation_stake(coins(10000000000, MIX_DENOM.base)).is_ok());
}
}
@@ -543,7 +543,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("sender", &coins(123, DENOM)),
mock_info("sender", &coins(123, MIX_DENOM.base)),
"non-existent-mix-identity".into(),
)
);
@@ -559,7 +559,7 @@ mod tests {
deps.as_mut(),
);
let delegation_owner = Addr::unchecked("sender");
let delegation = coin(123, DENOM);
let delegation = coin(123, MIX_DENOM.base);
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
@@ -616,7 +616,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(123, DENOM)),
mock_info(delegation_owner.as_str(), &coins(123, MIX_DENOM.base)),
identity,
)
);
@@ -637,7 +637,7 @@ mod tests {
tests::fixtures::good_mixnode_pledge(),
deps.as_mut(),
);
let delegation = coin(123, DENOM);
let delegation = coin(123, MIX_DENOM.base);
let delegation_owner = Addr::unchecked("sender");
assert!(try_delegate_to_mixnode(
deps.as_mut(),
@@ -687,8 +687,8 @@ mod tests {
deps.as_mut(),
);
let delegation_owner = Addr::unchecked("sender");
let delegation1 = coin(100, DENOM);
let delegation2 = coin(50, DENOM);
let delegation1 = coin(100, MIX_DENOM.base);
let delegation2 = coin(50, MIX_DENOM.base);
let mut env = mock_env();
@@ -715,7 +715,7 @@ mod tests {
// let expected = Delegation::new(
// delegation_owner.clone(),
// identity.clone(),
// coin(delegation1.amount.u128() + delegation2.amount.u128(), DENOM),
// coin(delegation1.amount.u128() + delegation2.amount.u128(), MIX_DENOM.base),
// mock_env().block.height,
// None,
// );
@@ -750,7 +750,7 @@ mod tests {
deps.as_mut(),
);
let delegation_owner = Addr::unchecked("sender");
let delegation = coin(100, DENOM);
let delegation = coin(100, MIX_DENOM.base);
let env1 = mock_env();
let mut env2 = mock_env();
let initial_height = env1.block.height;
@@ -815,8 +815,8 @@ mod tests {
);
let delegation_owner1 = Addr::unchecked("sender1");
let delegation_owner2 = Addr::unchecked("sender2");
let delegation1 = coin(100, DENOM);
let delegation2 = coin(120, DENOM);
let delegation1 = coin(100, MIX_DENOM.base);
let delegation2 = coin(120, MIX_DENOM.base);
let env1 = mock_env();
let mut env2 = mock_env();
let initial_height = env1.block.height;
@@ -891,7 +891,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(100, DENOM)),
mock_info(delegation_owner.as_str(), &coins(100, MIX_DENOM.base)),
identity.clone(),
)
.unwrap();
@@ -903,7 +903,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(50, DENOM)),
mock_info(delegation_owner.as_str(), &coins(50, MIX_DENOM.base)),
identity,
)
);
@@ -928,14 +928,14 @@ mod tests {
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(123, DENOM)),
mock_info(delegation_owner.as_str(), &coins(123, MIX_DENOM.base)),
identity1.clone(),
)
.is_ok());
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(42, DENOM)),
mock_info(delegation_owner.as_str(), &coins(42, MIX_DENOM.base)),
identity2.clone(),
)
.is_ok());
@@ -945,7 +945,7 @@ mod tests {
let expected1 = Delegation::new(
delegation_owner.clone(),
identity1.clone(),
coin(123, DENOM),
coin(123, MIX_DENOM.base),
mock_env().block.height,
None,
);
@@ -953,7 +953,7 @@ mod tests {
let expected2 = Delegation::new(
delegation_owner.clone(),
identity2.clone(),
coin(42, DENOM),
coin(42, MIX_DENOM.base),
mock_env().block.height,
None,
);
@@ -989,8 +989,8 @@ mod tests {
tests::fixtures::good_mixnode_pledge(),
deps.as_mut(),
);
let delegation1 = coin(123, DENOM);
let delegation2 = coin(234, DENOM);
let delegation1 = coin(123, MIX_DENOM.base);
let delegation2 = coin(234, MIX_DENOM.base);
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
@@ -1026,7 +1026,7 @@ mod tests {
deps.as_mut(),
);
let delegation_owner = Addr::unchecked("sender");
let delegation_amount = coin(100, DENOM);
let delegation_amount = coin(100, MIX_DENOM.base);
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
@@ -1118,7 +1118,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(100, DENOM)),
mock_info(delegation_owner.as_str(), &coins(100, MIX_DENOM.base)),
identity.clone(),
)
.unwrap();
@@ -1137,7 +1137,7 @@ mod tests {
let expected_response = Response::new()
.add_message(BankMsg::Send {
to_address: delegation_owner.clone().into(),
amount: coins(100, DENOM),
amount: coins(100, MIX_DENOM.base),
})
.add_event(new_undelegation_event(
&delegation_owner,
@@ -1188,14 +1188,14 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(100, DENOM)),
mock_info(delegation_owner.as_str(), &coins(100, MIX_DENOM.base)),
identity.clone(),
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
let delegation = query_mixnode_delegation(
let _delegation = query_mixnode_delegation(
&deps.storage,
&deps.api,
identity.clone(),
@@ -1207,7 +1207,7 @@ mod tests {
let expected_response = Response::new()
.add_message(BankMsg::Send {
to_address: delegation_owner.clone().into(),
amount: coins(100, DENOM),
amount: coins(100, MIX_DENOM.base),
})
.add_event(new_undelegation_event(
&delegation_owner,
@@ -1251,8 +1251,8 @@ mod tests {
);
let delegation_owner1 = Addr::unchecked("sender1");
let delegation_owner2 = Addr::unchecked("sender2");
let delegation1 = coin(123, DENOM);
let delegation2 = coin(234, DENOM);
let delegation1 = coin(123, MIX_DENOM.base);
let delegation2 = coin(234, MIX_DENOM.base);
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
+3 -3
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{Addr, StdError};
use mixnet_contract_common::{error::MixnetContractError, IdentityKey};
use thiserror::Error;
@@ -33,13 +33,13 @@ pub enum ContractError {
#[error("MIXNET ({}): Unauthorized", line!())]
Unauthorized,
#[error("MIXNET ({}): Wrong coin denomination, you must send {}", line!(), DENOM)]
#[error("MIXNET ({}): Wrong coin denomination, you must send {}", line!(), MIX_DENOM.base)]
WrongDenom,
#[error("MIXNET ({}): Received multiple coin types during staking", line!())]
MultipleDenoms,
#[error("MIXNET ({}): No coin was sent for the bonding, you must send {}", line!(), DENOM)]
#[error("MIXNET ({}): No coin was sent for the bonding, you must send {}", line!(), MIX_DENOM.base)]
NoBondFound,
#[error("MIXNET ({}): Provided active set size is bigger than the rewarded set", line!())]
+2 -2
View File
@@ -36,7 +36,7 @@ pub(crate) fn gateways<'a>() -> IndexedMap<'a, IdentityKeyRef<'a>, GatewayBond,
mod tests {
use super::super::storage;
use crate::support::tests;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::StdResult;
use cosmwasm_std::Storage;
@@ -84,7 +84,7 @@ mod tests {
let pledge_amount = 1000;
let gateway_bond = GatewayBond {
pledge_amount: coin(pledge_amount, DENOM),
pledge_amount: coin(pledge_amount, MIX_DENOM.base),
owner: node_owner,
block_height: 12_345,
gateway: Gateway {
@@ -5,7 +5,7 @@ use super::storage;
use crate::error::ContractError;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::support::helpers::{ensure_no_existing_bond, validate_node_identity_signature};
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{
wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128,
};
@@ -203,7 +203,7 @@ fn validate_gateway_pledge(
}
// check that the denomination is correct
if pledge[0].denom != DENOM {
if pledge[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom {});
}
@@ -226,7 +226,7 @@ pub mod tests {
use crate::gateways::transactions::validate_gateway_pledge;
use crate::support::tests;
use crate::support::tests::test_helpers;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coins, BankMsg, Response};
use cosmwasm_std::{from_binary, Addr, Uint128};
@@ -238,7 +238,7 @@ pub mod tests {
// if we fail validation (by say not sending enough funds
let insufficient_bond = Into::<u128>::into(INITIAL_GATEWAY_PLEDGE) - 1;
let info = mock_info("anyone", &coins(insufficient_bond, DENOM));
let info = mock_info("anyone", &coins(insufficient_bond, MIX_DENOM.base));
let (msg, _) = tests::messages::valid_bond_gateway_msg("anyone");
// we are informed that we didn't send enough funds
+4 -4
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{StdResult, Storage, Uint128};
use cw_storage_plus::{Index, IndexList, IndexedSnapshotMap, Map, Strategy, UniqueIndex};
use mixnet_contract_common::{
@@ -171,7 +171,7 @@ pub(crate) fn read_full_mixnode_bond(
Ok(Some(MixNodeBond {
pledge_amount: stored_bond.pledge_amount,
total_delegation: Coin {
denom: DENOM.to_owned(),
denom: MIX_DENOM.base.to_owned(),
amount: total_delegation.unwrap_or_default(),
},
owner: stored_bond.owner,
@@ -190,7 +190,7 @@ mod tests {
use super::super::storage;
use super::*;
use crate::support::tests;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::{coin, Addr, Uint128};
use mixnet_contract_common::{IdentityKey, MixNode};
@@ -223,7 +223,7 @@ mod tests {
let pledge_value = 1000000000;
let mixnode_bond = StoredMixnodeBond {
pledge_amount: coin(pledge_value, DENOM),
pledge_amount: coin(pledge_value, MIX_DENOM.base),
owner: node_owner,
layer: Layer::One,
block_height: 12_345,
@@ -7,7 +7,7 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::layer_queries::query_layer_distribution;
use crate::mixnodes::storage::StoredMixnodeBond;
use crate::support::helpers::{ensure_no_existing_bond, validate_node_identity_signature};
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{
wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Storage, Uint128,
};
@@ -364,7 +364,7 @@ fn validate_mixnode_pledge(
}
// check that the denomination is correct
if pledge[0].denom != DENOM {
if pledge[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom {});
}
@@ -381,15 +381,13 @@ fn validate_mixnode_pledge(
#[cfg(test)]
pub mod tests {
use std::f64::MIN;
use super::*;
use crate::contract::{execute, query, INITIAL_MIXNODE_PLEDGE};
use crate::error::ContractError;
use crate::mixnodes::transactions::validate_mixnode_pledge;
use crate::support::tests;
use crate::support::tests::test_helpers;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coins, BankMsg, Response};
use cosmwasm_std::{from_binary, Addr, Uint128};
@@ -404,7 +402,7 @@ pub mod tests {
// if we don't send enough funds
let insufficient_bond = Into::<u128>::into(INITIAL_MIXNODE_PLEDGE) - 1;
let info = mock_info("anyone", &coins(insufficient_bond, DENOM));
let info = mock_info("anyone", &coins(insufficient_bond, MIX_DENOM.base));
let (msg, _) = tests::messages::valid_bond_mixnode_msg("anyone");
// we are informed that we didn't send enough funds
+5 -2
View File
@@ -79,7 +79,7 @@ pub(crate) mod tests {
use crate::delegations::transactions::try_delegate_to_mixnode;
use crate::interval::storage::{save_epoch, save_epoch_reward_params};
use crate::rewards::transactions::try_reward_mixnode;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{coin, Addr};
use mixnet_contract_common::{
Interval, RewardingResult, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT,
@@ -181,7 +181,10 @@ pub(crate) mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env.clone(),
mock_info(&*format!("delegator{:04}", i), &[coin(200_000000, DENOM)]),
mock_info(
&*format!("delegator{:04}", i),
&[coin(200_000000, MIX_DENOM.base)],
),
node_identity.clone(),
)
.unwrap();
+30 -28
View File
@@ -14,7 +14,7 @@ use crate::mixnodes::storage::mixnodes;
use crate::mixnodes::storage::{self as mixnodes_storage, StoredMixnodeBond};
use crate::rewards::helpers;
use crate::support::helpers::is_authorized;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{
coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, MessageInfo, Order, Response,
Storage, Uint128,
@@ -105,7 +105,7 @@ fn _try_claim_operator_reward(
let return_tokens = BankMsg::Send {
to_address: proxy.as_ref().unwrap_or(&owner).to_string(),
amount: coins(reward.u128(), DENOM),
amount: coins(reward.u128(), MIX_DENOM.base),
};
let mut response = Response::default()
@@ -115,7 +115,7 @@ fn _try_claim_operator_reward(
if let Some(proxy) = proxy {
let msg = Some(VestingContractExecuteMsg::TrackReward {
address: owner.to_string(),
amount: Coin::new(reward.u128(), DENOM),
amount: Coin::new(reward.u128(), MIX_DENOM.base),
});
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
@@ -153,7 +153,7 @@ pub fn _try_claim_delegator_reward(
let return_tokens = BankMsg::Send {
to_address: proxy.as_ref().unwrap_or(&owner).to_string(),
amount: coins(reward.u128(), DENOM),
amount: coins(reward.u128(), MIX_DENOM.base),
};
let mut response =
@@ -169,7 +169,7 @@ pub fn _try_claim_delegator_reward(
if let Some(proxy) = proxy {
let msg = Some(VestingContractExecuteMsg::TrackReward {
address: owner.to_string(),
amount: Coin::new(reward.u128(), DENOM),
amount: Coin::new(reward.u128(), MIX_DENOM.base),
});
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
@@ -436,7 +436,7 @@ pub fn _try_compound_delegator_reward(
owner_address,
Coin {
amount: compounded_delegation,
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
proxy,
)?;
@@ -721,7 +721,7 @@ pub mod tests {
use crate::support::tests;
use crate::support::tests::test_helpers;
use az::CheckedCast;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coin, coins, Addr, StdError, Timestamp, Uint128};
use mixnet_contract_common::events::{
@@ -737,7 +737,7 @@ pub mod tests {
let mut deps = test_helpers::init_contract();
let mut env = mock_env();
let sender = rewarding_validator_address(&deps.storage).unwrap();
let info = mock_info(&sender, &coins(1000, DENOM));
let info = mock_info(&sender, &coins(1000, MIX_DENOM.base));
crate::interval::transactions::init_epoch(&mut deps.storage, env.clone()).unwrap();
// bond the node
@@ -878,7 +878,7 @@ pub mod tests {
let initial_bond = 10000_000000;
let initial_delegation = 20000_000000;
let mixnode_bond = StoredMixnodeBond {
pledge_amount: coin(initial_bond, DENOM),
pledge_amount: coin(initial_bond, MIX_DENOM.base),
owner: node_owner,
layer: Layer::One,
block_height: env.block.height,
@@ -918,7 +918,7 @@ pub mod tests {
&Delegation::new(
Addr::unchecked("delegator"),
node_identity.clone(),
coin(initial_delegation, DENOM),
coin(initial_delegation, MIX_DENOM.base),
env.block.height,
None,
),
@@ -1098,7 +1098,7 @@ pub mod tests {
assert_eq!(circulating_supply, 750_000_000_000_000u128);
let sender = Addr::unchecked("alice");
let stake = coins(10_000_000_000, DENOM);
let stake = coins(10_000_000_000, MIX_DENOM.base);
let keypair = crypto::asymmetric::identity::KeyPair::new(&mut thread_rng());
let owner_signature = keypair
@@ -1151,14 +1151,14 @@ pub mod tests {
let node_owner: Addr = Addr::unchecked("johnny");
let node_identity_2 = test_helpers::add_mixnode(
node_owner.as_str(),
coins(10_000_000_000, DENOM),
coins(10_000_000_000, MIX_DENOM.base),
deps.as_mut(),
);
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alice_d1", &[coin(8000_000000, DENOM)]),
mock_info("alice_d1", &[coin(8000_000000, MIX_DENOM.base)]),
node_identity_1.clone(),
)
.unwrap();
@@ -1166,7 +1166,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alice_d2", &[coin(2000_000000, DENOM)]),
mock_info("alice_d2", &[coin(2000_000000, MIX_DENOM.base)]),
node_identity_1.clone(),
)
.unwrap();
@@ -1174,7 +1174,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("bob_d1", &[coin(8000_000000, DENOM)]),
mock_info("bob_d1", &[coin(8000_000000, MIX_DENOM.base)]),
node_identity_2.clone(),
)
.unwrap();
@@ -1182,7 +1182,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("bob_d2", &[coin(2000_000000, DENOM)]),
mock_info("bob_d2", &[coin(2000_000000, MIX_DENOM.base)]),
node_identity_2.clone(),
)
.unwrap();
@@ -1190,14 +1190,14 @@ pub mod tests {
let node_owner: Addr = Addr::unchecked("alicebob");
let node_identity_3 = test_helpers::add_mixnode(
node_owner.as_str(),
coins(10_000_000_000 * 2, DENOM),
coins(10_000_000_000 * 2, MIX_DENOM.base),
deps.as_mut(),
);
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alicebob_d1", &[coin(8000_000000 * 2, DENOM)]),
mock_info("alicebob_d1", &[coin(8000_000000 * 2, MIX_DENOM.base)]),
node_identity_3.clone(),
)
.unwrap();
@@ -1205,7 +1205,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alicebob_d2", &[coin(2000_000000 * 2, DENOM)]),
mock_info("alicebob_d2", &[coin(2000_000000 * 2, MIX_DENOM.base)]),
node_identity_3.clone(),
)
.unwrap();
@@ -1216,7 +1216,6 @@ pub mod tests {
)
.unwrap();
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
env.block.height += 2 * constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
let mix_1 = mixnodes_storage::read_full_mixnode_bond(&deps.storage, &node_identity_1)
@@ -1305,7 +1304,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env.clone(),
mock_info("alice_d1", &[coin(8000_000000, DENOM)]),
mock_info("alice_d1", &[coin(8000_000000, MIX_DENOM.base)]),
node_identity_1.clone(),
)
.unwrap();
@@ -1506,7 +1505,7 @@ pub mod tests {
);
assert_eq!(mix_2_reward_result.reward().int(), 129557u128);
let mix_3_reward_result = mix_3.reward(&params3);
let _mix_3_reward_result = mix_3.reward(&params3);
// assert_eq!(mix_3_reward_result.reward().int(), mix_1_reward_result.reward().int() + mix_2_reward_result.reward().int());
}
@@ -1533,14 +1532,14 @@ pub mod tests {
let node_owner: Addr = Addr::unchecked("alice");
let node_identity = test_helpers::add_mixnode(
node_owner.as_str(),
coins(10_000_000_000, DENOM),
coins(10_000_000_000, MIX_DENOM.base),
deps.as_mut(),
);
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alice_d1", &[coin(8000_000000, DENOM)]),
mock_info("alice_d1", &[coin(8000_000000, MIX_DENOM.base)]),
node_identity.clone(),
)
.unwrap();
@@ -1548,7 +1547,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alice_d2", &[coin(2000_000000, DENOM)]),
mock_info("alice_d2", &[coin(2000_000000, MIX_DENOM.base)]),
node_identity.clone(),
)
.unwrap();
@@ -1698,7 +1697,7 @@ pub mod tests {
#[allow(clippy::inconsistent_digit_grouping)]
let node_identity = test_helpers::add_mixnode(
node_owner.as_str(),
coins(10000_000_000, DENOM),
coins(10000_000_000, MIX_DENOM.base),
deps.as_mut(),
);
@@ -1725,7 +1724,7 @@ pub mod tests {
#[allow(clippy::inconsistent_digit_grouping)]
let node_identity = test_helpers::add_mixnode(
node_owner.as_str(),
coins(10000_000_000, DENOM),
coins(10000_000_000, MIX_DENOM.base),
deps.as_mut(),
);
@@ -1733,7 +1732,10 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env.clone(),
mock_info(&*format!("delegator{:04}", i), &[coin(2000_000000, DENOM)]),
mock_info(
&*format!("delegator{:04}", i),
&[coin(2000_000000, MIX_DENOM.base)],
),
node_identity.clone(),
)
.unwrap();
@@ -1,7 +1,7 @@
use crate::contract::INITIAL_MIXNODE_PLEDGE;
use crate::mixnodes::storage as mixnodes_storage;
use crate::{mixnodes::storage::StoredMixnodeBond, support::tests};
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{coin, Addr, Coin};
use mixnet_contract_common::reward_params::NodeRewardParams;
use mixnet_contract_common::{Gateway, GatewayBond, Layer, MixNode};
@@ -37,7 +37,7 @@ pub fn gateway_bond_fixture(owner: &str) -> GatewayBond {
..tests::fixtures::gateway_fixture()
};
GatewayBond::new(
coin(50, DENOM),
coin(50, MIX_DENOM.base),
Addr::unchecked(owner),
12_345,
gateway,
@@ -47,7 +47,7 @@ pub fn gateway_bond_fixture(owner: &str) -> GatewayBond {
pub(crate) fn stored_mixnode_bond_fixture(owner: &str) -> mixnodes_storage::StoredMixnodeBond {
StoredMixnodeBond::new(
coin(50, DENOM),
coin(50, MIX_DENOM.base),
Addr::unchecked(owner),
Layer::One,
12_345,
@@ -64,14 +64,14 @@ pub(crate) fn stored_mixnode_bond_fixture(owner: &str) -> mixnodes_storage::Stor
pub fn good_mixnode_pledge() -> Vec<Coin> {
vec![Coin {
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
amount: INITIAL_MIXNODE_PLEDGE,
}]
}
pub fn good_gateway_pledge() -> Vec<Coin> {
vec![Coin {
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
amount: INITIAL_MIXNODE_PLEDGE,
}]
}
+2 -2
View File
@@ -17,7 +17,7 @@ pub mod test_helpers {
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::transactions::try_add_mixnode;
use crate::support::tests;
use config::defaults::{DEFAULT_NETWORK, DENOM};
use config::defaults::{DEFAULT_NETWORK, MIX_DENOM};
use cosmwasm_std::testing::mock_dependencies;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::testing::mock_info;
@@ -111,7 +111,7 @@ pub mod test_helpers {
let delegation = Delegation {
owner: Addr::unchecked(owner.into()),
node_identity: mix.into(),
amount: coin(12345, DENOM),
amount: coin(12345, MIX_DENOM.base),
block_height: block_height,
proxy: None,
};
@@ -1,4 +1,4 @@
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{
from_binary,
testing::{mock_env, MockApi, MockQuerier, MockStorage},
@@ -45,5 +45,5 @@ pub fn query_contract_balance(
deps: OwnedDeps<MockStorage, MockApi, MockQuerier>,
) -> Vec<Coin> {
let querier = deps.as_ref().querier;
vec![querier.query_balance(address, DENOM).unwrap()]
vec![querier.query_balance(address, MIX_DENOM.base).unwrap()]
}
+8 -5
View File
@@ -4,7 +4,7 @@ use crate::traits::{
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
};
use crate::vesting::{populate_vesting_periods, Account};
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
Response, Timestamp,
@@ -147,8 +147,11 @@ pub fn try_withdraw_vested_coins(
info: MessageInfo,
deps: DepsMut<'_>,
) -> Result<Response, ContractError> {
if amount.denom != DENOM {
return Err(ContractError::WrongDenom(amount.denom, DENOM.to_string()));
if amount.denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom(
amount.denom,
MIX_DENOM.base.to_string(),
));
}
let address = info.sender.clone();
@@ -611,10 +614,10 @@ fn validate_funds(funds: &[Coin]) -> Result<Coin, ContractError> {
return Err(ContractError::MultipleDenoms);
}
if funds[0].denom != DENOM {
if funds[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom(
funds[0].denom.clone(),
DENOM.to_string(),
MIX_DENOM.base.to_string(),
));
}
+3 -3
View File
@@ -2,7 +2,7 @@
pub mod helpers {
use crate::contract::instantiate;
use crate::vesting::{populate_vesting_periods, Account};
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::{Addr, Coin, Empty, Env, MemoryStorage, OwnedDeps, Storage, Uint128};
use vesting_contract_common::messages::{InitMsg, VestingSpecification};
@@ -31,7 +31,7 @@ pub mod helpers {
Some(Addr::unchecked("staking")),
Coin {
amount: Uint128::new(1_000_000_000_000),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
start_time_ts,
periods,
@@ -50,7 +50,7 @@ pub mod helpers {
Some(Addr::unchecked("staking")),
Coin {
amount: Uint128::new(1_000_000_000_000),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
start_time,
periods,
+5 -5
View File
@@ -25,11 +25,11 @@ fn generate_storage_key(storage: &mut dyn Storage) -> Result<u32, ContractError>
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Account {
owner_address: Addr,
staking_address: Option<Addr>,
start_time: Timestamp,
periods: Vec<VestingPeriod>,
coin: Coin,
pub owner_address: Addr,
pub staking_address: Option<Addr>,
pub start_time: Timestamp,
pub periods: Vec<VestingPeriod>,
pub coin: Coin,
storage_key: u32,
}
@@ -1,7 +1,7 @@
use crate::errors::ContractError;
use crate::storage::{delete_account, save_account, DELEGATIONS};
use crate::traits::VestingAccount;
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{Addr, Coin, Env, Order, Storage, Timestamp, Uint128};
use vesting_contract_common::{OriginalVestingResponse, Period};
@@ -38,7 +38,7 @@ impl VestingAccount for Account {
.u128(),
),
),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
})
}
@@ -54,7 +54,7 @@ impl VestingAccount for Account {
.u128()
.saturating_sub(self.locked_coins(block_time, env, storage)?.amount.u128()),
),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
})
}
@@ -69,15 +69,15 @@ impl VestingAccount for Account {
let amount = match period {
Period::Before => Coin {
amount: Uint128::new(0),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
Period::In(idx) => Coin {
amount: Uint128::new(self.tokens_per_period()? * idx as u128),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
Period::After => Coin {
amount: self.coin.amount,
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
};
Ok(amount)
@@ -91,7 +91,7 @@ impl VestingAccount for Account {
Ok(Coin {
amount: self.get_original_vesting().amount().amount
- self.get_vested_coins(block_time, env)?.amount,
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
})
}
@@ -143,7 +143,7 @@ impl VestingAccount for Account {
Ok(Coin {
amount,
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
})
}
@@ -161,7 +161,7 @@ impl VestingAccount for Account {
Ok(Coin {
amount,
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
})
}
@@ -197,7 +197,7 @@ impl VestingAccount for Account {
Ok(Coin {
amount,
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
})
}
@@ -217,12 +217,12 @@ impl VestingAccount for Account {
let amount = bond.amount().amount - bonded_free.amount;
Ok(Coin {
amount,
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
})
} else {
Ok(Coin {
amount: Uint128::zero(),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
})
}
}
+14 -14
View File
@@ -44,7 +44,7 @@ mod tests {
use crate::traits::DelegatingAccount;
use crate::traits::VestingAccount;
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
use config::defaults::DENOM;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128};
use mixnet_contract_common::{Gateway, MixNode};
@@ -55,7 +55,7 @@ mod tests {
fn test_account_creation() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("not_admin", &coins(1_000_000_000_000, DENOM));
let info = mock_info("not_admin", &coins(1_000_000_000_000, MIX_DENOM.base));
let msg = ExecuteMsg::CreateAccount {
owner_address: "owner".to_string(),
staking_address: Some("staking".to_string()),
@@ -65,7 +65,7 @@ mod tests {
let response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone());
assert!(response.is_err());
let info = mock_info("admin", &coins(1_000_000_000_000, DENOM));
let info = mock_info("admin", &coins(1_000_000_000_000, MIX_DENOM.base));
let _response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone());
let created_account = load_account(&Addr::unchecked("owner"), &deps.storage)
.unwrap()
@@ -131,7 +131,7 @@ mod tests {
let msg = ExecuteMsg::WithdrawVestedCoins {
amount: Coin {
amount: Uint128::new(1),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
};
let info = mock_info("new_owner", &[]);
@@ -262,7 +262,7 @@ mod tests {
let delegation = Coin {
amount: Uint128::new(500_000_000_000),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
};
let ok = account.try_delegate_to_mixnode(
@@ -388,7 +388,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -399,7 +399,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(500_000_000_000),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -414,7 +414,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(500_000_000_001),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -510,7 +510,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -522,7 +522,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(500_000_000_000),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -538,7 +538,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(500_000_000_001),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -630,7 +630,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -642,7 +642,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(500_000_000_000),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -658,7 +658,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(500_000_000_001),
denom: DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -0,0 +1,2 @@
export interface AppEnv { ADMIN_ADDRESS: string | null, SHOW_TERMINAL: string | null, }
@@ -0,0 +1,2 @@
export interface ValidatorUrl { url: string, name: string | null, }
@@ -0,0 +1,2 @@
export interface Validator { nymd_url: string, nymd_name: string | null, api_url: string | null, }
@@ -28,6 +28,7 @@ impl Network {
self.to_string().to_lowercase()
}
#[deprecated]
pub fn denom(&self) -> Denom {
match self {
// network defaults should be correctly formatted
@@ -36,6 +37,22 @@ impl Network {
Network::MAINNET => Denom::from_str(mainnet::DENOM).unwrap(),
}
}
pub fn base_mix_denom(&self) -> &str {
match self {
Network::QA => qa::MIX_DENOM.base,
Network::SANDBOX => sandbox::MIX_DENOM.base,
Network::MAINNET => mainnet::MIX_DENOM.base,
}
}
pub fn display_mix_denom(&self) -> &str {
match self {
Network::QA => qa::MIX_DENOM.display,
Network::SANDBOX => sandbox::MIX_DENOM.display,
Network::MAINNET => mainnet::MIX_DENOM.display,
}
}
}
impl Default for Network {
+5
View File
@@ -1,4 +1,5 @@
use nym_types::error::TypesError;
use nym_wallet_types::network::Network;
use serde::{Serialize, Serializer};
use std::io;
use std::num::ParseIntError;
@@ -104,6 +105,10 @@ pub enum BackendError {
FailedToDeriveAddress,
#[error("{0}")]
ValueParseError(#[from] ParseIntError),
#[error("The provided coin has an unknown denomination - {0}")]
UnknownCoinDenom(String),
#[error("Network {network} doesn't have any associated registered coin denoms")]
NoCoinsRegistered { network: Network },
}
impl Serialize for BackendError {
+2 -4
View File
@@ -4,9 +4,7 @@
)]
use mixnet_contract_common::{Gateway, MixNode};
use std::sync::Arc;
use tauri::Menu;
use tokio::sync::RwLock;
mod config;
mod error;
@@ -24,14 +22,14 @@ use crate::operations::simulate;
use crate::operations::validator_api;
use crate::operations::vesting;
use crate::state::State;
use crate::state::WalletState;
fn main() {
dotenv::dotenv().ok();
setup_logging();
tauri::Builder::default()
.manage(Arc::new(RwLock::new(State::default())))
.manage(WalletState::default())
.invoke_handler(tauri::generate_handler![
mixnet::account::add_account_for_password,
mixnet::account::connect_with_mnemonic,
+9 -14
View File
@@ -1,20 +1,15 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::error::BackendError;
use crate::state::WalletState;
use nym_wallet_types::network::Network as WalletNetwork;
use nym_wallet_types::network_config::{Validator, ValidatorUrl, ValidatorUrls};
use crate::error::BackendError;
use crate::state::State;
#[tauri::command]
pub async fn get_validator_nymd_urls(
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<ValidatorUrls, BackendError> {
let state = state.read().await;
let urls: Vec<ValidatorUrl> = state.get_nymd_urls(network).collect();
@@ -24,7 +19,7 @@ pub async fn get_validator_nymd_urls(
#[tauri::command]
pub async fn get_validator_api_urls(
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<ValidatorUrls, BackendError> {
let state = state.read().await;
let urls: Vec<ValidatorUrl> = state.get_api_urls(network).collect();
@@ -35,7 +30,7 @@ pub async fn get_validator_api_urls(
pub async fn select_validator_nymd_url(
url: &str,
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
log::debug!("Selecting new validator nymd_url for {network}: {url}");
state
@@ -49,7 +44,7 @@ pub async fn select_validator_nymd_url(
pub async fn select_validator_api_url(
url: &str,
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
log::debug!("Selecting new validator api_url for {network}: {url}");
state.write().await.select_validator_api_url(url, network)?;
@@ -60,7 +55,7 @@ pub async fn select_validator_api_url(
pub async fn add_validator(
validator: Validator,
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
log::debug!("Add validator for {network}: {validator}");
let url = validator.try_into()?;
@@ -72,7 +67,7 @@ pub async fn add_validator(
pub async fn remove_validator(
validator: Validator,
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
log::debug!("Remove validator for {network}: {validator}");
let url = validator.try_into()?;
@@ -83,7 +78,7 @@ pub async fn remove_validator(
// Update the list of validators by fecthing additional ones remotely. If it fails, just ignore.
#[tauri::command]
pub async fn update_validator_urls(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
let mut w_state = state.write().await;
let _r = w_state.fetch_updated_validator_urls().await;
@@ -1,8 +1,7 @@
use crate::config::{Config, CUSTOM_SIMULATED_GAS_MULTIPLIER};
use crate::error::BackendError;
use crate::network_config;
use crate::nymd_client;
use crate::state::{State, WalletAccountIds};
use crate::state::{WalletAccountIds, WalletState};
use crate::wallet_storage::{self, DEFAULT_LOGIN_ID};
use bip39::{Language, Mnemonic};
use config::defaults::all::Network;
@@ -10,15 +9,11 @@ use config::defaults::COSMOS_DERIVATION_PATH;
use cosmrs::bip32::DerivationPath;
use itertools::Itertools;
use nym_types::account::{Account, AccountEntry, Balance};
use nym_types::currency::MajorCurrencyAmount;
use nym_wallet_types::network::Network as WalletNetwork;
use rand::seq::SliceRandom;
use std::collections::HashMap;
use std::convert::TryInto;
use std::str::FromStr;
use std::sync::Arc;
use strum::IntoEnumIterator;
use tokio::sync::RwLock;
use url::Url;
use validator_client::nymd::wallet::{AccountData, DirectSecp256k1HdWallet};
use validator_client::{nymd::SigningNymdClient, Client};
@@ -26,33 +21,30 @@ use validator_client::{nymd::SigningNymdClient, Client};
#[tauri::command]
pub async fn connect_with_mnemonic(
mnemonic: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Account, BackendError> {
let mnemonic = Mnemonic::from_str(&mnemonic)?;
_connect_with_mnemonic(mnemonic, state).await
}
#[tauri::command]
pub async fn get_balance(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Balance, BackendError> {
let denom = state.read().await.current_network().denom();
match nymd_client!(state)
.get_balance(nymd_client!(state).address(), denom)
.await
pub async fn get_balance(state: tauri::State<'_, WalletState>) -> Result<Balance, BackendError> {
let guard = state.read().await;
let client = guard.current_client()?;
let address = client.nymd.address();
let network = guard.current_network();
let base_mix_denom = network.base_mix_denom();
match client
.nymd
.get_balance(address, base_mix_denom.to_string())
.await?
{
Ok(Some(coin)) => {
let amount = MajorCurrencyAmount::from(coin);
let printable_balance = amount.to_string();
Ok(Balance {
amount,
printable_balance,
})
Some(coin) => {
let amount = guard.attempt_convert_to_display_dec_coin(coin)?;
Ok(Balance::new(amount))
}
Ok(None) => Err(BackendError::NoBalance(
nymd_client!(state).address().to_string(),
)),
Err(e) => Err(BackendError::from(e)),
None => Err(BackendError::NoBalance(address.to_string())),
}
}
@@ -68,18 +60,18 @@ pub fn validate_mnemonic(mnemonic: &str) -> bool {
#[tauri::command]
pub async fn switch_network(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
network: WalletNetwork,
) -> Result<Account, BackendError> {
let account = {
let r_state = state.read().await;
let client = r_state.client(network)?;
let denom = network.denom();
let denom = network.base_mix_denom();
Account::new(
client.nymd.mixnet_contract_address().to_string(),
client.nymd.address().to_string(),
denom.try_into()?,
denom.into(),
)
};
@@ -90,7 +82,7 @@ pub async fn switch_network(
}
#[tauri::command]
pub async fn logout(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<(), BackendError> {
pub async fn logout(state: tauri::State<'_, WalletState>) -> Result<(), BackendError> {
state.write().await.logout();
Ok(())
}
@@ -102,7 +94,7 @@ fn random_mnemonic() -> Mnemonic {
async fn _connect_with_mnemonic(
mnemonic: Mnemonic,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Account, BackendError> {
{
let mut w_state = state.write().await;
@@ -162,7 +154,7 @@ async fn _connect_with_mnemonic(
Some(client) => Ok(Account::new(
client.nymd.mixnet_contract_address().to_string(),
client.nymd.address().to_string(),
default_network.denom().try_into()?,
default_network.base_mix_denom().into(),
)),
None => Err(BackendError::NetworkNotSupported(
config::defaults::DEFAULT_NETWORK,
@@ -178,6 +170,7 @@ async fn _connect_with_mnemonic(
let network: WalletNetwork = client.network.into();
let mut w_state = state.write().await;
w_state.add_client(network, client);
w_state.register_default_denoms(network);
}
account_for_default_network
@@ -322,7 +315,7 @@ pub fn create_password(mnemonic: &str, password: String) -> Result<(), BackendEr
#[tauri::command]
pub async fn sign_in_with_password(
password: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Account, BackendError> {
log::info!("Signing in with password");
@@ -362,7 +355,7 @@ fn extract_first_mnemonic(
pub async fn sign_in_with_password_and_account_id(
account_id: &str,
password: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Account, BackendError> {
log::info!("Signing in with password");
@@ -409,7 +402,7 @@ pub async fn add_account_for_password(
mnemonic: &str,
password: &str,
account_id: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<AccountEntry, BackendError> {
log::info!("Adding account for the current password: {account_id}");
let mnemonic = Mnemonic::from_str(mnemonic)?;
@@ -451,7 +444,7 @@ pub async fn add_account_for_password(
async fn set_state_with_all_accounts(
stored_login: wallet_storage::StoredLogin,
first_id_when_converting: wallet_storage::AccountId,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
log::trace!("Set state with accounts:");
let all_accounts: Vec<_> = stored_login
@@ -492,7 +485,7 @@ async fn set_state_with_all_accounts(
pub async fn remove_account_for_password(
password: &str,
account_id: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
log::info!("Removing account: {account_id}");
// Currently we only support a single, default, id in the wallet
@@ -523,7 +516,7 @@ fn derive_address(
#[tauri::command]
pub async fn list_accounts(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Vec<AccountEntry>, BackendError> {
log::trace!("Listing accounts");
let state = state.read().await;
@@ -1,19 +1,14 @@
use std::convert::TryInto;
use std::sync::Arc;
use tokio::sync::RwLock;
use mixnet_contract_common::ContractStateParams;
use nym_wallet_types::admin::TauriContractStateParams;
use validator_client::nymd::Fee;
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use crate::state::WalletState;
use mixnet_contract_common::ContractStateParams;
use nym_wallet_types::admin::TauriContractStateParams;
use std::convert::TryInto;
use validator_client::nymd::Fee;
#[tauri::command]
pub async fn get_contract_settings(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TauriContractStateParams, BackendError> {
log::info!(">>> Getting contract settings");
let res = nymd_client!(state).get_contract_settings().await?.into();
@@ -25,7 +20,7 @@ pub async fn get_contract_settings(
pub async fn update_contract_settings(
params: TauriContractStateParams,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TauriContractStateParams, BackendError> {
let mixnet_contract_settings_params: ContractStateParams = params.try_into()?;
log::info!(
@@ -1,56 +1,56 @@
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use crate::state::WalletState;
use crate::{Gateway, MixNode};
use nym_types::currency::MajorCurrencyAmount;
use nym_types::currency::DecCoin;
use nym_types::gateway::GatewayBond;
use nym_types::mixnode::MixNodeBond;
use nym_types::transaction::TransactionExecuteResult;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::{CosmWasmCoin, Fee};
use validator_client::nymd::{Coin, Fee};
#[tauri::command]
pub async fn bond_gateway(
gateway: Gateway,
pledge: MajorCurrencyAmount,
pledge: DecCoin,
owner_signature: String,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let pledge_minor = pledge.clone().into();
let guard = state.read().await;
let pledge_base = guard.attempt_convert_to_base_coin(pledge.clone())?;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Bond gateway: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
&gateway.identity_key,
">>> Bond gateway: identity_key = {}, pledge_display = {}, pledge_base = {}, fee = {:?}",
gateway.identity_key,
pledge,
&pledge_minor,
pledge_base,
fee,
);
let res = nymd_client!(state)
.bond_gateway(gateway, owner_signature, pledge_minor, fee)
let res = guard
.current_client()?
.nymd
.bond_gateway(gateway, owner_signature, pledge_base, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
#[tauri::command]
pub async fn unbond_gateway(
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let guard = state.read().await;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(">>> Unbond gateway, fee = {:?}", fee);
let res = nymd_client!(state).unbond_gateway(fee).await?;
let res = guard.current_client()?.nymd.unbond_gateway(fee).await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
@@ -58,43 +58,46 @@ pub async fn unbond_gateway(
pub async fn bond_mixnode(
mixnode: MixNode,
owner_signature: String,
pledge: MajorCurrencyAmount,
pledge: DecCoin,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let pledge_minor = pledge.clone().into();
let guard = state.read().await;
let pledge_base = guard.attempt_convert_to_base_coin(pledge.clone())?;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Bond mixnode: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
">>> Bond mixnode: identity_key = {}, pledge_display = {}, pledge_base = {}, fee = {:?}",
mixnode.identity_key,
pledge,
pledge_minor,
pledge_base,
fee,
);
let res = nymd_client!(state)
.bond_mixnode(mixnode, owner_signature, pledge_minor, fee)
let res = guard
.current_client()?
.nymd
.bond_mixnode(mixnode, owner_signature, pledge_base, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
#[tauri::command]
pub async fn unbond_mixnode(
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let guard = state.read().await;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(">>> Unbond mixnode, fee = {:?}", fee);
let res = nymd_client!(state).unbond_mixnode(fee).await?;
let res = guard.current_client()?.nymd.unbond_mixnode(fee).await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
@@ -102,34 +105,43 @@ pub async fn unbond_mixnode(
pub async fn update_mixnode(
profit_margin_percent: u8,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let guard = state.read().await;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Update mixnode: profit_margin_percent = {}, fee {:?}",
profit_margin_percent,
fee,
);
let res = nymd_client!(state)
let res = guard
.current_client()?
.nymd
.update_mixnode_config(profit_margin_percent, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
#[tauri::command]
pub async fn mixnode_bond_details(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Option<MixNodeBond>, BackendError> {
log::info!(">>> Get mixnode bond details");
let guard = state.read().await;
let client = guard.current_client()?;
let bond = client.nymd.owns_mixnode(client.nymd.address()).await?;
let res = MixNodeBond::from_mixnet_contract_mixnode_bond(bond)?;
let res = bond
.map(|bond| {
guard
.registered_coins()
.map(|reg| MixNodeBond::from_mixnet_contract_mixnode_bond(bond, reg))
})
.transpose()?
.transpose()?;
log::info!(
"<<< identity_key = {:?}",
res.as_ref().map(|r| r.mix_node.identity_key.to_string())
@@ -140,13 +152,21 @@ pub async fn mixnode_bond_details(
#[tauri::command]
pub async fn gateway_bond_details(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Option<GatewayBond>, BackendError> {
log::info!(">>> Get gateway bond details");
let guard = state.read().await;
let client = guard.current_client()?;
let bond = client.nymd.owns_gateway(client.nymd.address()).await?;
let res = GatewayBond::from_mixnet_contract_gateway_bond(bond)?;
let res = bond
.map(|bond| {
guard
.registered_coins()
.map(|reg| GatewayBond::from_mixnet_contract_gateway_bond(bond, reg))
})
.transpose()?
.transpose()?;
log::info!(
"<<< identity_key = {:?}",
res.as_ref().map(|r| r.gateway.identity_key.to_string())
@@ -158,17 +178,23 @@ pub async fn gateway_bond_details(
#[tauri::command]
pub async fn get_operator_rewards(
address: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<MajorCurrencyAmount, BackendError> {
state: tauri::State<'_, WalletState>,
) -> Result<DecCoin, BackendError> {
log::info!(">>> Get operator rewards for {}", address);
let denom = state.read().await.current_network().denom();
let rewards_as_minor = nymd_client!(state).get_operator_rewards(address).await?;
let coin = CosmWasmCoin::new(rewards_as_minor.u128(), denom.as_ref());
let amount: MajorCurrencyAmount = coin.into();
let guard = state.read().await;
let network = guard.current_network();
let denom = network.base_mix_denom();
let reward_amount = guard
.current_client()?
.nymd
.get_operator_rewards(address)
.await?;
let base_coin = Coin::new(reward_amount.u128(), denom);
let display_coin: DecCoin = guard.attempt_convert_to_display_dec_coin(base_coin.clone())?;
log::info!(
"<<< rewards_as_minor = {}, amount = {}",
rewards_as_minor,
amount
"<<< rewards_base = {}, rewards_display = {}",
base_coin,
display_coin
);
Ok(amount)
Ok(display_coin)
}
@@ -1,61 +1,64 @@
use crate::error::BackendError;
use crate::state::State;
use crate::state::WalletState;
use crate::vesting::delegate::get_pending_vesting_delegation_events;
use crate::{api_client, nymd_client};
use cosmwasm_std::Coin as CosmWasmCoin;
use mixnet_contract_common::IdentityKey;
use nym_types::currency::{CurrencyDenom, MajorCurrencyAmount};
use nym_types::currency::DecCoin;
use nym_types::delegation::{
from_contract_delegation_events, Delegation, DelegationEvent, DelegationRecord,
DelegationWithEverything, DelegationsSummaryResponse,
Delegation, DelegationEvent, DelegationRecord, DelegationWithEverything,
DelegationsSummaryResponse,
};
use nym_types::transaction::TransactionExecuteResult;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::Fee;
use validator_client::nymd::{Coin, Fee};
#[tauri::command]
pub async fn get_pending_delegation_events(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Vec<DelegationEvent>, BackendError> {
log::info!(">>> Get pending delegation events");
let events = nymd_client!(state)
.get_pending_delegation_events(nymd_client!(state).address().to_string(), None)
let guard = state.read().await;
let reg = guard.registered_coins()?;
let client = guard.current_client()?;
let events = client
.nymd
.get_pending_delegation_events(client.nymd.address().to_string(), None)
.await?;
log::info!("<<< {} pending delegation events", events.len());
log::trace!("<<< pending delegation events = {:?}", events);
match from_contract_delegation_events(events) {
Ok(res) => Ok(res),
Err(e) => Err(e.into()),
}
Ok(events
.into_iter()
.map(|event| DelegationEvent::from_mixnet_contract(event, reg))
.collect::<Result<_, _>>()?)
}
#[tauri::command]
pub async fn delegate_to_mixnode(
identity: &str,
amount: MajorCurrencyAmount,
amount: DecCoin,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let delegation = amount.clone().into();
let guard = state.read().await;
let delegation_base = guard.attempt_convert_to_base_coin(amount.clone())?;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Delegate to mixnode: identity_key = {}, amount = {}, minor_amount = {}, fee = {:?}",
">>> Delegate to mixnode: identity_key = {}, display_amount = {}, base_amount = {}, fee = {:?}",
identity,
amount,
delegation,
delegation_base,
fee,
);
let res = nymd_client!(state)
.delegate_to_mixnode(identity, delegation, fee)
.delegate_to_mixnode(identity, delegation_base, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
@@ -63,50 +66,54 @@ pub async fn delegate_to_mixnode(
pub async fn undelegate_from_mixnode(
identity: &str,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let guard = state.read().await;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Undelegate from mixnode: identity_key = {}, fee = {:?}",
identity,
fee
);
let res = nymd_client!(state)
let res = guard
.current_client()?
.nymd
.remove_mixnode_delegation(identity, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
struct DelegationWithHistory {
pub delegation: Delegation,
pub amount_sum: MajorCurrencyAmount,
pub amount_sum: DecCoin,
pub history: Vec<DelegationRecord>,
}
#[tauri::command]
pub async fn get_all_mix_delegations(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Vec<DelegationWithEverything>, BackendError> {
log::info!(">>> Get all mixnode delegations");
let guard = state.read().await;
let client = guard.current_client()?;
let reg = guard.registered_coins()?;
// TODO: add endpoint to validator API to get a single mix node bond
let mixnodes = api_client!(state).get_mixnodes().await?;
let mixnodes = client.validator_api.get_mixnodes().await?;
let address = nymd_client!(state).address().to_string();
let denom_minor = state.read().await.current_network().denom();
let denom: CurrencyDenom = denom_minor.clone().try_into()?;
let address = client.nymd.address();
let network = guard.current_network();
let display_mix_denom = network.display_mix_denom();
let base_mix_denom = network.base_mix_denom();
log::info!(" >>> Get delegations");
let delegations = nymd_client!(state)
.get_delegator_delegations_paged(address.clone(), None, None) // get all delegations, ignoring paging
.await?
.delegations;
let delegations = client.get_all_delegator_delegations(address).await?;
log::info!(" <<< {} delegations", delegations.len());
// first get pending events from the mixnet contract (operations made with unlocked tokens)
@@ -125,20 +132,21 @@ pub async fn get_all_mix_delegations(
let mut map: HashMap<String, DelegationWithHistory> = HashMap::new();
for pending_event in pending_events_for_account.clone() {
for pending_event in &pending_events_for_account {
if delegations
.iter()
.any(|d| d.node_identity == pending_event.node_identity)
{
let amount = pending_event
.amount
.unwrap_or_else(|| MajorCurrencyAmount::zero(&denom));
.clone()
.unwrap_or_else(|| DecCoin::zero(display_mix_denom));
let delegation = DelegationWithHistory {
delegation: Delegation {
amount: amount.clone(),
node_identity: pending_event.node_identity,
proxy: None,
owner: pending_event.address,
node_identity: pending_event.node_identity.clone(),
proxy: None, // TODO: ask @MS about delegations via vesting contract => surely we'd have proxy there?
owner: pending_event.address.clone(),
block_height: pending_event.block_height,
},
amount_sum: amount,
@@ -150,11 +158,13 @@ pub async fn get_all_mix_delegations(
for d in delegations {
// create record of delegation
let delegated_on_iso_datetime = nymd_client!(state)
let delegated_on_iso_datetime = client
.nymd
.get_block_timestamp(Some(d.block_height as u32))
.await?
.to_rfc3339();
let amount: MajorCurrencyAmount = d.amount.clone().into();
let amount = guard.attempt_convert_to_display_dec_coin(d.amount.clone().into())?;
let record = DelegationRecord {
amount: amount.clone(),
block_height: d.block_height,
@@ -164,13 +174,15 @@ pub async fn get_all_mix_delegations(
let entry = map
.entry(d.node_identity.clone())
.or_insert(DelegationWithHistory {
delegation: d.try_into()?,
delegation: Delegation::from_mixnet_contract(d, reg)?,
history: vec![],
amount_sum: MajorCurrencyAmount::zero(&amount.denom),
amount_sum: DecCoin::zero(display_mix_denom),
});
debug_assert_eq!(entry.amount_sum.denom, amount.denom);
entry.history.push(record);
entry.amount_sum = entry.amount_sum.clone() + amount;
entry.amount_sum.amount += amount.amount;
}
let mut with_everything: Vec<DelegationWithEverything> = vec![];
@@ -196,25 +208,25 @@ pub async fn get_all_mix_delegations(
.iter()
.find(|m| m.mix_node.identity_key == node_identity);
let pledge_amount: Option<MajorCurrencyAmount> =
mixnode.and_then(|m| m.pledge_amount.clone().try_into().ok());
let pledge_amount = mixnode
.map(|m| guard.attempt_convert_to_display_dec_coin(m.pledge_amount.clone().into()))
.transpose()?;
let total_delegation: Option<MajorCurrencyAmount> =
mixnode.and_then(|m| m.total_delegation.clone().try_into().ok());
let total_delegation = mixnode
.map(|m| guard.attempt_convert_to_display_dec_coin(m.total_delegation.clone().into()))
.transpose()?;
let profit_margin_percent: Option<u8> = mixnode.map(|m| m.mix_node.profit_margin_percent);
log::trace!(" >>> Get accumulated rewards: address = {}", address);
let accumulated_rewards = match nymd_client!(state)
.get_delegator_rewards(address.clone(), node_identity.clone(), proxy.clone())
let accumulated_rewards = match client
.nymd
.get_delegator_rewards(address.to_string(), node_identity.clone(), proxy.clone())
.await
{
Ok(rewards) => {
let reward = CosmWasmCoin {
denom: denom_minor.to_string(),
amount: rewards,
};
let amount = MajorCurrencyAmount::from(reward);
let reward = Coin::new(rewards.u128(), base_mix_denom);
let amount = guard.attempt_convert_to_display_dec_coin(reward)?;
log::trace!(" <<< rewards = {}, amount = {}", rewards, amount);
Some(amount)
}
@@ -305,40 +317,52 @@ pub async fn get_delegator_rewards(
address: String,
mix_identity: IdentityKey,
proxy: Option<String>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<MajorCurrencyAmount, BackendError> {
let denom_minor = state.read().await.current_network().denom();
state: tauri::State<'_, WalletState>,
) -> Result<DecCoin, BackendError> {
log::info!(
">>> Get delegator rewards: mix_identity = {}, proxy = {:?}",
mix_identity,
proxy
);
let res = nymd_client!(state)
let guard = state.read().await;
let network = guard.current_network();
let denom = network.base_mix_denom();
let reward_amount = guard
.current_client()?
.nymd
.get_delegator_rewards(address, mix_identity, proxy)
.await?;
let coin = CosmWasmCoin::new(res.u128(), denom_minor.as_ref());
let amount = coin.into();
log::info!(">>> res = {}, amount = {}", res, amount);
Ok(amount)
let base_coin = Coin::new(reward_amount.u128(), denom);
let display_coin: DecCoin = guard.attempt_convert_to_display_dec_coin(base_coin.clone())?;
log::info!(
"<<< rewards_base = {}, rewards_display = {}",
base_coin,
display_coin
);
Ok(display_coin)
}
#[tauri::command]
pub async fn get_delegation_summary(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<DelegationsSummaryResponse, BackendError> {
log::info!(">>> Get delegation summary");
let denom_minor = state.read().await.current_network().denom();
let denom: CurrencyDenom = denom_minor.clone().try_into()?;
let guard = state.read().await;
let network = guard.current_network();
let display_mix_denom = network.display_mix_denom();
let delegations = get_all_mix_delegations(state.clone()).await?;
let mut total_delegations = MajorCurrencyAmount::zero(&denom);
let mut total_rewards = MajorCurrencyAmount::zero(&denom);
let mut total_delegations = DecCoin::zero(display_mix_denom);
let mut total_rewards = DecCoin::zero(display_mix_denom);
for d in delegations.clone() {
total_delegations = total_delegations + d.amount;
if let Some(rewards) = d.accumulated_rewards {
total_rewards = total_rewards + rewards;
for d in &delegations {
debug_assert_eq!(d.amount.denom, display_mix_denom);
total_delegations.amount += d.amount.amount;
if let Some(rewards) = &d.accumulated_rewards {
debug_assert_eq!(rewards.denom, display_mix_denom);
total_rewards.amount += rewards.amount;
}
}
log::info!(
@@ -358,7 +382,7 @@ pub async fn get_delegation_summary(
#[tauri::command]
pub async fn get_all_pending_delegation_events(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Vec<DelegationEvent>, BackendError> {
log::info!(">>> Get all pending delegation events");
@@ -1,13 +1,11 @@
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use crate::state::WalletState;
use nym_wallet_types::epoch::Epoch;
use std::sync::Arc;
use tokio::sync::RwLock;
#[tauri::command]
pub async fn get_current_epoch(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Epoch, BackendError> {
log::info!(">>> Get curren epoch");
let interval = nymd_client!(state).get_current_epoch().await?;
@@ -1,15 +1,13 @@
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use crate::state::WalletState;
use mixnet_contract_common::IdentityKey;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::Fee;
#[tauri::command]
pub async fn claim_operator_reward(
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
nymd_client!(state)
.execute_claim_operator_reward(fee)
@@ -20,7 +18,7 @@ pub async fn claim_operator_reward(
#[tauri::command]
pub async fn compound_operator_reward(
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
nymd_client!(state)
.execute_compound_operator_reward(fee)
@@ -32,7 +30,7 @@ pub async fn compound_operator_reward(
pub async fn claim_delegator_reward(
mix_identity: IdentityKey,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
nymd_client!(state)
.execute_claim_delegator_reward(mix_identity, fee)
@@ -44,7 +42,7 @@ pub async fn claim_delegator_reward(
pub async fn compound_delegator_reward(
mix_identity: IdentityKey,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
nymd_client!(state)
.execute_compound_delegator_reward(mix_identity, fee)
@@ -1,46 +1,43 @@
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use nym_types::currency::MajorCurrencyAmount;
use crate::state::WalletState;
use nym_types::currency::DecCoin;
use nym_types::transaction::{SendTxResult, TransactionDetails};
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::{AccountId, Fee};
#[tauri::command]
pub async fn send(
address: &str,
amount: MajorCurrencyAmount,
amount: DecCoin,
memo: String,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<SendTxResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let address = AccountId::from_str(address)?;
let from_address = nymd_client!(state).address().to_string();
let amount2 = amount.clone().into();
let guard = state.read().await;
let amount_base = guard.attempt_convert_to_base_coin(amount.clone())?;
let to_address = AccountId::from_str(address)?;
let from_address = guard.current_client()?.nymd.address().to_string();
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Send: amount = {}, minor_amount = {:?}, from = {}, to = {}, fee = {:?}",
">>> Send: display_amount = {}, base_amount = {}, from = {}, to = {}, fee = {:?}",
amount,
amount2,
amount_base,
from_address,
address.as_ref(),
to_address,
fee,
);
let raw_res = nymd_client!(state)
.send(&address, vec![amount2], memo, fee)
let raw_res = guard
.current_client()?
.nymd
.send(&to_address, vec![amount_base], memo, fee)
.await?;
log::info!("<<< tx hash = {}", raw_res.hash.to_string());
let res = SendTxResult::new(
raw_res,
TransactionDetails {
from_address,
to_address: address.to_string(),
amount,
},
denom_minor.as_ref(),
)?;
TransactionDetails::new(amount, from_address, to_address.to_string()),
fee_amount,
);
log::trace!("<<< {:?}", res);
Ok(res)
}
@@ -3,16 +3,14 @@
use crate::error::BackendError;
use crate::operations::simulate::{FeeDetails, SimulateResult};
use crate::State;
use crate::WalletState;
use mixnet_contract_common::{ContractStateParams, ExecuteMsg};
use nym_wallet_types::admin::TauriContractStateParams;
use std::sync::Arc;
use tokio::sync::RwLock;
#[tauri::command]
pub async fn simulate_update_contract_settings(
params: TauriContractStateParams,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
let mixnet_contract_settings_params: ContractStateParams = params.try_into()?;
@@ -28,5 +26,5 @@ pub async fn simulate_update_contract_settings(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
@@ -3,23 +3,22 @@
use crate::error::BackendError;
use crate::operations::simulate::{FeeDetails, SimulateResult};
use crate::state::State;
use nym_types::currency::MajorCurrencyAmount;
use crate::state::WalletState;
use nym_types::currency::DecCoin;
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::{AccountId, MsgSend};
#[tauri::command]
pub async fn simulate_send(
address: &str,
amount: MajorCurrencyAmount,
state: tauri::State<'_, Arc<RwLock<State>>>,
amount: DecCoin,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
let amount_base = guard.attempt_convert_to_base_coin(amount.clone())?;
let to_address = AccountId::from_str(address)?;
let amount = vec![amount.into()];
let amount = vec![amount_base.into()];
let client = guard.current_client()?;
let from_address = client.nymd.address().clone();
@@ -33,5 +32,5 @@ pub async fn simulate_send(
};
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
@@ -4,22 +4,20 @@
use crate::error::BackendError;
use crate::nymd_client;
use crate::operations::simulate::{FeeDetails, SimulateResult};
use crate::State;
use crate::WalletState;
use mixnet_contract_common::IdentityKey;
use mixnet_contract_common::{ExecuteMsg, Gateway, MixNode};
use nym_types::currency::MajorCurrencyAmount;
use std::sync::Arc;
use tokio::sync::RwLock;
use nym_types::currency::DecCoin;
#[tauri::command]
pub async fn simulate_bond_gateway(
gateway: Gateway,
pledge: MajorCurrencyAmount,
pledge: DecCoin,
owner_signature: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
let pledge = pledge.into();
let pledge = guard.attempt_convert_to_base_coin(pledge)?;
let client = guard.current_client()?;
let mixnet_contract = client.nymd.mixnet_contract_address();
@@ -36,12 +34,12 @@ pub async fn simulate_bond_gateway(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_unbond_gateway(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
@@ -56,18 +54,18 @@ pub async fn simulate_unbond_gateway(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_bond_mixnode(
mixnode: MixNode,
owner_signature: String,
pledge: MajorCurrencyAmount,
state: tauri::State<'_, Arc<RwLock<State>>>,
pledge: DecCoin,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
let pledge = pledge.into();
let pledge = guard.attempt_convert_to_base_coin(pledge)?;
let client = guard.current_client()?;
let mixnet_contract = client.nymd.mixnet_contract_address();
@@ -83,12 +81,12 @@ pub async fn simulate_bond_mixnode(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_unbond_mixnode(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
@@ -103,13 +101,13 @@ pub async fn simulate_unbond_mixnode(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_update_mixnode(
profit_margin_percent: u8,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
@@ -126,17 +124,17 @@ pub async fn simulate_update_mixnode(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_delegate_to_mixnode(
identity: &str,
amount: MajorCurrencyAmount,
state: tauri::State<'_, Arc<RwLock<State>>>,
amount: DecCoin,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
let delegation = amount.into();
let delegation = guard.attempt_convert_to_base_coin(amount)?;
let client = guard.current_client()?;
let mixnet_contract = client.nymd.mixnet_contract_address();
@@ -151,13 +149,13 @@ pub async fn simulate_delegate_to_mixnode(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_undelegate_from_mixnode(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
@@ -174,51 +172,51 @@ pub async fn simulate_undelegate_from_mixnode(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_claim_operator_reward(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let result = nymd_client!(state)
.simulate_claim_operator_reward(None)
.await?;
let gas_price = nymd_client!(state).gas_price().clone();
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
}
#[tauri::command]
pub async fn simulate_compound_operator_reward(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let result = nymd_client!(state)
.simulate_compound_operator_reward(None)
.await?;
let gas_price = nymd_client!(state).gas_price().clone();
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
}
#[tauri::command]
pub async fn simulate_claim_delegator_reward(
mix_identity: IdentityKey,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let result = nymd_client!(state)
.simulate_claim_delegator_reward(mix_identity, None)
.await?;
let gas_price = nymd_client!(state).gas_price().clone();
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
}
#[tauri::command]
pub async fn simulate_compound_delegator_reward(
mix_identity: IdentityKey,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let result = nymd_client!(state)
.simulate_compound_delegator_reward(mix_identity, None)
.await?;
let gas_price = nymd_client!(state).gas_price().clone();
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
}
@@ -1,7 +1,6 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_types::currency::MajorCurrencyAmount;
use nym_types::fees::FeeDetails;
use validator_client::nymd::cosmwasm_client::types::GasInfo;
use validator_client::nymd::{tx, CosmosCoin, Fee, GasPrice};
@@ -29,20 +28,12 @@ impl SimulateResult {
}
}
pub fn detailed_fee(&self) -> FeeDetails {
let amount = self.to_fee_amount().map(MajorCurrencyAmount::from);
FeeDetails {
amount,
fee: self.to_fee(),
}
}
fn to_fee_amount(&self) -> Option<CosmosCoin> {
pub(crate) fn to_fee_amount(&self) -> Option<CosmosCoin> {
self.gas_info
.map(|gas_info| &self.gas_price * gas_info.gas_used)
}
fn to_fee(&self) -> Fee {
pub(crate) fn to_fee(&self) -> Fee {
self.to_fee_amount()
.and_then(|fee_amount| {
self.gas_info.map(|gas_info| {
@@ -4,23 +4,21 @@
use crate::error::BackendError;
use crate::nymd_client;
use crate::operations::simulate::{FeeDetails, SimulateResult};
use crate::State;
use crate::WalletState;
use mixnet_contract_common::IdentityKey;
use mixnet_contract_common::{Gateway, MixNode};
use nym_types::currency::MajorCurrencyAmount;
use std::sync::Arc;
use tokio::sync::RwLock;
use nym_types::currency::DecCoin;
use vesting_contract_common::ExecuteMsg;
#[tauri::command]
pub async fn simulate_vesting_bond_gateway(
gateway: Gateway,
pledge: MajorCurrencyAmount,
pledge: DecCoin,
owner_signature: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
let pledge = pledge.into();
let pledge = guard.attempt_convert_to_base_coin(pledge)?;
let client = guard.current_client()?;
let vesting_contract = client.nymd.vesting_contract_address();
@@ -31,18 +29,18 @@ pub async fn simulate_vesting_bond_gateway(
&ExecuteMsg::BondGateway {
gateway,
owner_signature,
amount: pledge,
amount: pledge.into(),
},
vec![],
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_vesting_unbond_gateway(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
@@ -57,18 +55,18 @@ pub async fn simulate_vesting_unbond_gateway(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_vesting_bond_mixnode(
mixnode: MixNode,
owner_signature: String,
pledge: MajorCurrencyAmount,
state: tauri::State<'_, Arc<RwLock<State>>>,
pledge: DecCoin,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
let pledge = pledge.into();
let pledge = guard.attempt_convert_to_base_coin(pledge)?;
let client = guard.current_client()?;
let vesting_contract = client.nymd.vesting_contract_address();
@@ -79,18 +77,18 @@ pub async fn simulate_vesting_bond_mixnode(
&ExecuteMsg::BondMixnode {
mix_node: mixnode,
owner_signature,
amount: pledge,
amount: pledge.into(),
},
vec![],
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_vesting_unbond_mixnode(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
@@ -105,13 +103,13 @@ pub async fn simulate_vesting_unbond_mixnode(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_vesting_update_mixnode(
profit_margin_percent: u8,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
@@ -128,16 +126,16 @@ pub async fn simulate_vesting_update_mixnode(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_withdraw_vested_coins(
amount: MajorCurrencyAmount,
state: tauri::State<'_, Arc<RwLock<State>>>,
amount: DecCoin,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let guard = state.read().await;
let amount = amount.into();
let amount = guard.attempt_convert_to_base_coin(amount)?.into();
let client = guard.current_client()?;
let vesting_contract = client.nymd.vesting_contract_address();
@@ -150,51 +148,51 @@ pub async fn simulate_withdraw_vested_coins(
)?;
let result = client.nymd.simulate(vec![msg]).await?;
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
}
#[tauri::command]
pub async fn simulate_vesting_claim_operator_reward(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let result = nymd_client!(state)
.simulate_vesting_claim_operator_reward(None)
.await?;
let gas_price = nymd_client!(state).gas_price().clone();
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
}
#[tauri::command]
pub async fn simulate_vesting_compound_operator_reward(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let result = nymd_client!(state)
.simulate_vesting_compound_operator_reward(None)
.await?;
let gas_price = nymd_client!(state).gas_price().clone();
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
}
#[tauri::command]
pub async fn simulate_vesting_claim_delegator_reward(
mix_identity: IdentityKey,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let result = nymd_client!(state)
.simulate_vesting_claim_delegator_reward(mix_identity, None)
.await?;
let gas_price = nymd_client!(state).gas_price().clone();
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
}
#[tauri::command]
pub async fn simulate_vesting_compound_delegator_reward(
mix_identity: IdentityKey,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
let result = nymd_client!(state)
.simulate_vesting_compound_delegator_reward(mix_identity, None)
.await?;
let gas_price = nymd_client!(state).gas_price().clone();
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee())
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
}
@@ -3,9 +3,7 @@
use crate::api_client;
use crate::error::BackendError;
use crate::state::State;
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::state::WalletState;
use validator_client::models::{
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse,
@@ -15,7 +13,7 @@ use validator_client::models::{
pub async fn mixnode_core_node_status(
identity: &str,
since: Option<i64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<CoreNodeStatusResponse, BackendError> {
Ok(api_client!(state)
.get_mixnode_core_status_count(identity, since)
@@ -26,7 +24,7 @@ pub async fn mixnode_core_node_status(
pub async fn gateway_core_node_status(
identity: &str,
since: Option<i64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<CoreNodeStatusResponse, BackendError> {
Ok(api_client!(state)
.get_gateway_core_status_count(identity, since)
@@ -36,7 +34,7 @@ pub async fn gateway_core_node_status(
#[tauri::command]
pub async fn mixnode_status(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<MixnodeStatusResponse, BackendError> {
Ok(api_client!(state).get_mixnode_status(identity).await?)
}
@@ -44,7 +42,7 @@ pub async fn mixnode_status(
#[tauri::command]
pub async fn mixnode_reward_estimation(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<RewardEstimationResponse, BackendError> {
Ok(api_client!(state)
.get_mixnode_reward_estimation(identity)
@@ -54,7 +52,7 @@ pub async fn mixnode_reward_estimation(
#[tauri::command]
pub async fn mixnode_stake_saturation(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<StakeSaturationResponse, BackendError> {
Ok(api_client!(state)
.get_mixnode_stake_saturation(identity)
@@ -64,7 +62,7 @@ pub async fn mixnode_stake_saturation(
#[tauri::command]
pub async fn mixnode_inclusion_probability(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<InclusionProbabilityResponse, BackendError> {
Ok(api_client!(state)
.get_mixnode_inclusion_probability(identity)
@@ -1,48 +1,50 @@
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use crate::state::WalletState;
use crate::{Gateway, MixNode};
use nym_types::currency::MajorCurrencyAmount;
use nym_types::currency::DecCoin;
use nym_types::transaction::TransactionExecuteResult;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::{Fee, VestingSigningClient};
#[tauri::command]
pub async fn vesting_bond_gateway(
gateway: Gateway,
pledge: MajorCurrencyAmount,
pledge: DecCoin,
owner_signature: String,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let pledge_minor = pledge.clone().into();
let guard = state.read().await;
let pledge_base = guard.attempt_convert_to_base_coin(pledge.clone())?;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Bond gateway with locked tokens: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
gateway.identity_key,
pledge,
pledge_minor,
fee,
);
let res = nymd_client!(state)
.vesting_bond_gateway(gateway, &owner_signature, pledge_minor, fee)
">>> Bond gateway with locked tokens: identity_key = {}, pledge_display = {}, pledge_base = {}, fee = {:?}",
gateway.identity_key,
pledge,
pledge_base,
fee,
);
let res = guard
.current_client()?
.nymd
.vesting_bond_gateway(gateway, &owner_signature, pledge_base, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
#[tauri::command]
pub async fn vesting_unbond_gateway(
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let guard = state.read().await;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Unbond gateway bonded with locked tokens, fee = {:?}",
fee
@@ -51,8 +53,7 @@ pub async fn vesting_unbond_gateway(
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
@@ -60,71 +61,81 @@ pub async fn vesting_unbond_gateway(
pub async fn vesting_bond_mixnode(
mixnode: MixNode,
owner_signature: String,
pledge: MajorCurrencyAmount,
pledge: DecCoin,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let pledge_minor = pledge.clone().into();
let guard = state.read().await;
let pledge_base = guard.attempt_convert_to_base_coin(pledge.clone())?;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Bond mixnode with locked tokens: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
">>> Bond mixnode with locked tokens: identity_key = {}, pledge_display = {}, pledge_base = {}, fee = {:?}",
mixnode.identity_key,
pledge,
pledge_minor,
pledge_base,
fee
);
let res = nymd_client!(state)
.vesting_bond_mixnode(mixnode, &owner_signature, pledge_minor, fee)
let res = guard
.current_client()?
.nymd
.vesting_bond_mixnode(mixnode, &owner_signature, pledge_base, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
#[tauri::command]
pub async fn vesting_unbond_mixnode(
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let guard = state.read().await;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Unbond mixnode bonded with locked tokens, fee = {:?}",
fee
);
let res = nymd_client!(state).vesting_unbond_mixnode(fee).await?;
let res = guard
.current_client()?
.nymd
.vesting_unbond_mixnode(fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
#[tauri::command]
pub async fn withdraw_vested_coins(
amount: MajorCurrencyAmount,
amount: DecCoin,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let amount_minor = amount.clone().into();
let guard = state.read().await;
let amount_base = guard.attempt_convert_to_base_coin(amount.clone())?;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Withdraw vested liquid coins: amount = {}, amount_minor = {}, fee = {:?}",
">>> Withdraw vested liquid coins: amount_base = {}, amount_base = {}, fee = {:?}",
amount,
amount_minor,
amount_base,
fee
);
let res = nymd_client!(state)
.withdraw_vested_coins(amount_minor, fee)
let res = guard
.current_client()?
.nymd
.withdraw_vested_coins(amount_base, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
@@ -132,21 +143,23 @@ pub async fn withdraw_vested_coins(
pub async fn vesting_update_mixnode(
profit_margin_percent: u8,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let guard = state.read().await;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Update mixnode bonded with locked tokens: profit_margin_percent = {}, fee = {:?}",
profit_margin_percent,
fee,
);
let res = nymd_client!(state)
let res = guard
.current_client()?
.nymd
.vesting_update_mixnode_config(profit_margin_percent, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
@@ -1,23 +1,18 @@
use std::sync::Arc;
use tokio::sync::RwLock;
use nym_types::currency::MajorCurrencyAmount;
use nym_types::delegation::{from_contract_delegation_events, DelegationEvent};
use crate::error::BackendError;
use crate::state::WalletState;
use nym_types::currency::DecCoin;
use nym_types::delegation::DelegationEvent;
use nym_types::transaction::TransactionExecuteResult;
use validator_client::nymd::{Fee, VestingSigningClient};
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
#[tauri::command]
pub async fn get_pending_vesting_delegation_events(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Vec<DelegationEvent>, BackendError> {
log::info!(">>> Get pending delegations from vesting contract");
let guard = state.read().await;
let reg = guard.registered_coins()?;
let client = &guard.current_client()?.nymd;
let vesting_contract = client.vesting_contract_address();
@@ -31,36 +26,39 @@ pub async fn get_pending_vesting_delegation_events(
log::info!("<<< {} events", events.len());
log::trace!("<<< {:?}", events);
match from_contract_delegation_events(events) {
Ok(res) => Ok(res),
Err(e) => Err(e.into()),
}
Ok(events
.into_iter()
.map(|event| DelegationEvent::from_mixnet_contract(event, reg))
.collect::<Result<_, _>>()?)
}
#[tauri::command]
pub async fn vesting_delegate_to_mixnode(
identity: &str,
amount: MajorCurrencyAmount,
amount: DecCoin,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let delegation = amount.clone().into();
let guard = state.read().await;
let delegation = guard.attempt_convert_to_base_coin(amount.clone())?;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Delegate to mixnode with locked tokens: identity_key = {}, amount = {}, minor_amount = {}, fee = {:?}",
">>> Delegate to mixnode with locked tokens: identity_key = {}, amount_display = {}, amount_base = {}, fee = {:?}",
identity,
amount,
delegation,
fee
);
let res = nymd_client!(state)
let res = guard
.current_client()?
.nymd
.vesting_delegate_to_mixnode(identity, delegation, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
@@ -68,21 +66,23 @@ pub async fn vesting_delegate_to_mixnode(
pub async fn vesting_undelegate_from_mixnode(
identity: &str,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let denom_minor = state.read().await.current_network().denom();
let guard = state.read().await;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Undelegate from mixnode delegated with locked tokens: identity_key = {}, fee = {:?}",
identity,
fee,
);
let res = nymd_client!(state)
let res = guard
.current_client()?
.nymd
.vesting_undelegate_from_mixnode(identity, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res,
denom_minor.as_ref(),
res, fee_amount,
)?)
}
@@ -1,92 +1,106 @@
use std::sync::Arc;
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::WalletState;
use cosmwasm_std::Timestamp;
use tokio::sync::RwLock;
use nym_types::currency::MajorCurrencyAmount;
use nym_types::currency::DecCoin;
use nym_types::vesting::VestingAccountInfo;
use nym_types::vesting::{OriginalVestingResponse, PledgeData};
use validator_client::nymd::VestingQueryClient;
use vesting_contract_common::Period;
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
#[tauri::command]
pub async fn locked_coins(
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<MajorCurrencyAmount, BackendError> {
state: tauri::State<'_, WalletState>,
) -> Result<DecCoin, BackendError> {
log::info!(">>> Query locked coins");
let res = nymd_client!(state)
let guard = state.read().await;
let client = guard.current_client()?;
let res = client
.nymd
.locked_coins(
nymd_client!(state).address().as_ref(),
client.nymd.address().as_ref(),
block_time.map(Timestamp::from_seconds),
)
.await?
.into();
log::info!("<<< locked coins = {}", res);
Ok(res)
.await?;
let display = guard.attempt_convert_to_display_dec_coin(res)?;
log::info!("<<< locked coins = {}", display);
Ok(display)
}
#[tauri::command]
pub async fn spendable_coins(
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<MajorCurrencyAmount, BackendError> {
state: tauri::State<'_, WalletState>,
) -> Result<DecCoin, BackendError> {
log::info!(">>> Query spendable coins");
let res = nymd_client!(state)
let guard = state.read().await;
let client = guard.current_client()?;
let res = client
.nymd
.spendable_coins(
nymd_client!(state).address().as_ref(),
client.nymd.address().as_ref(),
block_time.map(Timestamp::from_seconds),
)
.await?
.into();
log::info!("<<< spendable coins = {}", res);
Ok(res)
.await?;
let display = guard.attempt_convert_to_display_dec_coin(res)?;
log::info!("<<< spendable coins = {}", display);
Ok(display)
}
#[tauri::command]
pub async fn vested_coins(
vesting_account_address: &str,
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<MajorCurrencyAmount, BackendError> {
state: tauri::State<'_, WalletState>,
) -> Result<DecCoin, BackendError> {
log::info!(">>> Query vested coins");
let res = nymd_client!(state)
let guard = state.read().await;
let res = guard
.current_client()?
.nymd
.vested_coins(
vesting_account_address,
block_time.map(Timestamp::from_seconds),
)
.await?
.into();
log::info!("<<< vested coins = {}", res);
Ok(res)
.await?;
let display = guard.attempt_convert_to_display_dec_coin(res)?;
log::info!("<<< vested coins = {}", display);
Ok(display)
}
#[tauri::command]
pub async fn vesting_coins(
vesting_account_address: &str,
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<MajorCurrencyAmount, BackendError> {
state: tauri::State<'_, WalletState>,
) -> Result<DecCoin, BackendError> {
log::info!(">>> Query vesting coins");
let res = nymd_client!(state)
let guard = state.read().await;
let res = guard
.current_client()?
.nymd
.vesting_coins(
vesting_account_address,
block_time.map(Timestamp::from_seconds),
)
.await?
.into();
log::info!("<<< vesting coins = {}", res);
Ok(res)
.await?;
let display = guard.attempt_convert_to_display_dec_coin(res)?;
log::info!("<<< vesting coins = {}", display);
Ok(display)
}
#[tauri::command]
pub async fn vesting_start_time(
vesting_account_address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<u64, BackendError> {
log::info!(">>> Query vesting start time");
let res = nymd_client!(state)
@@ -100,7 +114,7 @@ pub async fn vesting_start_time(
#[tauri::command]
pub async fn vesting_end_time(
vesting_account_address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<u64, BackendError> {
log::info!(">>> Query vesting end time");
let res = nymd_client!(state)
@@ -114,13 +128,19 @@ pub async fn vesting_end_time(
#[tauri::command]
pub async fn original_vesting(
vesting_account_address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<OriginalVestingResponse, BackendError> {
log::info!(">>> Query original vesting");
let res = nymd_client!(state)
let guard = state.read().await;
let reg = guard.registered_coins()?;
let res = guard
.current_client()?
.nymd
.original_vesting(vesting_account_address)
.await?
.try_into()?;
.await?;
let res = OriginalVestingResponse::from_vesting_contract(res, reg)?;
log::info!("<<< {:?}", res);
Ok(res)
}
@@ -129,18 +149,23 @@ pub async fn original_vesting(
pub async fn delegated_free(
vesting_account_address: &str,
block_time: Option<u64>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<MajorCurrencyAmount, BackendError> {
state: tauri::State<'_, WalletState>,
) -> Result<DecCoin, BackendError> {
log::info!(">>> Query delegated free");
let res = nymd_client!(state)
let guard = state.read().await;
let res = guard
.current_client()?
.nymd
.delegated_free(
vesting_account_address,
block_time.map(Timestamp::from_seconds),
)
.await?
.into();
log::info!("<<< delegated free = {}", res);
Ok(res)
.await?;
let display = guard.attempt_convert_to_display_dec_coin(res)?;
log::info!("<<< delegated free = {}", display);
Ok(display)
}
/// Returns the total amount of delegated tokens that have vested
@@ -148,30 +173,42 @@ pub async fn delegated_free(
pub async fn delegated_vesting(
block_time: Option<u64>,
vesting_account_address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<MajorCurrencyAmount, BackendError> {
state: tauri::State<'_, WalletState>,
) -> Result<DecCoin, BackendError> {
log::info!(">>> Query delegated vesting");
let res = nymd_client!(state)
let guard = state.read().await;
let res = guard
.current_client()?
.nymd
.delegated_vesting(
vesting_account_address,
block_time.map(Timestamp::from_seconds),
)
.await?
.into();
log::info!("<<< delegated_vesting = {}", res);
Ok(res)
.await?;
let display = guard.attempt_convert_to_display_dec_coin(res)?;
log::info!("<<< delegated_vesting = {}", display);
Ok(display)
}
#[tauri::command]
pub async fn vesting_get_mixnode_pledge(
address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Option<PledgeData>, BackendError> {
log::info!(">>> Query vesting get mixnode pledge");
let res = nymd_client!(state)
let guard = state.read().await;
let reg = guard.registered_coins()?;
let res = guard
.current_client()?
.nymd
.get_mixnode_pledge(address)
.await?
.and_then(PledgeData::and_then);
.map(|pledge| PledgeData::from_vesting_contract(pledge, reg))
.transpose()?;
log::info!("<<< {:?}", res);
Ok(res)
}
@@ -179,13 +216,20 @@ pub async fn vesting_get_mixnode_pledge(
#[tauri::command]
pub async fn vesting_get_gateway_pledge(
address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Option<PledgeData>, BackendError> {
log::info!(">>> Query vesting get gateway pledge");
let res = nymd_client!(state)
let guard = state.read().await;
let reg = guard.registered_coins()?;
let res = guard
.current_client()?
.nymd
.get_gateway_pledge(address)
.await?
.and_then(PledgeData::and_then);
.map(|pledge| PledgeData::from_vesting_contract(pledge, reg))
.transpose()?;
log::info!("<<< {:?}", res);
Ok(res)
}
@@ -193,7 +237,7 @@ pub async fn vesting_get_gateway_pledge(
#[tauri::command]
pub async fn get_current_vesting_period(
address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<Period, BackendError> {
log::info!(">>> Query current vesting period");
let res = nymd_client!(state)
@@ -206,10 +250,15 @@ pub async fn get_current_vesting_period(
#[tauri::command]
pub async fn get_account_info(
address: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<VestingAccountInfo, BackendError> {
log::info!(">>> Query account info");
let res = nymd_client!(state).get_account(address).await?.try_into()?;
let guard = state.read().await;
let res = guard.registered_coins()?;
let vesting_account = guard.current_client()?.nymd.get_account(address).await?;
let res = VestingAccountInfo::from_vesting_contract(vesting_account, res)?;
log::info!("<<< {:?}", res);
Ok(res)
}
@@ -1,14 +1,12 @@
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use crate::state::WalletState;
use mixnet_contract_common::IdentityKey;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::Fee;
#[tauri::command]
pub async fn vesting_claim_operator_reward(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
nymd_client!(state)
.execute_vesting_claim_operator_reward(None)
@@ -19,7 +17,7 @@ pub async fn vesting_claim_operator_reward(
#[tauri::command]
pub async fn vesting_compound_operator_reward(
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
nymd_client!(state)
.execute_vesting_compound_operator_reward(fee)
@@ -31,7 +29,7 @@ pub async fn vesting_compound_operator_reward(
pub async fn vesting_claim_delegator_reward(
mix_identity: IdentityKey,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
nymd_client!(state)
.execute_vesting_claim_delegator_reward(mix_identity, fee)
@@ -43,7 +41,7 @@ pub async fn vesting_claim_delegator_reward(
pub async fn vesting_compound_delegator_reward(
mix_identity: IdentityKey,
fee: Option<Fee>,
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
nymd_client!(state)
.execute_vesting_compound_delegator_reward(mix_identity, fee)
+94 -14
View File
@@ -1,20 +1,21 @@
use crate::config;
use crate::error::BackendError;
use crate::simulate::SimulateResult;
use itertools::Itertools;
use log::warn;
use nym_types::currency::{DecCoin, RegisteredCoins};
use nym_types::fees::FeeDetails;
use nym_wallet_types::network::Network;
use nym_wallet_types::network_config;
use strum::IntoEnumIterator;
use validator_client::nymd::{AccountId as CosmosAccountId, SigningNymdClient};
use validator_client::Client;
use itertools::Itertools;
use once_cell::sync::Lazy;
use tokio::sync::RwLock;
use url::Url;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use strum::IntoEnumIterator;
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use url::Url;
use validator_client::nymd::{AccountId as CosmosAccountId, Coin, Fee, SigningNymdClient};
use validator_client::Client;
// Some hardcoded metadata overrides
static METADATA_OVERRIDES: Lazy<Vec<(Url, ValidatorMetadata)>> = Lazy::new(|| {
@@ -28,7 +29,7 @@ static METADATA_OVERRIDES: Lazy<Vec<(Url, ValidatorMetadata)>> = Lazy::new(|| {
#[tauri::command]
pub async fn load_config_from_files(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
state.write().await.load_config_files();
Ok(())
@@ -36,13 +37,30 @@ pub async fn load_config_from_files(
#[tauri::command]
pub async fn save_config_to_files(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
) -> Result<(), BackendError> {
state.read().await.save_config_files()
}
#[derive(Default, Clone)]
pub struct WalletState {
inner: Arc<RwLock<WalletStateInner>>,
}
impl WalletState {
// not the best API, but those are exposed here for backwards compatibility with the existing
// state type assumptions so that we wouldn't need to fix it up everywhere at once
pub(crate) async fn read(&self) -> RwLockReadGuard<'_, WalletStateInner> {
self.inner.read().await
}
pub(crate) async fn write(&self) -> RwLockWriteGuard<'_, WalletStateInner> {
self.inner.write().await
}
}
#[derive(Default)]
pub struct State {
pub struct WalletStateInner {
config: config::Config,
signing_clients: HashMap<Network, Client<SigningNymdClient>>,
current_network: Network,
@@ -56,6 +74,7 @@ pub struct State {
/// We fetch (and cache) some metadata, such as names, when available
validator_metadata: HashMap<Url, ValidatorMetadata>,
registered_coins: HashMap<Network, RegisteredCoins>,
}
pub(crate) struct WalletAccountIds {
@@ -65,7 +84,63 @@ pub(crate) struct WalletAccountIds {
pub addresses: HashMap<Network, CosmosAccountId>,
}
impl State {
impl WalletStateInner {
// note that `Coin` is ALWAYS the base coin
pub fn attempt_convert_to_base_coin(&self, coin: DecCoin) -> Result<Coin, BackendError> {
let registered_coins = self
.registered_coins
.get(&self.current_network)
.ok_or_else(|| BackendError::UnknownCoinDenom(coin.denom.clone()))?;
Ok(registered_coins.attempt_convert_to_base_coin(coin)?)
}
pub fn attempt_convert_to_display_dec_coin(&self, coin: Coin) -> Result<DecCoin, BackendError> {
let registered_coins = self
.registered_coins
.get(&self.current_network)
.ok_or_else(|| BackendError::UnknownCoinDenom(coin.denom.clone()))?;
Ok(registered_coins.attempt_convert_to_display_dec_coin(coin)?)
}
pub(crate) fn registered_coins(&self) -> Result<&RegisteredCoins, BackendError> {
self.registered_coins
.get(&self.current_network)
.ok_or(BackendError::NoCoinsRegistered {
network: self.current_network,
})
}
pub(crate) fn convert_tx_fee(&self, fee: Option<&Fee>) -> Option<DecCoin> {
let mut fee_amount = fee?.try_get_manual_amount()?;
if fee_amount.len() > 1 {
warn!(
"our tx fee contained more than a single denomination. using the first one for display"
)
}
if fee_amount.is_empty() {
warn!("our tx has had an unknown fee set");
None
} else {
self.attempt_convert_to_display_dec_coin(fee_amount.pop().unwrap())
.ok()
}
}
// this one is rather gnarly and I'm not 100% sure how to feel about existence of it
pub(crate) fn create_detailed_fee(
&self,
simulate_res: SimulateResult,
) -> Result<FeeDetails, BackendError> {
let amount = simulate_res
.to_fee_amount()
.map(|amount| self.attempt_convert_to_display_dec_coin(amount.into()))
.transpose()?;
Ok(FeeDetails::new(amount, simulate_res.to_fee()))
}
pub fn client(&self, network: Network) -> Result<&Client<SigningNymdClient>, BackendError> {
self.signing_clients
.get(&network)
@@ -112,6 +187,11 @@ impl State {
self.signing_clients.insert(network, client);
}
pub fn register_default_denoms(&mut self, network: Network) {
self.registered_coins
.insert(network, RegisteredCoins::default_denoms(network.into()));
}
pub fn set_network(&mut self, network: Network) {
self.current_network = network;
}
@@ -390,7 +470,7 @@ mod tests {
#[test]
fn adding_validators_urls_prepends() {
let mut state = State::default();
let mut state = WalletStateInner::default();
let _api_urls = state.get_api_urls(Network::MAINNET).collect::<Vec<_>>();
state.add_validator_url(
+9 -14
View File
@@ -1,11 +1,9 @@
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use nym_types::currency::MajorCurrencyAmount;
use crate::state::WalletState;
use nym_types::currency::DecCoin;
use nym_wallet_types::app::AppEnv;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::{tx, Coin, CosmosCoin, Gas, GasPrice};
fn get_env_as_option(key: &str) -> Option<String> {
@@ -24,9 +22,7 @@ pub fn get_env() -> AppEnv {
}
#[tauri::command]
pub async fn owns_mixnode(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<bool, BackendError> {
pub async fn owns_mixnode(state: tauri::State<'_, WalletState>) -> Result<bool, BackendError> {
Ok(nymd_client!(state)
.owns_mixnode(nymd_client!(state).address())
.await?
@@ -34,9 +30,7 @@ pub async fn owns_mixnode(
}
#[tauri::command]
pub async fn owns_gateway(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<bool, BackendError> {
pub async fn owns_gateway(state: tauri::State<'_, WalletState>) -> Result<bool, BackendError> {
Ok(nymd_client!(state)
.owns_gateway(nymd_client!(state).address())
.await?
@@ -144,13 +138,14 @@ impl Operation {
#[tauri::command]
pub async fn get_old_and_incorrect_hardcoded_fee(
state: tauri::State<'_, Arc<RwLock<State>>>,
state: tauri::State<'_, WalletState>,
operation: Operation,
) -> Result<MajorCurrencyAmount, BackendError> {
let mut approximate_fee = operation.default_fee(nymd_client!(state).gas_price());
) -> Result<DecCoin, BackendError> {
let guard = state.read().await;
let mut approximate_fee = operation.default_fee(guard.current_client()?.nymd.gas_price());
// on all our chains it should only ever contain a single type of currency
assert_eq!(approximate_fee.amount.len(), 1);
let coin: Coin = approximate_fee.amount.pop().unwrap().into();
log::info!("hardcoded fee for {:?} is {:?}", operation, coin);
Ok(coin.into())
guard.attempt_convert_to_display_dec_coin(coin)
}
+3 -4
View File
@@ -4,7 +4,7 @@ use walkdir::WalkDir;
use mixnet_contract_common::mixnode::RewardedSetNodeStatus;
use nym_types::account::{Account, AccountEntry, AccountWithMnemonic, Balance};
use nym_types::currency::{CurrencyDenom, MajorAmountString, MajorCurrencyAmount};
use nym_types::currency::{CurrencyDenom, DecCoin};
use nym_types::delegation::{
Delegation, DelegationEvent, DelegationEventKind, DelegationRecord, DelegationResult,
DelegationWithEverything, DelegationsSummaryResponse, PendingUndelegate,
@@ -59,7 +59,6 @@ fn main() {
do_export!(AccountEntry);
do_export!(AccountWithMnemonic);
do_export!(Balance);
do_export!(CurrencyDenom);
do_export!(Delegation);
do_export!(DelegationEvent);
do_export!(DelegationEventKind);
@@ -72,8 +71,8 @@ fn main() {
do_export!(GasInfo);
do_export!(Gateway);
do_export!(GatewayBond);
do_export!(MajorAmountString);
do_export!(MajorCurrencyAmount);
do_export!(CurrencyDenom);
do_export!(DecCoin);
do_export!(MixNode);
do_export!(MixNodeBond);
do_export!(OriginalVestingResponse);
+1 -6
View File
@@ -1,7 +1,2 @@
import type { CurrencyDenom } from './CurrencyDenom';
export interface Account {
contract_address: string;
client_address: string;
denom: CurrencyDenom;
}
export interface Account { contract_address: string, client_address: string, denom: string, }
@@ -1,4 +1,2 @@
export interface AccountEntry {
id: string;
address: string;
}
export interface AccountEntry { id: string, address: string, }
@@ -1,6 +1,3 @@
import type { Account } from './Account';
import type { Account } from "./Account";
export interface AccountWithMnemonic {
account: Account;
mnemonic: string;
}
export interface AccountWithMnemonic { account: Account, mnemonic: string, }
+2 -5
View File
@@ -1,6 +1,3 @@
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
export interface Balance {
amount: MajorCurrencyAmount;
printable_balance: string;
}
export interface Balance { amount: DecCoin, printable_balance: string, }
@@ -1,4 +1,2 @@
export interface CoreNodeStatusResponse {
identity: string;
count: number;
}
export interface CoreNodeStatusResponse { identity: string, count: number, }
@@ -1,7 +0,0 @@
import type { CurrencyDenom } from './CurrencyDenom';
import type { MajorAmountString } from './CurrencyStringMajorAmount';
export interface MajorCurrencyAmount {
amount: MajorAmountString;
denom: CurrencyDenom;
}
@@ -1 +1,2 @@
export type CurrencyDenom = 'NYM' | 'NYMT' | 'NYX' | 'NYXT';
export type CurrencyDenom = "nym" | "nymt" | "nyx" | "nyxt";
@@ -1 +0,0 @@
export type MajorAmountString = string;
@@ -0,0 +1,3 @@
import type { CurrencyDenom } from "./CurrencyDenom";
export type DecCoin = { denom: CurrencyDenom, amount: string };
@@ -1,9 +1,3 @@
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
export interface Delegation {
owner: string;
node_identity: string;
amount: MajorCurrencyAmount;
block_height: bigint;
proxy: string | null;
}
export interface Delegation { owner: string, node_identity: string, amount: DecCoin, block_height: bigint, proxy: string | null, }
@@ -1,10 +1,4 @@
import type { DelegationEventKind } from './DelegationEventKind';
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
import type { DelegationEventKind } from "./DelegationEventKind";
export interface DelegationEvent {
kind: DelegationEventKind;
node_identity: string;
address: string;
amount: MajorCurrencyAmount | null;
block_height: bigint;
}
export interface DelegationEvent { kind: DelegationEventKind, node_identity: string, address: string, amount: DecCoin | null, block_height: bigint, }
@@ -1 +1,2 @@
export type DelegationEventKind = 'Delegate' | 'Undelegate';
export type DelegationEventKind = "Delegate" | "Undelegate";
@@ -1,7 +1,3 @@
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
export interface DelegationRecord {
amount: MajorCurrencyAmount;
block_height: bigint;
delegated_on_iso_datetime: string;
}
export interface DelegationRecord { amount: DecCoin, block_height: bigint, delegated_on_iso_datetime: string, }
@@ -1,7 +1,3 @@
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
export interface DelegationResult {
source_address: string;
target_address: string;
amount: MajorCurrencyAmount | null;
}
export interface DelegationResult { source_address: string, target_address: string, amount: DecCoin | null, }
@@ -1,8 +1,4 @@
import type { DelegationWithEverything } from './DelegationWithEverything';
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
import type { DelegationWithEverything } from "./DelegationWithEverything";
export interface DelegationsSummaryResponse {
delegations: Array<DelegationWithEverything>;
total_delegations: MajorCurrencyAmount;
total_rewards: MajorCurrencyAmount;
}
export interface DelegationsSummaryResponse { delegations: Array<DelegationWithEverything>, total_delegations: DecCoin, total_rewards: DecCoin, }
@@ -1,20 +1,5 @@
import type { DelegationEvent } from './DelegationEvent';
import type { DelegationRecord } from './DelegationRecord';
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
import type { DelegationEvent } from "./DelegationEvent";
import type { DelegationRecord } from "./DelegationRecord";
export interface DelegationWithEverything {
owner: string;
node_identity: string;
amount: MajorCurrencyAmount;
total_delegation: MajorCurrencyAmount | null;
pledge_amount: MajorCurrencyAmount | null;
block_height: bigint;
delegated_on_iso_datetime: string;
profit_margin_percent: number | null;
avg_uptime_percent: number | null;
stake_saturation: number | null;
proxy: string | null;
accumulated_rewards: MajorCurrencyAmount | null;
pending_events: Array<DelegationEvent>;
history: Array<DelegationRecord>;
}
export interface DelegationWithEverything { owner: string, node_identity: string, amount: DecCoin, total_delegation: DecCoin | null, pledge_amount: DecCoin | null, block_height: bigint, delegated_on_iso_datetime: string, profit_margin_percent: number | null, avg_uptime_percent: number | null, stake_saturation: number | null, proxy: string | null, accumulated_rewards: DecCoin | null, pending_events: Array<DelegationEvent>, history: Array<DelegationRecord>, }
@@ -1,5 +1,3 @@
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
export interface FeeDetails {
amount: MajorCurrencyAmount | null;
}
export interface FeeDetails { amount: DecCoin | null, }
+1 -5
View File
@@ -1,6 +1,2 @@
import type { MajorCurrencyAmount } from './Currency';
export interface Gas {
gas_units: bigint;
amount: MajorCurrencyAmount;
}
export interface Gas { gas_units: bigint, }
+2 -6
View File
@@ -1,7 +1,3 @@
import type { MajorCurrencyAmount } from './Currency';
import type { Gas } from "./Gas";
export interface GasInfo {
gas_wanted: bigint;
gas_used: bigint;
fee: MajorCurrencyAmount;
}
export interface GasInfo { gas_wanted: Gas, gas_used: Gas, }
@@ -1,10 +1,4 @@
import type { Gateway } from './Gateway';
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
import type { Gateway } from "./Gateway";
export interface GatewayBond {
pledge_amount: MajorCurrencyAmount;
owner: string;
block_height: bigint;
gateway: Gateway;
proxy: string | null;
}
export interface GatewayBond { pledge_amount: DecCoin, owner: string, block_height: bigint, gateway: Gateway, proxy: string | null, }
@@ -1,6 +1,3 @@
import type { SelectionChance } from './SelectionChance';
import type { SelectionChance } from "./SelectionChance";
export interface InclusionProbabilityResponse {
in_active: SelectionChance;
in_reserve: SelectionChance;
}
export interface InclusionProbabilityResponse { in_active: SelectionChance, in_reserve: SelectionChance, }
@@ -1,13 +1,4 @@
import type { MajorCurrencyAmount } from './Currency';
import type { MixNode } from './Mixnode';
import type { DecCoin } from "./DecCoin";
import type { MixNode } from "./Mixnode";
export interface MixNodeBond {
pledge_amount: MajorCurrencyAmount;
total_delegation: MajorCurrencyAmount;
owner: string;
layer: string;
block_height: bigint;
mix_node: MixNode;
proxy: string | null;
accumulated_rewards: MajorCurrencyAmount | null;
}
export interface MixNodeBond { pledge_amount: DecCoin, total_delegation: DecCoin, owner: string, layer: string, block_height: bigint, mix_node: MixNode, proxy: string | null, accumulated_rewards: DecCoin | null, }
@@ -1 +1,2 @@
export type MixnodeStatus = 'active' | 'standby' | 'inactive' | 'not_found';
export type MixnodeStatus = "active" | "standby" | "inactive" | "not_found";
@@ -1,5 +1,3 @@
import type { MixnodeStatus } from './MixnodeStatus';
import type { MixnodeStatus } from "./MixnodeStatus";
export interface MixnodeStatusResponse {
status: MixnodeStatus;
}
export interface MixnodeStatusResponse { status: MixnodeStatus, }
@@ -1,36 +0,0 @@
// TODO: remove this type
export type Operation =
| 'Upload'
| 'Init'
| 'Migrate'
| 'ChangeAdmin'
| 'Send'
| 'BondMixnode'
| 'BondMixnodeOnBehalf'
| 'UnbondMixnode'
| 'UnbondMixnodeOnBehalf'
| 'UpdateMixnodeConfig'
| 'DelegateToMixnode'
| 'DelegateToMixnodeOnBehalf'
| 'UndelegateFromMixnode'
| 'UndelegateFromMixnodeOnBehalf'
| 'BondGateway'
| 'BondGatewayOnBehalf'
| 'UnbondGateway'
| 'UnbondGatewayOnBehalf'
| 'UpdateContractSettings'
| 'BeginMixnodeRewarding'
| 'FinishMixnodeRewarding'
| 'TrackUnbondGateway'
| 'TrackUnbondMixnode'
| 'WithdrawVestedCoins'
| 'TrackUndelegation'
| 'CreatePeriodicVestingAccount'
| 'AdvanceCurrentInterval'
| 'AdvanceCurrentEpoch'
| 'WriteRewardedSet'
| 'ClearRewardedSet'
| 'UpdateMixnetAddress'
| 'CheckpointMixnodes'
| 'ReconcileDelegations';
@@ -1,7 +1,3 @@
import type { MajorCurrencyAmount } from './Currency';
import type { DecCoin } from "./DecCoin";
export interface OriginalVestingResponse {
amount: MajorCurrencyAmount;
number_of_periods: number;
period_duration: bigint;
}
export interface OriginalVestingResponse { amount: DecCoin, number_of_periods: number, period_duration: bigint, }
@@ -1,6 +1,2 @@
export interface PendingUndelegate {
mix_identity: string;
delegate: string;
proxy: string | null;
block_height: bigint;
}
export interface PendingUndelegate { mix_identity: string, delegate: string, proxy: string | null, block_height: bigint, }

Some files were not shown because too many files have changed in this diff Show More