use super::Inner;
use crate::{
AsGear, Error, TxInBlock, TxStatus,
backtrace::BacktraceStatus,
config::GearConfig,
gear::{
self,
runtime_types::vara_runtime::{RuntimeCall, RuntimeEvent},
sudo,
},
result::{Result, TxStatusExt, TxSuccess},
};
use colored::Colorize;
use subxt::{
OnlineClient,
blocks::ExtrinsicEvents,
config::polkadot::PolkadotExtrinsicParamsBuilder,
tx::{Payload, TxProgress as SubxtTxProgress},
utils::H256,
};
type TxProgress = SubxtTxProgress<GearConfig, OnlineClient<GearConfig>>;
pub type EventsResult = Result<(H256, ExtrinsicEvents<GearConfig>)>;
impl Inner {
pub async fn log_balance_spent(&self, before: u128) -> Result<()> {
match self.rpc().get_balance().await {
Ok(balance) => {
let after = before.saturating_sub(balance);
log::info!("\tBalance spent: {after}");
}
Err(e) => log::info!("\tAccount was removed from storage: {e}"),
}
Ok(())
}
pub(crate) fn log_status(status: &TxStatus) {
match status {
TxStatus::Validated => log::info!("\tStatus: Validated"),
TxStatus::Broadcasted => log::info!("\tStatus: Broadcast"),
TxStatus::NoLongerInBestBlock => log::info!("\tStatus: NoLongerInBestBlock"),
TxStatus::InBestBlock(b) => log::info!(
"\tStatus: InBestBlock( block hash: {}, extrinsic hash: {} )",
b.block_hash(),
b.extrinsic_hash()
),
TxStatus::InFinalizedBlock(b) => log::info!(
"\tStatus: Finalized( block hash: {}, extrinsic hash: {} )",
b.block_hash(),
b.extrinsic_hash()
),
TxStatus::Error { message: e } => log::error!("\tStatus: Error( {e:?} )"),
TxStatus::Dropped { message: e } => log::error!("\tStatus: Dropped( {e:?} )"),
TxStatus::Invalid { message: e } => log::error!("\tStatus: Invalid( {e:?} )"),
}
}
pub async fn process<Call: Payload>(&self, call: Call) -> Result<TxInBlock> {
let before = self.rpc().get_balance().await?;
let mut process = self.sign_and_submit_then_watch(&call).await?;
let extrinsic = if let Some(details) = call.validation_details() {
let (pallet, name) = (details.pallet_name, details.call_name);
format!("{pallet}::{name}")
} else {
"an unknown extrinsic".to_owned()
}
.magenta()
.bold();
log::info!("Pending {extrinsic} ...");
let mut queue: Vec<BacktraceStatus> = Default::default();
let mut hash: Option<H256> = None;
while let Some(status) = process.next().await {
let status = status?;
Self::log_status(&status);
if let Some(h) = &hash {
self.backtrace
.clone()
.append(*h, BacktraceStatus::from(&status));
} else {
queue.push((&status).into());
}
match status.into_result() {
Ok(success) => match success {
TxSuccess::Validated
| TxSuccess::Broadcasted
| TxSuccess::NoLongerInBestBlock => (),
TxSuccess::InBestBlock(b) => {
hash = Some(b.extrinsic_hash());
self.backtrace.append(
b.extrinsic_hash(),
BacktraceStatus::InBestBlock {
block_hash: b.block_hash(),
extrinsic_hash: b.extrinsic_hash(),
},
);
}
TxSuccess::InFinalizedBlock(b) => {
log::info!("Submitted {extrinsic} !");
log::info!("\tBlock Hash: {:?}", b.block_hash());
log::info!("\tTransaction Hash: {:?}", b.extrinsic_hash());
self.log_balance_spent(before).await?;
return Ok(b);
}
},
Err(err) => {
self.log_balance_spent(before).await?;
return Err(err.into());
}
}
}
Err(Error::SubscriptionDied)
}
pub async fn process_sudo<Call: Payload>(&self, call: Call) -> EventsResult {
let tx = self.process(call).await?;
let events = tx.wait_for_success().await?;
for event in events.iter() {
let event = event?.as_gear()?;
if let RuntimeEvent::Sudo(sudo::Event::Sudid {
sudo_result: Err(err),
}) = event
{
return Err(self.api().decode_error(err).into());
}
}
Ok((tx.block_hash(), events))
}
pub async fn run_tx<Call: Payload>(&self, call: Call) -> Result<TxInBlock> {
self.process(call).await
}
pub async fn sudo_run_tx<Call: Payload>(&self, call: Call) -> EventsResult {
self.process_sudo(call).await
}
pub async fn sudo(&self, call: RuntimeCall) -> EventsResult {
self.sudo_run_tx(gear::tx().sudo().sudo(call)).await
}
async fn sign_and_submit_then_watch<Call: Payload>(
&self,
call: &Call,
) -> Result<TxProgress, subxt::Error> {
if let Some(nonce) = self.nonce {
self.api
.tx()
.create_signed(
call,
&self.signer,
PolkadotExtrinsicParamsBuilder::new().nonce(nonce).build(),
)
.await?
.submit_and_watch()
.await
} else {
self.api
.tx()
.sign_and_submit_then_watch_default(call, &self.signer)
.await
}
}
}