Compare commits

..

3 Commits

Author SHA1 Message Date
2afc38068a
additional validator info in nominator tab
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-02-16 16:23:00 +03:00
fafc5bd2c2
show spinner for non-validator stash
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-02-16 14:26:19 +03:00
90c07aa339
decrement nonce on transaction error
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-02-16 13:55:03 +03:00
8 changed files with 80 additions and 21 deletions

View File

@ -2,7 +2,7 @@
name = "ghost-eye" name = "ghost-eye"
authors = ["str3tch <stretch@ghostchain.io>"] authors = ["str3tch <stretch@ghostchain.io>"]
description = "Application for interacting with Casper/Ghost nodes that are exposing RPC only to the localhost" description = "Application for interacting with Casper/Ghost nodes that are exposing RPC only to the localhost"
version = "0.3.24" version = "0.3.27"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -114,7 +114,7 @@ pub enum Action {
SetStashAccount([u8; 32]), SetStashAccount([u8; 32]),
SetStashSecret([u8; 32]), SetStashSecret([u8; 32]),
SetChoosenValidator([u8; 32], u32, u32), SetChoosenValidator([u8; 32], u32, u32),
SetSlashingSpansLength(usize), SetSlashingSpansLength(usize, [u8; 32]),
SetUnlockingIsEmpty(bool), SetUnlockingIsEmpty(bool),
BestBlockInformation(H256, u32), BestBlockInformation(H256, u32),
@ -134,9 +134,9 @@ pub enum Action {
SetValidatorEraSlash(u32, u128), SetValidatorEraSlash(u32, u128),
SetValidatorEraUnlocking(u32, u128), SetValidatorEraUnlocking(u32, u128),
SetIsBonded(bool), SetIsBonded(bool),
SetStakedAmountRatio(u128, u128), SetStakedAmountRatio(u128, u128, [u8; 32]),
SetStakedRatio(u128, u128, [u8; 32]), SetStakedRatio(u128, u128, [u8; 32]),
SetValidatorPrefs(u32, bool, [u8; 32]), SetValidatorPrefs(Option<u32>, bool, [u8; 32]),
SetCurrentValidatorEraRewards(u32, u32, Vec<EraRewardPoints>), SetCurrentValidatorEraRewards(u32, u32, Vec<EraRewardPoints>),
GetTotalIssuance, GetTotalIssuance,

View File

@ -19,9 +19,12 @@ pub struct CurrentValidatorDetails {
choosen: [u8; 32], choosen: [u8; 32],
total_balance: u128, total_balance: u128,
own_balance: u128, own_balance: u128,
active_stake: u128,
others_len: usize, others_len: usize,
commission: f64, commission: f64,
points_ratio: f64, points_ratio: f64,
is_active_validator: bool,
is_nomination_disabled: bool,
} }
impl Default for CurrentValidatorDetails { impl Default for CurrentValidatorDetails {
@ -41,10 +44,13 @@ impl CurrentValidatorDetails {
choosen: [0u8; 32], choosen: [0u8; 32],
total_balance: 0, total_balance: 0,
own_balance: 0, own_balance: 0,
active_stake: 0,
others_len: 0, others_len: 0,
commission: 0.0, commission: 0.0,
points_ratio: 0.0, points_ratio: 0.0,
palette: Default::default(), palette: Default::default(),
is_active_validator: false,
is_nomination_disabled: false,
} }
} }
@ -54,6 +60,37 @@ impl CurrentValidatorDetails {
format!("{:.after$}{}", value, Self::TICKER) format!("{:.after$}{}", value, Self::TICKER)
} }
fn prepare_stake_imbalance(&self) -> String {
let (value, prefix) = if self.own_balance <= self.active_stake {
(self.active_stake.saturating_sub(self.own_balance), "+")
} else {
(self.own_balance.saturating_sub(self.active_stake), "-")
};
format!("{}{}", prefix, self.prepare_u128(value))
}
fn prepare_state_string(&self) -> String {
if self.is_active_validator {
"active staking".to_string()
} else {
"stop staking".to_string()
}
}
fn update_commission(&mut self, maybe_commission: Option<u32>, is_disabled: bool) {
self.is_nomination_disabled = is_disabled;
match maybe_commission {
Some(commission) => {
self.commission = commission as f64 / 1_000_000_000.0;
self.is_active_validator = true;
},
None => {
self.commission = 0.0;
self.is_active_validator = false;
},
}
}
fn update_choosen_validator(&mut self, account_id: [u8; 32], individual: u32, total: u32) { fn update_choosen_validator(&mut self, account_id: [u8; 32], individual: u32, total: u32) {
self.choosen = account_id; self.choosen = account_id;
self.points_ratio = match total { self.points_ratio = match total {
@ -64,6 +101,7 @@ impl CurrentValidatorDetails {
let _ = network_tx.send(Action::GetErasStakersOverview(account_id, false)); let _ = network_tx.send(Action::GetErasStakersOverview(account_id, false));
let _ = network_tx.send(Action::GetNominatorsByValidator(account_id, false)); let _ = network_tx.send(Action::GetNominatorsByValidator(account_id, false));
let _ = network_tx.send(Action::GetValidatorPrefs(account_id, false)); let _ = network_tx.send(Action::GetValidatorPrefs(account_id, false));
let _ = network_tx.send(Action::GetValidatorLedger(account_id, false));
} }
} }
} }
@ -95,9 +133,9 @@ impl Component for CurrentValidatorDetails {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetChoosenValidator(account_id, individual, total) => self.update_choosen_validator(account_id, individual, total), Action::SetChoosenValidator(account_id, individual, total) => self.update_choosen_validator(account_id, individual, total),
Action::SetValidatorPrefs(commission, is_disabled, account_id) if self.choosen == account_id => self.update_commission(commission, is_disabled),
Action::SetNominatorsByValidator(noms, account_id) if self.choosen == account_id => self.others_len = noms.len(), Action::SetNominatorsByValidator(noms, account_id) if self.choosen == account_id => self.others_len = noms.len(),
Action::SetValidatorPrefs(commission, _, account_id) if self.choosen == account_id => Action::SetStakedAmountRatio(_, active_stake, account_id) if self.choosen == account_id => self.active_stake = active_stake,
self.commission = commission as f64 / 1_000_000_000.0,
Action::SetStakedRatio(total, own, account_id) if self.choosen == account_id => { Action::SetStakedRatio(total, own, account_id) if self.choosen == account_id => {
self.total_balance = total; self.total_balance = total;
self.own_balance = own; self.own_balance = own;
@ -113,6 +151,10 @@ impl Component for CurrentValidatorDetails {
let table = Table::new( let table = Table::new(
vec![ vec![
Row::new(vec![
Cell::from(Text::from("Forbidden".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.is_nomination_disabled.to_string()).alignment(Alignment::Right)),
]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Total staked".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Total staked".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.total_balance)).alignment(Alignment::Right)), Cell::from(Text::from(self.prepare_u128(self.total_balance)).alignment(Alignment::Right)),
@ -133,6 +175,14 @@ impl Component for CurrentValidatorDetails {
Cell::from(Text::from("Points ratio".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Points ratio".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(format!("{:.4}%", self.points_ratio)).alignment(Alignment::Right)), Cell::from(Text::from(format!("{:.4}%", self.points_ratio)).alignment(Alignment::Right)),
]), ]),
Row::new(vec![
Cell::from(Text::from("In next era".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_state_string()).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("Imbalance".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_stake_imbalance()).alignment(Alignment::Right)),
]),
], ],
[ [
Constraint::Max(12), Constraint::Max(12),

View File

@ -8,6 +8,7 @@ use ratatui::{
}; };
use super::{PartialComponent, Component, CurrentTab}; use super::{PartialComponent, Component, CurrentTab};
use crate::widgets::DotSpinner;
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -16,7 +17,7 @@ use crate::{
pub struct RewardDetails { pub struct RewardDetails {
palette: StylePalette, palette: StylePalette,
commission: u32, commission: Option<u32>,
nominators_blocked: bool, nominators_blocked: bool,
apy: String, apy: String,
inflation: String, inflation: String,
@ -33,7 +34,7 @@ impl RewardDetails {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
palette: StylePalette::default(), palette: StylePalette::default(),
commission: 0, commission: None,
nominators_blocked: false, nominators_blocked: false,
apy: String::from("0.0%"), apy: String::from("0.0%"),
inflation: String::from("0.0%"), inflation: String::from("0.0%"),
@ -42,11 +43,12 @@ impl RewardDetails {
} }
fn comission_to_string(&self) -> String { fn comission_to_string(&self) -> String {
if self.nominators_blocked { match self.commission {
"blocked".to_string() Some(commission) => {
} else { if self.nominators_blocked { "blocked".to_string() }
let result = self.commission as f64 / 1_000_000_000.0; else { format!("{:.2}%", commission as f64 / 1_000_000_000.0) }
format!("{:.2}%", result) },
None => DotSpinner::default().to_string(),
} }
} }
} }

View File

@ -99,7 +99,7 @@ impl Component for StashDetails {
Action::SetStashSecret(secret) => self.stash_secret = secret, Action::SetStashSecret(secret) => self.stash_secret = secret,
Action::SetStashAccount(account_id) => self.stash_account_id = account_id, Action::SetStashAccount(account_id) => self.stash_account_id = account_id,
Action::SetIsBonded(is_bonded) => self.is_bonded = is_bonded, Action::SetIsBonded(is_bonded) => self.is_bonded = is_bonded,
Action::SetStakedAmountRatio(total, active) => { Action::SetStakedAmountRatio(total, active, account_id) if self.stash_account_id == account_id => {
self.staked_total = Some(total); self.staked_total = Some(total);
self.staked_active = Some(active); self.staked_active = Some(active);
}, },

View File

@ -21,6 +21,7 @@ pub struct WithdrawPopup {
is_active: bool, is_active: bool,
action_tx: Option<UnboundedSender<Action>>, action_tx: Option<UnboundedSender<Action>>,
network_tx: Option<Sender<Action>>, network_tx: Option<Sender<Action>>,
stash_account: [u8; 32],
secret_seed: [u8; 32], secret_seed: [u8; 32],
slashing_spans_length: u32, slashing_spans_length: u32,
unlocking_is_empty: bool, unlocking_is_empty: bool,
@ -39,6 +40,7 @@ impl WithdrawPopup {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
is_active: false, is_active: false,
stash_account: [0u8; 32],
secret_seed: [0u8; 32], secret_seed: [0u8; 32],
slashing_spans_length: 0u32, slashing_spans_length: 0u32,
unlocking_is_empty: false, unlocking_is_empty: false,
@ -118,9 +120,12 @@ impl Component for WithdrawPopup {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetStashAccount(account_id) => self.stash_account = account_id,
Action::SetStashSecret(secret_seed) => self.secret_seed = secret_seed, Action::SetStashSecret(secret_seed) => self.secret_seed = secret_seed,
Action::SetSlashingSpansLength(length) => self.slashing_spans_length = length as u32, Action::SetSlashingSpansLength(length, account_id) if self.stash_account == account_id =>
Action::SetStakedAmountRatio(_, active_balance) => self.active_balance = active_balance, self.slashing_spans_length = length as u32,
Action::SetStakedAmountRatio(_, active_balance, account_id) if self.stash_account == account_id =>
self.active_balance = active_balance,
Action::SetUnlockingIsEmpty(is_empty) => self.unlocking_is_empty = is_empty, Action::SetUnlockingIsEmpty(is_empty) => self.unlocking_is_empty = is_empty,
Action::SetExistentialDeposit(existential_deposit) => self.existential_deposit = existential_deposit, Action::SetExistentialDeposit(existential_deposit) => self.existential_deposit = existential_deposit,
_ => {} _ => {}

View File

@ -434,7 +434,7 @@ pub async fn get_validators_ledger(
.await?; .await?;
if let Some(ledger) = maybe_ledger { if let Some(ledger) = maybe_ledger {
action_tx.send(Action::SetStakedAmountRatio(ledger.total, ledger.active))?; action_tx.send(Action::SetStakedAmountRatio(ledger.total, ledger.active, *account_id))?;
action_tx.send(Action::SetUnlockingIsEmpty(ledger.unlocking.0.is_empty()))?; action_tx.send(Action::SetUnlockingIsEmpty(ledger.unlocking.0.is_empty()))?;
for chunk in ledger.unlocking.0.iter() { for chunk in ledger.unlocking.0.iter() {
action_tx.send(Action::SetValidatorEraUnlocking(chunk.era, chunk.value))?; action_tx.send(Action::SetValidatorEraUnlocking(chunk.era, chunk.value))?;
@ -515,10 +515,9 @@ pub async fn get_validator_prefs(
let maybe_validator_prefs = super::raw_calls::staking::validators(api, None, account_id) let maybe_validator_prefs = super::raw_calls::staking::validators(api, None, account_id)
.await?; .await?;
let (comission, blocked) = match maybe_validator_prefs { let (comission, blocked) = match maybe_validator_prefs {
Some(prefs) => (prefs.commission.0, prefs.blocked), Some(prefs) => (Some(prefs.commission.0), prefs.blocked),
None => (0, false), None => (None, false),
}; };
action_tx.send(Action::SetValidatorPrefs(comission, blocked, *account_id))?; action_tx.send(Action::SetValidatorPrefs(comission, blocked, *account_id))?;
Ok(()) Ok(())
} }
@ -543,6 +542,6 @@ pub async fn get_slashing_spans(
.await? .await?
.map(|spans| spans.prior.len()) .map(|spans| spans.prior.len())
.unwrap_or_default(); .unwrap_or_default();
action_tx.send(Action::SetSlashingSpansLength(slashing_spans_length))?; action_tx.send(Action::SetSlashingSpansLength(slashing_spans_length, *account_id))?;
Ok(()) Ok(())
} }

View File

@ -261,6 +261,9 @@ async fn inner_sign_and_submit_then_watch(
Ok(tx_progress) Ok(tx_progress)
}, },
Err(err) => { Err(err) => {
if let Some(ref mut nonce) = maybe_nonce {
**nonce = nonce.saturating_sub(1);
}
action_tx.send(Action::EventLog( action_tx.send(Action::EventLog(
format!("error during {tx_name} transaction: {err}"), format!("error during {tx_name} transaction: {err}"),
ActionLevel::Error, ActionLevel::Error,