Compare commits

..

20 Commits

Author SHA1 Message Date
gala1234 e3c9b11258 clean status once modal closes 2022-04-11 14:11:52 +02:00
gala1234 e7179eaa46 adding seleting validator functionality 2022-04-11 13:43:19 +02:00
gala1234 84cf1c6a62 some ui 2022-04-11 12:40:28 +02:00
gala1234 f5e874f8d9 Merge branch 'develop' into feature/validator_selector 2022-04-11 12:04:45 +02:00
gala1234 183f2779f0 story wip 2022-04-05 16:30:53 +02:00
gala1234 92e56c0121 wip story 2022-04-05 14:43:50 +02:00
gala1234 550e0ce856 wip 2022-04-05 14:27:48 +02:00
gala1234 3b0215ccee Merge branch 'develop' into feature/validator_selector 2022-04-05 13:39:59 +02:00
gala1234 38a8621032 starting storybook implementation 2022-04-05 13:38:10 +02:00
gala1234 caa0bc4e1e some ui changes on modal 2022-04-05 10:10:36 +02:00
gala1234 abcb0cbf5e wip display validators settings on a modal 2022-04-04 12:30:11 +02:00
gala1234 cdbcfbe3bf wip creating settings modal 2022-04-01 17:44:17 +02:00
gala1234 b8ee730561 adding dummy urls on mainnet and some dropdown improvements 2022-03-31 10:01:46 +02:00
gala1234 26a067c14d playing with mui 2022-03-30 17:41:04 +02:00
gala1234 69fe8e5cce some indent fix 2022-03-30 17:13:10 +02:00
gala1234 4fbf1cd876 connexion to fetch validator urls 2022-03-30 17:03:00 +02:00
gala1234 c693412258 Merge branch 'feature/wallet-expose-validator-urls' into feature/validator_selector 2022-03-30 13:43:19 +02:00
gala1234 72825a2ad3 start validators fetching 2022-03-30 13:40:02 +02:00
gala1234 89a19815fc first validators dropdown implementation 2022-03-30 13:33:52 +02:00
Jon Häggblad 394f0d30bf wallet: expose validator urls to the frontent 2022-03-30 11:51:41 +02:00
27 changed files with 620 additions and 268 deletions
Generated
+8 -8
View File
@@ -798,9 +798,9 @@ dependencies = [
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta8"
version = "1.0.0-beta7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e70111e9701c3ec43bfbff0e523cd4cb115876b4d3433813436dd0934ee962"
checksum = "88c2565b1e73a816fb659ef4838fc356143fbd35f43c48a51d2d7d4e5d6679d3"
dependencies = [
"digest 0.9.0",
"ed25519-zebra",
@@ -811,18 +811,18 @@ dependencies = [
[[package]]
name = "cosmwasm-derive"
version = "1.0.0-beta8"
version = "1.0.0-beta7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bc2ad5d86be5f6068833f63e20786768db6890019c095dd7775232184fb7b3"
checksum = "fa89fcdf8dbbe0088e663d0a814aa7368e7ebe8fb045a3a150fb5fdc2ffe3b45"
dependencies = [
"syn",
]
[[package]]
name = "cosmwasm-std"
version = "1.0.0-beta8"
version = "1.0.0-beta7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915ca82bd944f116f3a9717481f3fa657e4a73f28c4887288761ebb24e6fbe10"
checksum = "bcb8f99a61d0b9069e1afc80a4ffea87dcc3523edd992080923870b13a677da0"
dependencies = [
"base64",
"cosmwasm-crypto",
@@ -5966,9 +5966,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "uint"
version = "0.9.3"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0"
checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f"
dependencies = [
"byteorder",
"crunchy",
@@ -7,7 +7,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-std = "1.0.0-beta8"
cosmwasm-std = "1.0.0-beta6"
serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1"
@@ -26,7 +26,7 @@ pub use mixnode::{
pub use msg::*;
pub use types::*;
pub type U128 = fixed::types::U50F78;
pub type U128 = fixed::types::U75F53;
fixed::const_fixed_from_int! {
const ONE: U128 = 1;
@@ -6,7 +6,7 @@ use crate::reward_params::RewardParams;
use crate::{Delegation, IdentityKey, SphinxKey};
use crate::{ONE, U128};
use az::CheckedCast;
use cosmwasm_std::{coin, Addr, Coin, Decimal, Uint128};
use cosmwasm_std::{coin, Addr, Coin, Uint128};
use log::error;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -195,16 +195,22 @@ pub struct DelegatorRewardParams {
// to be completely honest I don't understand all consequences of using `#[schemars(with = "String")]`
// for U128 here, but it seems that CosmWasm is using the same attribute for their Uint128
sigma: Decimal,
profit_margin: Decimal,
node_profit: Uint128,
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
sigma: U128,
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
profit_margin: U128,
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
node_profit: U128,
}
impl DelegatorRewardParams {
pub fn new(
sigma: Decimal,
profit_margin: Decimal,
node_profit: Uint128,
sigma: U128,
profit_margin: U128,
node_profit: U128,
reward_params: RewardParams,
) -> Self {
DelegatorRewardParams {
@@ -217,14 +223,23 @@ impl DelegatorRewardParams {
pub fn determine_delegation_reward(&self, delegation_amount: Uint128) -> u128 {
// change all values into their fixed representations
let delegation_amount = U128::from_num(delegation_amount.u128());
let circulating_supply = U128::from_num(self.reward_params.circulating_supply());
let scaled_delegation_amount = delegation_amount / Uint128::new(self.reward_params.circulating_supply());
let scaled_delegation_amount = delegation_amount / circulating_supply;
let delegator_reward =
(Decimal::one() - self.profit_margin) * (scaled_delegation_amount / self.sigma.atomics()) * self.node_profit;
(ONE - self.profit_margin) * scaled_delegation_amount / self.sigma * self.node_profit;
let reward = delegator_reward.max(Uint128::zero());
reward.u128()
let reward = delegator_reward.max(U128::ZERO);
if let Some(int_reward) = reward.checked_cast() {
int_reward
} else {
error!(
"Could not cast delegator reward ({}) to u128, returning 0",
reward,
);
0u128
}
}
pub fn node_reward_params(&self) -> RewardParams {
@@ -235,8 +250,8 @@ impl DelegatorRewardParams {
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
pub struct StoredNodeRewardResult {
reward: Uint128,
lambda: Decimal,
sigma: Decimal,
lambda: Uint128,
sigma: Uint128,
}
impl StoredNodeRewardResult {
@@ -244,11 +259,11 @@ impl StoredNodeRewardResult {
self.reward
}
pub fn lambda(&self) -> Decimal {
pub fn lambda(&self) -> Uint128 {
self.lambda
}
pub fn sigma(&self) -> Decimal {
pub fn sigma(&self) -> Uint128 {
self.sigma
}
}
@@ -258,30 +273,45 @@ impl TryFrom<NodeRewardResult> for StoredNodeRewardResult {
fn try_from(node_reward_result: NodeRewardResult) -> Result<Self, Self::Error> {
Ok(StoredNodeRewardResult {
reward: node_reward_result.reward(),
lambda: node_reward_result.lambda(),
sigma: node_reward_result.sigma(),
reward: Uint128::new(
node_reward_result
.reward()
.checked_cast()
.ok_or(MixnetContractError::CastError)?,
),
lambda: Uint128::new(
node_reward_result
.lambda()
.checked_cast()
.ok_or(MixnetContractError::CastError)?,
),
sigma: Uint128::new(
node_reward_result
.sigma()
.checked_cast()
.ok_or(MixnetContractError::CastError)?,
),
})
}
}
#[derive(Debug, Copy, Clone)]
pub struct NodeRewardResult {
reward: Uint128,
lambda: Decimal,
sigma: Decimal,
reward: U128,
lambda: U128,
sigma: U128,
}
impl NodeRewardResult {
pub fn reward(&self) -> Uint128 {
pub fn reward(&self) -> U128 {
self.reward
}
pub fn lambda(&self) -> Decimal {
pub fn lambda(&self) -> U128 {
self.lambda
}
pub fn sigma(&self) -> Decimal {
pub fn sigma(&self) -> U128 {
self.sigma
}
}
@@ -327,10 +357,6 @@ impl MixNodeBond {
U128::from_num(self.mix_node.profit_margin_percent) / U128::from_num(100)
}
pub fn profit_margin_dec(&self) -> Decimal {
Decimal::from_ratio(self.mix_node.profit_margin_percent, 100u128)
}
pub fn identity(&self) -> &String {
&self.mix_node.identity_key
}
@@ -364,61 +390,53 @@ impl MixNodeBond {
self.total_delegation.clone()
}
pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> Decimal {
pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> U128 {
self.total_bond_to_circulating_supply(circulating_supply)
* Decimal::from_atomics(rewarded_set_size, 0).unwrap()
* U128::from_num(rewarded_set_size)
}
// TODO: There is an effect here when adding accumulted rewards to the total bond, ie accumulated rewards will not
// affect lambda, but will affect sigma, in turn over time, if left unclaimed operator rewards will not compound, but
// behave similarly to delegations.
// The question is should this be taken into account when calculating operator rewards?
pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> Decimal {
// U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply);
Decimal::from_atomics(self.pledge_amount().amount.u128(), 0).unwrap()
/ Uint128::from(circulating_supply)
pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply)
}
pub fn total_bond_to_circulating_supply(&self, circulating_supply: u128) -> Decimal {
// U128::from_num(self.pledge_amount().amount.u128() + self.total_delegation().amount.u128())
// / U128::from_num(circulating_supply)
Decimal::from_atomics(
self.total_bond().unwrap_or(0) + self.pledge_amount().amount.u128(),
0,
)
.unwrap()
/ Uint128::from(circulating_supply)
pub fn total_bond_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
U128::from_num(self.pledge_amount().amount.u128() + self.total_delegation().amount.u128())
/ U128::from_num(circulating_supply)
}
pub fn lambda(&self, params: &RewardParams) -> Decimal {
pub fn lambda(&self, params: &RewardParams) -> U128 {
// Ratio of a bond to the token circulating supply
let pledge_to_circulating_supply_ratio =
self.pledge_to_circulating_supply(params.circulating_supply());
pledge_to_circulating_supply_ratio.min(params.one_over_k_dec())
pledge_to_circulating_supply_ratio.min(params.one_over_k())
}
pub fn sigma(&self, params: &RewardParams) -> Decimal {
pub fn sigma(&self, params: &RewardParams) -> U128 {
// Ratio of a delegation to the the token circulating supply
let total_bond_to_circulating_supply_ratio =
self.total_bond_to_circulating_supply(params.circulating_supply());
total_bond_to_circulating_supply_ratio.min(params.one_over_k_dec())
total_bond_to_circulating_supply_ratio.min(params.one_over_k())
}
pub fn estimate_reward(
&self,
params: &RewardParams,
) -> Result<(u64, u64, u64), MixnetContractError> {
let total_node_reward = self
.reward(params)
.reward();
let total_node_reward = self.reward(params);
let operator_reward = self.operator_reward(params);
// Total reward has to be the sum of operator and delegator rewards
let delegators_reward = total_node_reward.u128() - operator_reward;
// TODO: This overestimates the reward by a lot, it should take a Uint128 and return estiamte for that
let delegators_reward = self.reward_delegation(self.total_delegation().amount, params);
Ok((
total_node_reward.u128().try_into()?,
total_node_reward
.reward()
.checked_to_num::<u128>()
.unwrap_or_default()
.try_into()?,
operator_reward.try_into()?,
delegators_reward.try_into()?,
))
@@ -428,16 +446,11 @@ impl MixNodeBond {
let lambda = self.lambda(params);
let sigma = self.sigma(params);
println!("performance: {}", params.performance_dec().atomics());
println!("reward pool: {}", Uint128::from(params.epoch_reward_pool()));
println!("omega: {}", params.omega());
let reward = params.performance_dec()
* Uint128::from(params.epoch_reward_pool())
let reward = params.performance()
* params.epoch_reward_pool()
* (sigma * params.omega()
+ params.alpha_dec() * lambda * sigma * Uint128::from(params.rewarded_set_size()))
/ (Decimal::one() + params.alpha_dec()).atomics();
+ params.alpha() * lambda * sigma * params.rewarded_set_size())
/ (ONE + params.alpha());
NodeRewardResult {
reward,
@@ -446,45 +459,53 @@ impl MixNodeBond {
}
}
pub fn node_profit(&self, params: &RewardParams) -> Uint128 {
if self.reward(params).reward() < params.node.operator_cost_dec() {
Uint128::zero()
pub fn node_profit(&self, params: &RewardParams) -> U128 {
if self.reward(params).reward() < params.node.operator_cost() {
U128::from_num(0u128)
} else {
self.reward(params).reward() - params.node.operator_cost_dec()
self.reward(params).reward() - params.node.operator_cost()
}
}
pub fn operator_reward(&self, params: &RewardParams) -> u128 {
let reward = self.reward(params);
println!("{:?}", reward);
let profit = if reward.reward < params.node.operator_cost_dec() {
Uint128::zero()
let profit = if reward.reward < params.node.operator_cost() {
U128::from_num(0u128)
} else {
reward.reward - params.node.operator_cost_dec()
reward.reward - params.node.operator_cost()
};
let operator_base_reward = reward.reward.min(params.node.operator_cost_dec());
let operator_reward = (self.profit_margin_dec()
+ (Decimal::one() - self.profit_margin_dec()) * reward.lambda / reward.sigma.atomics())
let operator_base_reward = reward.reward.min(params.node.operator_cost());
let operator_reward = (self.profit_margin()
+ (ONE - self.profit_margin()) * reward.lambda / reward.sigma)
* profit;
let reward = (operator_reward + operator_base_reward).max(Uint128::new(0));
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0));
reward.u128()
if let Some(int_reward) = reward.checked_cast() {
int_reward
} else {
error!(
"Could not cast reward ({}) to u128, returning 0 - mixnode {}",
reward,
self.identity()
);
0u128
}
}
// pub fn sigma_ratio(&self, params: &RewardParams) -> U128 {
// if self.total_bond_to_circulating_supply(params.circulating_supply()) < params.one_over_k()
// {
// self.total_bond_to_circulating_supply(params.circulating_supply())
// } else {
// params.one_over_k()
// }
// }
pub fn sigma_ratio(&self, params: &RewardParams) -> U128 {
if self.total_bond_to_circulating_supply(params.circulating_supply()) < params.one_over_k()
{
self.total_bond_to_circulating_supply(params.circulating_supply())
} else {
params.one_over_k()
}
}
pub fn reward_delegation(&self, delegation_amount: Uint128, params: &RewardParams) -> u128 {
let reward_params = DelegatorRewardParams::new(
self.sigma(params),
self.profit_margin_dec(),
self.profit_margin(),
self.node_profit(params),
params.to_owned(),
);
@@ -1,14 +1,10 @@
use crate::{error::MixnetContractError, mixnode::StoredNodeRewardResult, ONE, U128};
use az::CheckedCast;
use cosmwasm_std::{Decimal, Uint128};
use cosmwasm_std::Uint128;
use network_defaults::DEFAULT_OPERATOR_INTERVAL_COST;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
fn sane_decimal(value: &Uint128) -> Decimal {
Decimal::new(value * Uint128::new(1_000_000_000_000_000_000u128))
}
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
pub struct NodeEpochRewards {
params: NodeRewardParams,
@@ -29,11 +25,11 @@ impl NodeEpochRewards {
self.epoch_id
}
pub fn sigma(&self) -> Decimal {
pub fn sigma(&self) -> Uint128 {
self.result.sigma()
}
pub fn lambda(&self) -> Decimal {
pub fn lambda(&self) -> Uint128 {
self.result.lambda()
}
@@ -49,19 +45,6 @@ impl NodeEpochRewards {
U128::from_num(self.params.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
}
pub fn operator_cost_dec(&self) -> Uint128 {
Decimal::from_ratio(self.params.uptime, 100u128)
* Uint128::from(DEFAULT_OPERATOR_INTERVAL_COST)
}
pub fn node_profit_dec(&self) -> Uint128 {
if self.reward() < self.operator_cost_dec() {
Uint128::zero()
} else {
self.reward() - self.operator_cost_dec()
}
}
pub fn node_profit(&self) -> U128 {
let reward = U128::from_num(self.reward().u128());
if reward < self.operator_cost() {
@@ -71,34 +54,44 @@ impl NodeEpochRewards {
}
}
pub fn operator_reward(&self, profit_margin: Decimal) -> Result<Uint128, MixnetContractError> {
let reward = self.node_profit_dec();
let operator_base_reward = reward.min(self.operator_cost_dec());
pub fn operator_reward(&self, profit_margin: U128) -> Result<Uint128, MixnetContractError> {
let reward = self.node_profit();
let operator_base_reward = reward.min(self.operator_cost());
let operator_reward = (profit_margin
+ (Decimal::one() - profit_margin) * self.lambda() / self.sigma().atomics())
+ (ONE - profit_margin) * U128::from_num(self.lambda().u128())
/ U128::from_num(self.sigma().u128()))
* reward;
let reward = (operator_reward + operator_base_reward).max(Uint128::zero());
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0u128));
Ok(reward)
if let Some(int_reward) = reward.checked_cast() {
Ok(Uint128::new(int_reward))
} else {
Err(MixnetContractError::CastError)
}
}
pub fn delegation_reward(
&self,
delegation_amount: Uint128,
profit_margin: Decimal,
profit_margin: U128,
epoch_reward_params: EpochRewardParams,
) -> Result<Uint128, MixnetContractError> {
// change all values into their fixed representations;
// change all values into their fixed representations
let delegation_amount = U128::from_num(delegation_amount.u128());
let circulating_supply = U128::from_num(epoch_reward_params.circulating_supply());
let scaled_delegation_amount =
delegation_amount / Uint128::from(epoch_reward_params.circulating_supply());
let delegator_reward = (Decimal::one() - profit_margin) * scaled_delegation_amount
/ self.sigma().atomics()
* self.node_profit_dec();
let scaled_delegation_amount = delegation_amount / circulating_supply;
let delegator_reward = (ONE - profit_margin) * scaled_delegation_amount
/ U128::from_num(self.sigma().u128())
* self.node_profit();
let reward = delegator_reward.max(Uint128::zero());
Ok(reward)
let reward = delegator_reward.max(U128::ZERO);
if let Some(int_reward) = reward.checked_cast() {
Ok(Uint128::new(int_reward))
} else {
Err(MixnetContractError::CastError)
}
}
}
@@ -183,11 +176,6 @@ impl NodeRewardParams {
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
}
pub fn operator_cost_dec(&self) -> Uint128 {
Decimal::from_ratio(self.uptime.u128(), 100u128)
* Uint128::new(DEFAULT_OPERATOR_INTERVAL_COST as u128)
}
pub fn uptime(&self) -> u128 {
self.uptime.u128()
}
@@ -208,36 +196,18 @@ impl RewardParams {
RewardParams { epoch, node }
}
pub fn omega(&self) -> Uint128 {
pub fn omega(&self) -> U128 {
// As per keybase://chat/nymtech#tokeneconomics/1179
// let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
// - (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
- (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
let active_set_work_factor = self.active_set_work_factor_dec();
let rewarded_set_size = sane_decimal(&self.epoch.rewarded_set_size);
let idle_nodes = sane_decimal(&self.idle_nodes());
println!("active_set_work_factor: {}", active_set_work_factor);
println!("rewarded_set_size: {}", rewarded_set_size);
println!("idle_nodes: {}", idle_nodes);
let denom = active_set_work_factor * rewarded_set_size
- (active_set_work_factor - Decimal::one()) * idle_nodes;
println!("denom: {}", denom);
let result = if self.in_active_set() {
if self.in_active_set() {
// work_active = factor / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
active_set_work_factor
* Decimal::from_ratio(Decimal::one().atomics(), denom.atomics())
* rewarded_set_size
self.active_set_work_factor() / denom * self.rewarded_set_size()
} else {
// work_idle = 1 / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
Decimal::one() / denom.atomics() * rewarded_set_size
};
println!("omega_result: {}", result);
result.atomics()
ONE / denom * self.rewarded_set_size()
}
}
pub fn idle_nodes(&self) -> Uint128 {
@@ -248,10 +218,6 @@ impl RewardParams {
U128::from_num(self.epoch.active_set_work_factor)
}
pub fn active_set_work_factor_dec(&self) -> Decimal {
sane_decimal(&Uint128::new(self.epoch.active_set_work_factor as u128))
}
pub fn in_active_set(&self) -> bool {
self.node.in_active_set
}
@@ -260,10 +226,6 @@ impl RewardParams {
U128::from_num(self.node.uptime.u128()) / U128::from_num(100)
}
pub fn performance_dec(&self) -> Decimal {
Decimal::from_ratio(self.node.uptime, 100u128)
}
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
self.node.reward_blockstamp = blockstamp;
}
@@ -292,15 +254,7 @@ impl RewardParams {
ONE / U128::from_num(self.epoch.rewarded_set_size.u128())
}
pub fn one_over_k_dec(&self) -> Decimal {
Decimal::one() / self.epoch.rewarded_set_size
}
pub fn alpha(&self) -> U128 {
U128::from_num(self.epoch.sybil_resistance_percent) / U128::from_num(100)
}
pub fn alpha_dec(&self) -> Decimal {
Decimal::from_atomics(self.epoch.sybil_resistance_percent, 2).unwrap()
}
}
+6
View File
@@ -22,5 +22,11 @@ pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(
"https://rpc.nyx.nodes.guru/",
Some("https://api.nyx.nodes.guru/"),
), ValidatorDetails::new(
"https://test2.nyx.nodes.guru/",
Some("https://test1.nyx.nodes.guru/"),
), ValidatorDetails::new(
"https://test2.nyx.nodes.guru/",
Some("https://test1.nyx.nodes.guru/"),
)]
}
+10 -10
View File
@@ -241,9 +241,9 @@ dependencies = [
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta8"
version = "1.0.0-beta7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e70111e9701c3ec43bfbff0e523cd4cb115876b4d3433813436dd0934ee962"
checksum = "88c2565b1e73a816fb659ef4838fc356143fbd35f43c48a51d2d7d4e5d6679d3"
dependencies = [
"digest 0.9.0",
"ed25519-zebra",
@@ -254,9 +254,9 @@ dependencies = [
[[package]]
name = "cosmwasm-derive"
version = "1.0.0-beta8"
version = "1.0.0-beta7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bc2ad5d86be5f6068833f63e20786768db6890019c095dd7775232184fb7b3"
checksum = "fa89fcdf8dbbe0088e663d0a814aa7368e7ebe8fb045a3a150fb5fdc2ffe3b45"
dependencies = [
"syn",
]
@@ -273,9 +273,9 @@ dependencies = [
[[package]]
name = "cosmwasm-std"
version = "1.0.0-beta8"
version = "1.0.0-beta7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915ca82bd944f116f3a9717481f3fa657e4a73f28c4887288761ebb24e6fbe10"
checksum = "bcb8f99a61d0b9069e1afc80a4ffea87dcc3523edd992080923870b13a677da0"
dependencies = [
"base64",
"cosmwasm-crypto",
@@ -290,9 +290,9 @@ dependencies = [
[[package]]
name = "cosmwasm-storage"
version = "1.0.0-beta8"
version = "1.0.0-beta7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c4be9fd8c9d3ae7d0c32a925ecbc20707007ce0cba1f7538c0d78b7a2d3729b"
checksum = "07f856099c824aa8f2488e62d1da3fc06383d3fdbc764573595f451be43441a2"
dependencies = [
"cosmwasm-std",
"serde",
@@ -1467,9 +1467,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "uint"
version = "0.9.3"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0"
checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f"
dependencies = [
"byteorder",
"crunchy",
+2 -2
View File
@@ -20,8 +20,8 @@ mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" }
config = { path = "../../common/config"}
cosmwasm-std = "1.0.0-beta8"
cosmwasm-storage = "1.0.0-beta8"
cosmwasm-std = "1.0.0-beta6"
cosmwasm-storage = "1.0.0-beta6"
cw-storage-plus = "0.13.1"
az = "1.2.0"
+1 -5
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use config::defaults::DENOM;
use cosmwasm_std::{StdResult, Storage, Uint128, Decimal};
use cosmwasm_std::{StdResult, Storage, Uint128};
use cw_storage_plus::{Index, IndexList, IndexedSnapshotMap, Map, Strategy, UniqueIndex};
use mixnet_contract_common::U128;
use mixnet_contract_common::{
@@ -135,10 +135,6 @@ impl StoredMixnodeBond {
pub fn profit_margin(&self) -> U128 {
U128::from_num(self.mix_node.profit_margin_percent) / U128::from_num(100)
}
pub fn profit_margin_dec(&self) -> Decimal {
Decimal::from_ratio(self.mix_node.profit_margin_percent, 100u128)
}
}
impl Display for StoredMixnodeBond {
+38 -45
View File
@@ -117,7 +117,7 @@ pub fn calculate_operator_reward(
// Compound rewards from previous heights
let reward_at_height = epoch_rewards.delegation_reward(
bond.pledge_amount().amount + accumulated_reward,
bond.profit_margin_dec(),
bond.profit_margin(),
epoch_reward_params,
)?;
return Ok(accumulated_reward + reward_at_height);
@@ -281,7 +281,7 @@ pub fn calculate_delegator_reward(
epoch_reward_params_for_id(storage, epoch_rewards.epoch_id())?;
let reward_at_height = epoch_rewards.delegation_reward(
delegation_at_height + accumulated_reward,
bond.profit_margin_dec(),
bond.profit_margin(),
epoch_reward_params,
)?;
return Ok(accumulated_reward + reward_at_height);
@@ -440,9 +440,10 @@ pub mod tests {
use crate::rewards::transactions::try_reward_mixnode;
use crate::support::tests;
use crate::support::tests::test_helpers;
use az::CheckedCast;
use config::defaults::DENOM;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coin, coins, Addr, Timestamp, Uint128, Decimal};
use cosmwasm_std::{coin, coins, Addr, Timestamp, Uint128};
use mixnet_contract_common::events::{
must_find_attribute, BOND_TOO_FRESH_VALUE, NO_REWARD_REASON_KEY,
OPERATOR_REWARDING_EVENT_TYPE,
@@ -917,25 +918,25 @@ pub mod tests {
assert_eq!(
mix_1_reward_result.sigma(),
Decimal::zero()
U128::from_num(0.0000266666666666f64)
);
assert_eq!(
mix_1_reward_result.lambda(),
Decimal::zero()
U128::from_num(0.0000133333333333f64)
);
// assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
let mix_2_reward_result = mix_2.reward(&params2);
// assert_eq!(
// mix_2_reward_result.sigma(),
// U128::from_num(0.0000266666666666f64)
// );
// assert_eq!(
// mix_2_reward_result.lambda(),
// U128::from_num(0.0000133333333333f64)
// );
// assert_eq!(mix_2_reward_result.reward().int(), 129557u128);
assert_eq!(
mix_2_reward_result.sigma(),
U128::from_num(0.0000266666666666f64)
);
assert_eq!(
mix_2_reward_result.lambda(),
U128::from_num(0.0000133333333333f64)
);
assert_eq!(mix_2_reward_result.reward().int(), 129557u128);
let mix_3_reward_result = mix_3.reward(&params3);
@@ -946,7 +947,8 @@ pub mod tests {
fn test_tokenomics_rewarding() {
use crate::constants::INTERVAL_REWARD_PERCENT;
use crate::contract::INITIAL_REWARD_POOL;
use mixnet_contract_common::U128;
type U128 = fixed::types::U75F53;
let mut deps = test_helpers::init_contract();
let mut env = mock_env();
@@ -1015,37 +1017,28 @@ pub mod tests {
assert_eq!(
mix_1_reward_result.sigma(),
Decimal::new(Uint128::new(40000000000000))
U128::from_num(0.0000266666666666f64)
);
assert_eq!(
mix_1_reward_result.lambda(),
Decimal::new(Uint128::new(13333333333333))
U128::from_num(0.0000133333333333f64)
);
// assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
// assert_eq!(mix_1.node_profit(&params).int(), 203558u128);
assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
let mix1_operator_reward = mix_1.operator_reward(&params);
let mix1_delegator1_reward = mix_1.reward_delegation(Uint128::new(8000_000000), &params);
let mix1_delegator2_reward = mix_1.reward_delegation(Uint128::new(2000_000000), &params);
let mix1_total_delegator_reward = mix_1.reward_delegation(Uint128::new(10000_000000), &params);
assert_eq!(mix1_operator_reward, 167513);
assert_eq!(mix1_delegator1_reward, 73280);
assert_eq!(mix1_delegator2_reward, 18320);
// Rounding errors make this not be equal
assert!(mix1_total_delegator_reward > mix1_delegator1_reward + mix1_delegator2_reward);
// assert_eq!(
// mix_1_reward_result.reward().int(),
// mix1_operator_reward + mix1_delegator1_reward + mix1_delegator2_reward + 1
// );
// assert_eq!(
// mix1_operator_reward + mix1_delegator1_reward + mix1_delegator2_reward + 1,
// mix_1_reward_result.reward().int()
// );
assert_eq!(
mix1_operator_reward + mix1_delegator1_reward + mix1_delegator2_reward + 1,
mix_1_reward_result.reward().int()
);
let pre_reward_bond =
test_helpers::read_mixnode_pledge_amount(&deps.storage, &node_identity)
@@ -1099,18 +1092,18 @@ pub mod tests {
);
// it's all correctly saved
// match storage::REWARDING_STATUS
// .load(deps.as_ref().storage, (0u32, node_identity))
// .unwrap()
// {
// RewardingStatus::Complete(result) => assert_eq!(
// RewardingResult {
// node_reward: Uint128::new(mix_1_reward_result.reward().checked_cast().unwrap()),
// },
// result
// ),
// _ => unreachable!(),
// }
match storage::REWARDING_STATUS
.load(deps.as_ref().storage, (0u32, node_identity))
.unwrap()
{
RewardingStatus::Complete(result) => assert_eq!(
RewardingResult {
node_reward: Uint128::new(mix_1_reward_result.reward().checked_cast().unwrap()),
},
result
),
_ => unreachable!(),
}
}
#[cfg(test)]
+12 -12
View File
@@ -358,9 +358,9 @@
}
},
"node_modules/ansi-align/node_modules/ansi-regex": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"engines": {
"node": ">=6"
}
@@ -403,9 +403,9 @@
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"engines": {
"node": ">=8"
}
@@ -2309,9 +2309,9 @@
},
"dependencies": {
"ansi-regex": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"emoji-regex": {
"version": "7.0.3",
@@ -2344,9 +2344,9 @@
}
},
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"ansi-styles": {
"version": "4.3.0",
+11 -1
View File
@@ -4,7 +4,17 @@
*/
module.exports = {
invoke: (operation) => {
invoke: (operation, ...args) => {
switch(operation) {
case 'get_validator_nymd_urls': {
const { network } = args;
return new Promise(resolve => {
resolve({
urls: ['foo', 'bar'],
});
});
}
}
console.error(`Tauri cannot be used in Storybook. The operation requested was "${operation}". You can add mock responses to "nym_wallet/.storybook/mocks/tauri.js" if you need. The default response is "void".`);
return new Promise((resolve, reject) => {
reject(new Error(`Tauri operation ${operation} not available in storybook.`));
+12 -1
View File
@@ -4,9 +4,11 @@ import { Logout } from '@mui/icons-material';
import { ClientContext } from '../context/main';
import { NetworkSelector } from './NetworkSelector';
import { Node as NodeIcon } from '../svg-icons/node';
import { Delegate as DelegateIcon } from '../svg-icons';
export const AppBar = () => {
const { showSettings, logOut, handleShowSettings } = useContext(ClientContext);
const { showSettings, showValidatorSettings, logOut, handleShowSettings, handleShowValidatorSettings } = useContext(ClientContext);
return (
<MuiAppBar position="sticky" sx={{ boxShadow: 'none', bgcolor: 'transparent' }}>
@@ -16,6 +18,15 @@ export const AppBar = () => {
<NetworkSelector />
</Grid>
<Grid item container justifyContent="flex-end" md={12} lg={5} spacing={2}>
<Grid item>
<IconButton
onClick={handleShowValidatorSettings}
sx={{ color: showValidatorSettings ? 'primary.main' : 'nym.background.dark' }}
size="small"
>
<DelegateIcon fontSize="small" />
</IconButton>
</Grid>
<Grid item>
<IconButton
onClick={handleShowSettings}
+61 -10
View File
@@ -2,10 +2,10 @@ import React, { useMemo, createContext, useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { useSnackbar } from 'notistack';
import { TLoginType } from 'src/pages/sign-in/types';
import { Account, Network, TCurrency, TMixnodeBondDetails } from '../types';
import { Account, Network, TCurrency, TMixnodeBondDetails, ValidatorUrls } from '../types';
import { TUseuserBalance, useGetBalance } from '../hooks/useGetBalance';
import { config } from '../../config';
import { getMixnodeBondDetails, selectNetwork, signInWithMnemonic, signInWithPassword, signOut } from '../requests';
import { getMixnodeBondDetails, selectNetwork, signInWithMnemonic, signInWithPassword, signOut, getValidatorUrls, selectValidatorNymdUrl } from '../requests';
import { currencyMap } from '../utils';
import { Console } from '../utils/console';
@@ -14,13 +14,13 @@ export const { ADMIN_ADDRESS, IS_DEV_MODE } = config;
export const urls = (networkName?: Network) =>
networkName === 'MAINNET'
? {
blockExplorer: 'https://blocks.nymtech.net',
networkExplorer: 'https://explorer.nymtech.net',
}
blockExplorer: 'https://blocks.nymtech.net',
networkExplorer: 'https://explorer.nymtech.net',
}
: {
blockExplorer: `https://${networkName}-blocks.nymtech.net`,
networkExplorer: `https://${networkName}-explorer.nymtech.net`,
};
blockExplorer: `https://${networkName}-blocks.nymtech.net`,
networkExplorer: `https://${networkName}-explorer.nymtech.net`,
};
type TClientContext = {
mode: 'light' | 'dark';
@@ -29,6 +29,8 @@ type TClientContext = {
userBalance: TUseuserBalance;
showAdmin: boolean;
showSettings: boolean;
showValidatorSettings: boolean;
validatorsUrl?: ValidatorUrls;
network?: Network;
currency?: TCurrency;
isLoading: boolean;
@@ -37,7 +39,10 @@ type TClientContext = {
setError: (value?: string) => void;
switchNetwork: (network: Network) => void;
getBondDetails: () => Promise<void>;
fetchValidatorsUrl: (network: Network) => Promise<void>;
selectValidatorNymd: (validatorNymd: string, network: Network) => Promise<void>;
handleShowSettings: () => void;
handleShowValidatorSettings: () => void;
handleShowAdmin: () => void;
logIn: (opts: { type: 'mnemonic' | 'password'; value: string }) => void;
signInWithPassword: (password: string) => void;
@@ -49,10 +54,12 @@ export const ClientContext = createContext({} as TClientContext);
export const ClientContextProvider = ({ children }: { children: React.ReactNode }) => {
const [clientDetails, setClientDetails] = useState<Account>();
const [mixnodeDetails, setMixnodeDetails] = useState<TMixnodeBondDetails | null>();
const [validatorsUrl, setValidatorsUrl] = useState<ValidatorUrls>();
const [network, setNetwork] = useState<Network | undefined>();
const [currency, setCurrency] = useState<TCurrency>();
const [showAdmin, setShowAdmin] = useState(false);
const [showSettings, setShowSettings] = useState(false);
const [showValidatorSettings, setShowValidatorSettings] = useState(false);
const [mode] = useState<'light' | 'dark'>('light');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string>();
@@ -83,17 +90,49 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode
}
};
const fetchValidatorsUrl = async (network: Network) => {
try {
if (network) {
const urls = await getValidatorUrls(network);
setValidatorsUrl(urls);
}
} catch (e) {
Console.error(e as string);
}
};
const selectValidatorNymd = async (validatorNymd: string, network: Network) => {
try {
if (network) {
const response = await selectValidatorNymdUrl(validatorNymd, network);
return response;
}
} catch (e) {
Console.error(e as string);
}
};
useEffect(() => {
const refreshAccount = async () => {
if (network) {
await loadAccount(network);
await getBondDetails();
await userBalance.fetchBalance();
await fetchValidatorsUrl(network);
}
};
refreshAccount();
}, [network]);
useEffect(() => {
const refreshValidators = async () => {
if (!validatorsUrl && network) {
await fetchValidatorsUrl(network);
}
};
refreshValidators();
}, [showValidatorSettings]);
const logIn = async ({ type, value }: { type: TLoginType; value: string }) => {
if (value.length === 0) {
setError(`A ${type} must be provided`);
@@ -127,7 +166,14 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode
};
const handleShowAdmin = () => setShowAdmin((show) => !show);
const handleShowSettings = () => setShowSettings((show) => !show);
const handleShowSettings = () => {
setShowSettings((show) => !show)
setShowValidatorSettings(false);
};
const handleShowValidatorSettings = () => {
setShowValidatorSettings((show) => !show)
setShowSettings(false);
};
const switchNetwork = (_network: Network) => setNetwork(_network);
const memoizedValue = useMemo(
@@ -140,6 +186,8 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode
userBalance,
showAdmin,
showSettings,
showValidatorSettings,
validatorsUrl,
network,
currency,
setIsLoading,
@@ -147,12 +195,15 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode
signInWithPassword,
switchNetwork,
getBondDetails,
fetchValidatorsUrl,
selectValidatorNymd,
handleShowSettings,
handleShowValidatorSettings,
handleShowAdmin,
logIn,
logOut,
}),
[mode, isLoading, error, clientDetails, mixnodeDetails, userBalance, showAdmin, showSettings, network, currency],
[mode, isLoading, error, clientDetails, mixnodeDetails, userBalance, showAdmin, showSettings, showValidatorSettings, validatorsUrl, selectValidatorNymd, network, currency],
);
return <ClientContext.Provider value={memoizedValue}>{children}</ClientContext.Provider>;
+2 -1
View File
@@ -6,7 +6,7 @@ import { SnackbarProvider } from 'notistack';
import { AppRoutes, SignInRoutes } from './routes';
import { ClientContext, ClientContextProvider } from './context/main';
import { ApplicationLayout } from './layouts';
import { Admin, Settings } from './pages';
import { Admin, Settings, ValidatorSettingsModal } from './pages';
import { ErrorFallback } from './components';
import { NymWalletTheme, WelcomeTheme } from './theme';
import { maximizeWindow } from './utils';
@@ -29,6 +29,7 @@ const App = () => {
<NymWalletTheme>
<ApplicationLayout>
<Settings />
<ValidatorSettingsModal />
<Admin />
<AppRoutes />
</ApplicationLayout>
+1
View File
@@ -9,3 +9,4 @@ export * from './sign-in';
export * from './settings';
export * from './unbond';
export * from './undelegate';
export * from './validators';
@@ -0,0 +1 @@
export { ValidatorSettingsModal } from './validatorModal';
@@ -0,0 +1,88 @@
import React from 'react';
import { ComponentMeta } from '@storybook/react';
import { Button, Paper } from '@mui/material';
import { ValidatorSettingsModal } from './validatorModal';
export default {
title: 'Modals/Validator Setting Modal',
component: ValidatorSettingsModal,
} as ComponentMeta<typeof ValidatorSettingsModal>;
export const Default = () => {
const [open, setOpen] = React.useState<boolean>(true);
return (
<>
<Paper elevation={0} sx={{ px: 4, pt: 2, pb: 4 }}>
<h2>Lorem ipsum</h2>
<Button variant="contained" onClick={() => setOpen(true)}>
Show modal
</Button>
<p>
Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis
sunt velit elit do minim mollit non duis reprehenderit. Eiusmod dolore adipisicing ex nostrud consectetur
culpa exercitation do. Ad elit esse ipsum aliqua labore irure laborum qui culpa.
</p>
<p>
Occaecat commodo excepteur anim ut officia dolor laboris dolore id occaecat enim qui eiusmod occaecat aliquip
ad tempor. Labore amet laborum magna amet consequat dolor cupidatat in consequat sunt aliquip magna laboris
tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna
elit ut mollit.
</p>
<p>
Labore voluptate elit amet ipsum qui officia duis in et occaecat culpa ex do non labore mollit. Cillum
cupidatat duis ea dolore laboris laboris sunt duis anim consectetur cupidatat nulla ad minim sunt ea. Aliqua
amet commodo est irure sint magna sunt. Pariatur dolore commodo labore quis incididunt proident duis voluptate
exercitation in duis. Occaecat aliqua laboris reprehenderit nostrud est aute pariatur fugiat anim. Dolore sunt
cillum ea aliquip consectetur laborum ipsum qui veniam Lorem consectetur adipisicing velit magna aute. Amet
tempor quis excepteur minim culpa velit Lorem enim ad.
</p>
<p>
Mollit laborum exercitation excepteur laboris adipisicing ipsum veniam cillum mollit voluptate do. Amet et
anim Lorem mollit minim duis cupidatat non. Consectetur sit deserunt nisi nisi non excepteur dolor eiusmod
aute aute irure anim dolore ipsum et veniam.
</p>
</Paper>
<ValidatorSettingsModal/>
</>
);
};
export const NoSubheader = () => {
const [open, setOpen] = React.useState<boolean>(true);
return (
<>
<Paper elevation={0} sx={{ px: 4, pt: 2, pb: 4 }}>
<h2>Lorem ipsum</h2>
<Button variant="contained" onClick={() => setOpen(true)}>
Show modal
</Button>
<p>
Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis
sunt velit elit do minim mollit non duis reprehenderit. Eiusmod dolore adipisicing ex nostrud consectetur
culpa exercitation do. Ad elit esse ipsum aliqua labore irure laborum qui culpa.
</p>
<p>
Occaecat commodo excepteur anim ut officia dolor laboris dolore id occaecat enim qui eiusmod occaecat aliquip
ad tempor. Labore amet laborum magna amet consequat dolor cupidatat in consequat sunt aliquip magna laboris
tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna
elit ut mollit.
</p>
<p>
Labore voluptate elit amet ipsum qui officia duis in et occaecat culpa ex do non labore mollit. Cillum
cupidatat duis ea dolore laboris laboris sunt duis anim consectetur cupidatat nulla ad minim sunt ea. Aliqua
amet commodo est irure sint magna sunt. Pariatur dolore commodo labore quis incididunt proident duis voluptate
exercitation in duis. Occaecat aliqua laboris reprehenderit nostrud est aute pariatur fugiat anim. Dolore sunt
cillum ea aliquip consectetur laborum ipsum qui veniam Lorem consectetur adipisicing velit magna aute. Amet
tempor quis excepteur minim culpa velit Lorem enim ad.
</p>
<p>
Mollit laborum exercitation excepteur laboris adipisicing ipsum veniam cillum mollit voluptate do. Amet et
anim Lorem mollit minim duis cupidatat non. Consectetur sit deserunt nisi nisi non excepteur dolor eiusmod
aute aute irure anim dolore ipsum et veniam.
</p>
</Paper>
<ValidatorSettingsModal />
</>
);
};
@@ -0,0 +1,142 @@
import React, { useContext, useEffect, useState } from 'react';
import { Button, Box, Dialog, CircularProgress, Typography } from '@mui/material';
import { NymCard } from '../../components';
import { ClientContext } from '../../context/main';
import { ValidatorSelector } from './validatorSelector';
import { Delegate as DelegateIcon } from '../../svg-icons';
import { Console } from '../../utils/console';
export const ValidatorSettingsModal = () => {
const [isSubmitting, setIsSubmitting] = useState(false);
const [validatorSelectedSuccessfully, setValidatorSelectedSuccessfully] = useState(false);
const [validator, setValidator] = useState('');
const { showValidatorSettings, getBondDetails, handleShowValidatorSettings, network, selectValidatorNymd } = useContext(ClientContext);
useEffect(() => {
if (showValidatorSettings) {
getBondDetails();
} else {
setValidatorSelectedSuccessfully(false);
};
}, [showValidatorSettings]);
const onDataChanged = (selectedValidator: string) => {
if (selectedValidator) {
setValidator(selectedValidator);
};
}
const handleSubmit = async () => {
setIsSubmitting(true);
try {
if (network) {
selectValidatorNymd(validator, network).then(res => console.log('res', res));
setValidatorSelectedSuccessfully(true);
}
} catch (e) {
Console.error(e as string);
} finally {
setIsSubmitting(false);
}
};
return showValidatorSettings ? (
<Dialog open onClose={handleShowValidatorSettings} maxWidth="md" fullWidth>
<NymCard
title={
<Box display="flex" alignItems="center">
<DelegateIcon sx={{ mr: 1 }} />
Settings
</Box>
}
noPadding
>
<Box
sx={{
display: 'flex',
alignItems: 'center',
padding: 3,
borderTop: '1px solid',
borderColor: 'grey.300',
}}
>
<Typography
variant="h6"
sx={{
fontWeight: 600,
}}>
Wallet Settings
</Typography>
</Box>
<Box
sx={{
display: 'flex',
alignItems: 'center',
p: 2,
pl: 3,
borderTop: '1px solid',
borderBottom: '1px solid',
borderColor: 'grey.300',
bgcolor: 'grey.200',
}}
>
<Typography
variant="h6"
sx={{
fontWeight: 600,
}}>
Validators
</Typography>
</Box>
<>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
minHeight: 300,
padding: 3,
}}
>
<ValidatorSelector
type="Validator API Url"
onChangeValidatorSelection={(selectedValidator) => onDataChanged(selectedValidator)}
/>
{validatorSelectedSuccessfully && (
<Typography sx={{ pt: 2, fontSize: 12, color: (theme) => theme.palette.success.light }}>
Successfully selected the validator: {validator}
</Typography>
)}
</Box>
</>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: 3,
bgcolor: 'grey.200',
borderTop: '1px solid',
borderColor: 'grey.300',
}}
>
<Button
size="large"
variant="contained"
data-testid="validatorsSettings-button"
color="primary"
disableElevation
onClick={() => handleSubmit()}
disabled={isSubmitting}
endIcon={isSubmitting && <CircularProgress size={20} />}
>
Save Changes
</Button>
</Box>
</NymCard>
</Dialog>
) : null;
};
@@ -0,0 +1,49 @@
import React, { useContext, useEffect, useState } from 'react';
import { FormControl, InputLabel, ListItemText, MenuItem, Select, SelectChangeEvent, Typography, useMediaQuery } from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { ClientContext } from '../../context/main';
type TValidatorUrl = string;
export const ValidatorSelector: React.FC<{ onChangeValidatorSelection: (validator: TValidatorUrl) => void, type: string }> = ({
onChangeValidatorSelection,
}) => {
const [selectedValidator, setSelectedValidator] = useState<TValidatorUrl>('');
const {
validatorsUrl
} = useContext(ClientContext);
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down('sm'));
useEffect(() => {
onChangeValidatorSelection(selectedValidator);
}, [selectedValidator]);
return (
<FormControl fullWidth>
<InputLabel id="validatorSelect_label">Validator API Url</InputLabel>
<Select
labelId="validatorSelect_label"
id="validatorSelect"
sx={{
width: '100%'
}}
value={selectedValidator || ''}
label="Choose a Validator"
onChange={(e: SelectChangeEvent) => {
setSelectedValidator(e.target.value as TValidatorUrl);
}}
renderValue={(value) => <Typography sx={{ textTransform: 'capitalize' }}>{value}</Typography>}
>
{
validatorsUrl && validatorsUrl.urls.map((validator) => (
<MenuItem value={validator} key={validator}>
<ListItemText>{validator}</ListItemText>
</MenuItem>
))
}
</Select>
</FormControl>
)
};
+1
View File
@@ -5,3 +5,4 @@ export * from './contract';
export * from './vesting';
export * from './network';
export * from './queries';
export * from './validators';
+16
View File
@@ -0,0 +1,16 @@
import { invoke } from '@tauri-apps/api';
import { Network } from '../types';
import {
ValidatorUrls
} from '../types';
export const getValidatorUrls = async (network: Network): Promise<ValidatorUrls> => {
const res: ValidatorUrls = await invoke('get_validator_nymd_urls', { network });
return res;
};
export const selectValidatorNymdUrl = async (validator: string, network: Network): Promise<void> => {
const res: void = await invoke('select_validator_nymd_url', { url: validator, network });
return res;
};
@@ -0,0 +1,10 @@
import * as React from 'react';
import { ComponentMeta } from '@storybook/react';
import { Playground } from '@nymproject/react';
export default {
title: 'Playground',
component: Playground,
} as ComponentMeta<typeof Playground>;
export const AllControls = () => <Playground />;
+1
View File
@@ -23,3 +23,4 @@ export * from './vestingperiod';
export * from './pendingundelegate';
export * from './delegationevent';
export * from './epoch';
export * from './validatorurls';
+1 -1
View File
@@ -2,7 +2,7 @@ import { invoke } from '@tauri-apps/api';
import { appWindow } from '@tauri-apps/api/window';
import bs58 from 'bs58';
import { valid } from 'semver';
import { userBalance, majorToMinor, getLockedCoins, getSpendableCoins } from '../requests';
import { userBalance, majorToMinor, getLockedCoins, getSpendableCoins, getValidatorUrls } from '../requests';
import { Coin, Network, TCurrency } from '../types';
import { Console } from './console';
+1 -1
View File
@@ -1,3 +1,3 @@
export * from './components';
export * from './hooks';
export { Playground } from './playground';
export { Playground } from './playground/Playground';
@@ -22,4 +22,4 @@ export const Playground: React.FC = () => (
<h2>Fonts</h2>
<PlaygroundFonts />
</>
);
);