extraction and parsing for balances, draft version

Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
Uncle Stretch 2024-12-05 20:07:26 +03:00
parent 63e35a4120
commit dc20ba936a
Signed by: str3tch
GPG Key ID: 84F3190747EE79AA
9 changed files with 166 additions and 40 deletions

View File

@ -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),

View File

@ -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())?;
}

View File

@ -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<Action>) -> Result<()> {
let _ = tx;
Ok(())
}
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
let _ = tx;
Ok(())

View File

@ -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<CasperConfig, Pair>,
}
pub struct Accounts {
is_active: bool,
wallet_keys_file: PathBuf,
action_tx: Option<UnboundedSender<Action>>,
network_tx: Option<Sender<Action>>,
wallet_keys_file: PathBuf,
palette: StylePalette,
scroll_state: ScrollbarState,
table_state: TableState,
wallet_keys: Vec<(String, String, String, PairSigner<CasperConfig, Pair>)>,
wallet_keys: Vec<AccountInfo>,
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::<CasperConfig, Pair>::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::<CasperConfig, Pair>::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::<CasperConfig, Pair>::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<Action>) -> Result<()> {
self.network_tx = Some(tx);
Ok(())
}
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> 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())

View File

@ -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<Action>) -> Result<()> {
for component in self.components.iter_mut() {
component.register_network_handler(tx.clone())?;
}
Ok(())
}
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
for component in self.components.iter_mut() {
component.register_action_handler(tx.clone())?;

View File

@ -27,6 +27,7 @@ pub struct Network {
rpc_client: RpcClient,
best_hash: Option<H256>,
finalized_hash: Option<H256>,
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(())
}
}

View File

@ -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<Action>,
// api: &OnlineClient<CasperConfig>,
// 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<Action>,
api: &OnlineClient<CasperConfig>,
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(())
}

10
src/types/account.rs Normal file
View File

@ -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,
}

View File

@ -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;