use crate::*; use sea_orm::DbErr; /// track progress of transaction on the network /// until it is in block with success or failure pub async fn track_progress( mut progress: TxProgress, ) -> Result<ExtrinsicEvents<Runtime>, subxt::Error> { loop { if let Some(status) = progress.next().await { match status? { TxStatus::Validated => { println!("transaction submitted to the network, waiting 6 seconds..."); } TxStatus::InBestBlock(in_block) => break in_block, TxStatus::Invalid { message } => { println!("Invalid {message}"); } _ => continue, } } } .wait_for_success() .await } /// generic extrinsic submitter pub async fn submit_call_and_look_event< E: std::fmt::Debug + StaticEvent + DisplayEvent, TxPayload: Payload, >( data: &Data, payload: &TxPayload, ) -> Result<(), subxt::Error> { // submit call let progress = submit_call(data, payload).await?; // if no wait, return immediately if data.args.no_wait { return Ok(()); } // collect events let events = track_progress(progress).await?; // print given event if there look_event::<E>(data, &events) } /// submit call pub async fn submit_call<TxPayload: Payload>( data: &Data, payload: &TxPayload, ) -> Result<TxProgress, subxt::Error> { // get account nonce manually to be based on last block and not last finalized let nonce = data .legacy_rpc_methods() // see issue #32 .await .system_account_next_index(&data.address()) .await?; // sign and submit match data.keypair().await { // sr25519 key pair KeyPair::Sr25519(keypair) => data.client().tx().create_signed_offline( payload, &PairSigner::<Runtime, sp_core::sr25519::Pair>::new(keypair), DefaultExtrinsicParamsBuilder::new().nonce(nonce).build(), ), KeyPair::Ed25519(keypair) => data.client().tx().create_signed_offline( payload, &PairSigner::<Runtime, sp_core::ed25519::Pair>::new(keypair), DefaultExtrinsicParamsBuilder::new().nonce(nonce).build(), ), }? .submit_and_watch() .await } /// look event pub fn look_event<E: std::fmt::Debug + StaticEvent + DisplayEvent>( data: &Data, events: &ExtrinsicEvents<Runtime>, ) -> Result<(), subxt::Error> { if let Some(e) = events.find_first::<E>()? { println!("{}", e.display(data)); } else { // print nothing, this could happen for // - new cert vs renew cert // - new smith cert and smith "promotion" // println!("(no event of type {})", std::any::type_name::<E>()) } Ok(()) } /// custom error type intended to provide more convenient error message to user #[derive(Debug)] pub enum GcliError { /// error coming from subxt Subxt(subxt::Error), /// error coming from duniter #[allow(dead_code)] Duniter(String), /// error coming from indexer #[allow(dead_code)] Indexer(String), /// error coming from database #[allow(dead_code)] DatabaseError(DbErr), /// logic error (illegal operation or security) #[allow(dead_code)] Logic(String), /// input error #[allow(dead_code)] Input(String), /// error coming from anyhow (to be removed) #[allow(dead_code)] Anyhow(anyhow::Error), /// error coming from io #[allow(dead_code)] IoError(std::io::Error), } impl std::fmt::Display for GcliError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { // prettier runtime error GcliError::Subxt(subxt::Error::Runtime(e)) => { write!(f, "{e}") } // debug log for detailed error _ => write!(f, "{:?}", self), } } } impl std::error::Error for GcliError {} impl From<subxt::Error> for GcliError { fn from(e: subxt::Error) -> GcliError { GcliError::Subxt(e) } } impl From<anyhow::Error> for GcliError { fn from(e: anyhow::Error) -> GcliError { GcliError::Anyhow(e) } } impl From<confy::ConfyError> for GcliError { fn from(e: confy::ConfyError) -> GcliError { GcliError::Anyhow(e.into()) } } impl From<std::io::Error> for GcliError { fn from(error: std::io::Error) -> Self { GcliError::IoError(error) } } impl From<sea_orm::DbErr> for GcliError { fn from(error: sea_orm::DbErr) -> Self { GcliError::DatabaseError(error) } }