use color_eyre::Result; use subxt::{ ext::sp_core::{sr25519::Pair, Pair as PairT}, tx::{PairSigner, TxProgress}, OnlineClient, }; use tokio::sync::mpsc::UnboundedSender; use crate::{ action::Action, casper::{CasperConfig, CasperExtrinsicParamsBuilder}, casper_network::{self, runtime_types}, types::{ActionLevel, ActionTarget}, }; pub async fn transfer_balance( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], receiver: &[u8; 32], amount: &u128, maybe_nonce: Option<&mut u32>, ) -> Result>> { let receiver_id = subxt::utils::MultiAddress::Id(subxt::utils::AccountId32::from(*receiver)); let transfer_tx = casper_network::tx().balances().transfer_allow_death(receiver_id, *amount); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(transfer_tx), "transfer", ActionTarget::WalletLog, ).await } pub async fn bond_extra( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], amount: &u128, maybe_nonce: Option<&mut u32>, ) -> Result>> { let bond_extra_tx = casper_network::tx().staking().bond_extra(*amount); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(bond_extra_tx), "bond extra", ActionTarget::ValidatorLog, ).await } pub async fn bond( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], amount: &u128, maybe_nonce: Option<&mut u32>, ) -> Result>> { // auto-stake everything by now let reward_destination = casper_network::runtime_types::pallet_staking::RewardDestination::Staked; let bond_tx = casper_network::tx().staking().bond(*amount, reward_destination); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(bond_tx), "bond", ActionTarget::ValidatorLog, ).await } pub async fn payout_stakers( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], stash: &[u8; 32], era_index: u32, maybe_nonce: Option<&mut u32>, ) -> Result>> { let stash_id = subxt::utils::AccountId32::from(*stash); let payout_stakers_tx = casper_network::tx().staking().payout_stakers(stash_id, era_index); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(payout_stakers_tx), "payout stakers", ActionTarget::ValidatorLog, ).await } pub async fn set_keys( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], hashed_keys_str: &String, maybe_nonce: Option<&mut u32>, ) -> Result>> { let (gran_key, babe_key, audi_key, slow_key) = { let s = hashed_keys_str.trim_start_matches("0x"); ( hex::decode(&s[0..64]).unwrap().as_slice().try_into().unwrap(), hex::decode(&s[64..128]).unwrap().as_slice().try_into().unwrap(), hex::decode(&s[128..192]).unwrap().as_slice().try_into().unwrap(), hex::decode(&s[192..256]).unwrap().as_slice().try_into().unwrap(), ) }; let session_keys = runtime_types::casper_runtime::opaque::SessionKeys { grandpa: runtime_types::sp_consensus_grandpa::app::Public(gran_key), babe: runtime_types::sp_consensus_babe::app::Public(babe_key), authority_discovery: runtime_types::sp_authority_discovery::app::Public(audi_key), slow_clap: runtime_types::ghost_slow_clap::sr25519::app_sr25519::Public(slow_key), }; // it seems like there is no check for the second paramter, that's why // we it can be anything. For example empty vector. let set_keys_tx = casper_network::tx().session().set_keys(session_keys, Vec::new()); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(set_keys_tx), "set keys", ActionTarget::ValidatorLog, ).await } pub async fn validate( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], percent: u32, maybe_nonce: Option<&mut u32>, ) -> Result>> { let validator_prefs = casper_network::runtime_types::pallet_staking::ValidatorPrefs { commission: runtime_types::sp_arithmetic::per_things::Perbill(percent), blocked: percent >= 1_000_000_000u32, }; let validate_tx = casper_network::tx().staking().validate(validator_prefs); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(validate_tx), "validate", ActionTarget::ValidatorLog, ).await } pub async fn chill( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], maybe_nonce: Option<&mut u32>, ) -> Result>> { let chill_tx = casper_network::tx().staking().chill(); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(chill_tx), "chill", ActionTarget::ValidatorLog, ).await } pub async fn unbond( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], amount: &u128, maybe_nonce: Option<&mut u32>, ) -> Result>> { let unbond_tx = casper_network::tx().staking().unbond(*amount); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(unbond_tx), "unbond", ActionTarget::ValidatorLog, ).await } pub async fn rebond( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], amount: &u128, maybe_nonce: Option<&mut u32>, ) -> Result>> { let rebond_tx = casper_network::tx().staking().rebond(*amount); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(rebond_tx), "rebond", ActionTarget::ValidatorLog, ).await } pub async fn withdraw_unbonded( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], spans: &u32, maybe_nonce: Option<&mut u32>, ) -> Result>> { let withdraw_unbonded_tx = casper_network::tx().staking().withdraw_unbonded(*spans); inner_sign_and_submit_then_watch( action_tx, api, sender, maybe_nonce, Box::new(withdraw_unbonded_tx), "withdraw unbonded", ActionTarget::ValidatorLog, ).await } async fn inner_sign_and_submit_then_watch( action_tx: &UnboundedSender, api: &OnlineClient, sender: &[u8; 32], mut maybe_nonce: Option<&mut u32>, tx_call: Box, tx_name: &str, target: ActionTarget, ) -> Result>> { let signer = PairSigner::::new(Pair::from_seed(sender)); let tx_params = match maybe_nonce { Some(ref mut nonce) => { **nonce = nonce.saturating_add(1); CasperExtrinsicParamsBuilder::new() .nonce(nonce.saturating_sub(1) as u64) .build() }, None => CasperExtrinsicParamsBuilder::new().build(), }; match api .tx() .sign_and_submit_then_watch(&tx_call, &signer, tx_params) .await { Ok(tx_progress) => { action_tx.send(Action::EventLog( format!("{tx_name} transaction {} sent", tx_progress.extrinsic_hash()), ActionLevel::Info, target))?; Ok(tx_progress) }, Err(err) => { action_tx.send(Action::EventLog( format!("error during {tx_name} transaction: {err}"), ActionLevel::Error, target))?; Err(err.into()) } } }