From dc20ba936a290af33f16645aa468e85543ee85b3 Mon Sep 17 00:00:00 2001 From: Uncle Stretch Date: Thu, 5 Dec 2024 20:07:26 +0300 Subject: [PATCH] extraction and parsing for balances, draft version Signed-off-by: Uncle Stretch --- src/action.rs | 5 +- src/app.rs | 3 + src/components/mod.rs | 6 ++ src/components/wallet/accounts.rs | 106 ++++++++++++++++++++++++------ src/components/wallet/mod.rs | 11 +++- src/network/mod.rs | 16 ++++- src/network/predefinded_calls.rs | 47 ++++++++----- src/types/account.rs | 10 +++ src/types/mod.rs | 2 + 9 files changed, 166 insertions(+), 40 deletions(-) create mode 100644 src/types/account.rs diff --git a/src/action.rs b/src/action.rs index 76be6c8..75cecbb 100644 --- a/src/action.rs +++ b/src/action.rs @@ -5,7 +5,7 @@ use subxt::utils::H256; use subxt::config::substrate::DigestItem; use crate::{ - types::{ActionLevel, EraInfo, CasperExtrinsicDetails}, + types::{SystemAccount, ActionLevel, EraInfo, CasperExtrinsicDetails}, }; use subxt::utils::AccountId32; @@ -30,6 +30,9 @@ pub enum Action { NewAccount(String), NewAddressBookRecord(String, String), + BalanceRequest([u8; 32], bool), + BalanceResponse([u8; 32], SystemAccount), + RenameAccount(String), RenameAddressBookRecord(String), UpdateAccountName(String), diff --git a/src/app.rs b/src/app.rs index 0622298..801d30d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -107,6 +107,9 @@ impl App { for component in self.components.iter_mut() { component.register_action_handler(self.action_tx.clone())?; } + for component in self.components.iter_mut() { + component.register_network_handler(self.network_tx.clone())?; + } for component in self.components.iter_mut() { component.register_config_handler(self.config.clone())?; } diff --git a/src/components/mod.rs b/src/components/mod.rs index 1438632..1af2709 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -5,6 +5,7 @@ use ratatui::{ Frame, }; use tokio::sync::mpsc::UnboundedSender; +use std::sync::mpsc::Sender; use crate::{palette, action::Action, config::Config, tui::Event}; @@ -17,6 +18,11 @@ pub mod wallet; pub mod empty; pub trait Component { + fn register_network_handler(&mut self, tx: Sender) -> Result<()> { + let _ = tx; + Ok(()) + } + fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { let _ = tx; Ok(()) diff --git a/src/components/wallet/accounts.rs b/src/components/wallet/accounts.rs index 9ab86b9..01cf7a8 100644 --- a/src/components/wallet/accounts.rs +++ b/src/components/wallet/accounts.rs @@ -23,24 +23,35 @@ use subxt::{ }, }; use tokio::sync::mpsc::UnboundedSender; +use std::sync::mpsc::Sender; use super::{PartialComponent, Component, CurrentTab}; use crate::casper::CasperConfig; -use crate::types::ActionLevel; +use crate::types::{SystemAccount, ActionLevel}; use crate::{ action::Action, config::Config, palette::StylePalette, }; +struct AccountInfo { + name: String, + address: String, + account_id: [u8; 32], + seed: String, + pair_signer: PairSigner, +} + pub struct Accounts { is_active: bool, - wallet_keys_file: PathBuf, action_tx: Option>, + network_tx: Option>, + wallet_keys_file: PathBuf, palette: StylePalette, scroll_state: ScrollbarState, table_state: TableState, - wallet_keys: Vec<(String, String, String, PairSigner)>, + wallet_keys: Vec, + balances: std::collections::HashMap<[u8; 32], SystemAccount>, } impl Default for Accounts { @@ -55,16 +66,25 @@ impl Accounts { is_active: false, wallet_keys_file: Default::default(), action_tx: None, + network_tx: None, scroll_state: ScrollbarState::new(0), table_state: TableState::new(), wallet_keys: Vec::new(), + balances: Default::default(), palette: StylePalette::default(), } } + fn send_balance_request(&mut self, account_id: [u8; 32], remove: bool) { + if let Some(action_tx) = &self.network_tx { + let _ = action_tx.send(Action::BalanceRequest(account_id, remove)); + } + } + fn create_new_account(&mut self, name: String) { let (pair, seed) = Pair::generate(); let secret_seed = hex::encode(seed); + let account_id = pair.public().0; let pair_signer = PairSigner::::new(pair); let address = AccountId32::from(seed.clone()) .to_ss58check_with_version(Ss58AddressFormat::custom(1996)); @@ -76,14 +96,21 @@ impl Accounts { )); } - self.wallet_keys.push((name, address, secret_seed, pair_signer)); + self.send_balance_request(account_id, false); + self.wallet_keys.push(AccountInfo { + name, + address, + account_id, + seed: secret_seed, + pair_signer, + }); self.last_row(); self.save_to_file(); } fn rename_account(&mut self, new_name: String) { if let Some(index) = self.table_state.selected() { - let old_name = self.wallet_keys[index].0.clone(); + let old_name = self.wallet_keys[index].name.clone(); if let Some(action_tx) = &self.action_tx { let _ = action_tx.send(Action::WalletLog( @@ -92,7 +119,7 @@ impl Accounts { )); } - self.wallet_keys[index].0 = new_name; + self.wallet_keys[index].name = new_name; self.save_to_file(); } @@ -102,7 +129,7 @@ impl Accounts { if let Some(index) = self.table_state.selected() { if let Some(action_tx) = &self.action_tx { let _ = action_tx.send(Action::RenameAccount( - self.wallet_keys[index].0.clone() + self.wallet_keys[index].name.clone() )); } } @@ -134,13 +161,14 @@ impl Accounts { if let Some(index) = self.table_state.selected() { if self.wallet_keys.len() > 1 { let wallet = self.wallet_keys.remove(index); + self.send_balance_request(wallet.account_id, true); self.previous_row(); self.save_to_file(); if let Some(action_tx) = &self.action_tx { let _ = action_tx.send(Action::WalletLog( format!("wallet `{}` with public address {} removed", - &wallet.0, &wallet.1), + &wallet.name, &wallet.address), ActionLevel::Warn, )); } @@ -151,7 +179,7 @@ impl Accounts { fn save_to_file(&mut self) { let mut file = File::create(&self.wallet_keys_file).unwrap(); for wallet in self.wallet_keys.iter() { - writeln!(file, "{}:0x{}", wallet.0, &wallet.2).unwrap(); + writeln!(file, "{}:0x{}", wallet.name, &wallet.seed).unwrap(); } } @@ -173,11 +201,19 @@ impl Accounts { .expect("stored seed is valid length; qed"); let pair = Pair::from_seed(&seed); - let address = AccountId32::from(pair.public().0) + let account_id = pair.public().0; + let address = AccountId32::from(account_id) .to_ss58check_with_version(Ss58AddressFormat::custom(1996)); let pair_signer = PairSigner::::new(pair); - self.wallet_keys.push((wallet_name.to_string(), address, wallet_key.to_string(), pair_signer)); + self.send_balance_request(account_id, false); + self.wallet_keys.push(AccountInfo { + name: wallet_name.to_string(), + account_id, + address, + seed: wallet_key.to_string(), + pair_signer, + }); } if let Some(action_tx) = &self.action_tx { let _ = action_tx.send(Action::WalletLog( @@ -220,6 +256,7 @@ impl Accounts { }; let secret_seed = hex::encode(seed); + let account_id = pair.public().0; let address = AccountId32::from(pair.public().0) .to_ss58check_with_version(Ss58AddressFormat::custom(1996)); let pair_signer = PairSigner::::new(pair); @@ -227,7 +264,14 @@ impl Accounts { let mut new_file = File::create(file_path)?; writeln!(new_file, "ghostie:0x{}", &secret_seed)?; - self.wallet_keys.push(("ghostie".to_string(), address, secret_seed, pair_signer)); + self.send_balance_request(account_id, false); + self.wallet_keys.push(AccountInfo { + name: "ghostie".to_string(), + address, + account_id, + seed: secret_seed, + pair_signer, + }); } }; self.table_state.select(Some(0)); @@ -238,8 +282,10 @@ impl Accounts { fn send_wallet_change(&mut self, index: usize) { if let Some(action_tx) = &self.action_tx { - let (_, _, _, pair) = &self.wallet_keys[index]; - let _ = action_tx.send(Action::UsedAccount(pair.account_id().clone())); + let _ = action_tx.send(Action::UsedAccount(self.wallet_keys[index] + .pair_signer + .account_id() + .clone())); } } @@ -287,6 +333,11 @@ impl Accounts { self.table_state.select(Some(i)); self.scroll_state = self.scroll_state.position(i); } + + fn prepare_u128(&self, value: u128, after: usize, ticker: Option<&str>) -> String { + let value = value as f64 / 10f64.powi(18); + format!("{:.after$}{}", value, ticker.unwrap_or_default()) + } } impl PartialComponent for Accounts { @@ -299,7 +350,11 @@ impl PartialComponent for Accounts { } impl Component for Accounts { - // TODO network_tx is needed here NOT action_tx + fn register_network_handler(&mut self, tx: Sender) -> Result<()> { + self.network_tx = Some(tx); + Ok(()) + } + fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { self.action_tx = Some(tx); Ok(()) @@ -328,6 +383,9 @@ impl Component for Accounts { match action { Action::NewAccount(name) => self.create_new_account(name), Action::UpdateAccountName(new_name) => self.rename_account(new_name), + Action::BalanceResponse(account_id, balance) => { + let _ = self.balances.insert(account_id, balance); + }, _ => {} }; Ok(None) @@ -355,15 +413,21 @@ impl Component for Accounts { let table = Table::new( self.wallet_keys .iter() - .map(|info| Row::new(vec![ - Cell::from(Text::from(info.0.clone()).alignment(Alignment::Left)), - Cell::from(Text::from(info.1.clone()).alignment(Alignment::Center)), - Cell::from(Text::from("31 CSPR".to_string()).alignment(Alignment::Right)), - ])), + .map(|info| { + let balance = self.balances + .get(&info.account_id) + .map(|b| b.free) + .unwrap_or_default(); + Row::new(vec![ + Cell::from(Text::from(info.name.clone()).alignment(Alignment::Left)), + Cell::from(Text::from(info.address.clone()).alignment(Alignment::Center)), + Cell::from(Text::from(self.prepare_u128(balance, 2, Some(" CSPR"))).alignment(Alignment::Right)), + ]) + }), [ Constraint::Min(5), Constraint::Max(51), - Constraint::Min(10), + Constraint::Min(11), ], ) .highlight_style(self.palette.create_highlight_style()) diff --git a/src/components/wallet/mod.rs b/src/components/wallet/mod.rs index c0bbb28..18da2d1 100644 --- a/src/components/wallet/mod.rs +++ b/src/components/wallet/mod.rs @@ -5,6 +5,9 @@ use ratatui::{ Frame, }; +use std::sync::mpsc::Sender; +use tokio::sync::mpsc::UnboundedSender; + mod balance; mod transfer; mod address_book; @@ -17,7 +20,6 @@ mod add_address_book_record; mod rename_address_book_record; use balance::Balance; -use tokio::sync::mpsc::UnboundedSender; use transfer::Transfer; use address_book::AddressBook; use add_account::AddAccount; @@ -94,6 +96,13 @@ impl Wallet { } impl Component for Wallet { + fn register_network_handler(&mut self, tx: Sender) -> Result<()> { + for component in self.components.iter_mut() { + component.register_network_handler(tx.clone())?; + } + Ok(()) + } + fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { for component in self.components.iter_mut() { component.register_action_handler(tx.clone())?; diff --git a/src/network/mod.rs b/src/network/mod.rs index fa0dfd9..ac9d4ce 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -27,6 +27,7 @@ pub struct Network { rpc_client: RpcClient, best_hash: Option, finalized_hash: Option, + accounts_to_watch: std::collections::HashSet<[u8; 32]> } impl Network { @@ -43,6 +44,7 @@ impl Network { rpc_client, best_hash: None, finalized_hash: None, + accounts_to_watch: Default::default(), } } @@ -50,6 +52,9 @@ impl Network { match io_event { Action::NewBestHash(hash) => { self.best_hash = Some(hash); + for account_id in self.accounts_to_watch.iter() { + predefinded_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await?; + } Ok(()) }, Action::NewFinalizedHash(hash) => { @@ -68,7 +73,16 @@ impl Network { Action::GetExistentialDeposit => predefinded_calls::get_existential_deposit(&self.action_tx, &self.online_client_api).await, Action::GetTotalIssuance => predefinded_calls::get_total_issuance(&self.action_tx, &self.online_client_api).await, - //Action::UsedAccount(account_id) => predefinded_calls::get_balance(&self.action_tx, &self.online_client_api, account_id).await, + + Action::BalanceRequest(account_id, remove) => { + if remove { + let _ = self.accounts_to_watch.remove(&account_id); + Ok(()) + } else { + let _ = self.accounts_to_watch.insert(account_id); + predefinded_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await + } + } _ => Ok(()) } } diff --git a/src/network/predefinded_calls.rs b/src/network/predefinded_calls.rs index 7171ec8..c18cb26 100644 --- a/src/network/predefinded_calls.rs +++ b/src/network/predefinded_calls.rs @@ -15,7 +15,7 @@ use crate::{ self, runtime_types::sp_consensus_slots, }, - types::EraInfo, + types::{SystemAccount, EraInfo}, CasperAccountId, CasperConfig }; @@ -148,18 +148,33 @@ pub async fn get_existential_deposit( } -//pub async fn get_balance( -// action_tx: &UnboundedSender, -// api: &OnlineClient, -// account_id: subxt::utils::AccountId32, -//) -> Result<()> { -// let storage_key = casper_network::storage().system().account(&account_id); -// let balance = api.storage() -// .at_latest() -// .await? -// .fetch(&storage_key) -// .await?; -// -// action_tx.send(Action::SetTotalIssuance(total_issuance))?; -// Ok(()) -//} +pub async fn get_balance( + action_tx: &UnboundedSender, + api: &OnlineClient, + account_id: &[u8; 32], +) -> Result<()> { + let account_id_converted = subxt::utils::AccountId32::from(*account_id); + let storage_key = casper_network::storage().system().account(account_id_converted); + + let maybe_balance = api + .storage() + .at_latest() + .await? + .fetch(&storage_key) + .await?; + + let balance = match maybe_balance { + Some(balance) => { + SystemAccount { + nonce: balance.nonce, + free: balance.data.free, + reserved: balance.data.reserved, + frozen: balance.data.frozen, + } + }, + None => SystemAccount::default(), + }; + + action_tx.send(Action::BalanceResponse(*account_id, balance))?; + Ok(()) +} diff --git a/src/types/account.rs b/src/types/account.rs new file mode 100644 index 0000000..0b5fcd1 --- /dev/null +++ b/src/types/account.rs @@ -0,0 +1,10 @@ +use codec::Decode; +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)] +pub struct SystemAccount { + pub nonce: u32, + pub free: u128, + pub reserved: u128, + pub frozen: u128, +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 24b17a0..3d562d9 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,7 +1,9 @@ mod era; mod extrinsics; mod log; +mod account; pub use extrinsics::CasperExtrinsicDetails; pub use era::EraInfo; pub use log::ActionLevel; +pub use account::SystemAccount;