diff --git a/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std b/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std index 1eea5bae..1de6eecf 160000 --- a/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std +++ b/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 +Subproject commit 1de6eecf821de7fe2c908cc48d3ab3dced20717f diff --git a/blueprints/incredible-squaring/contracts/lib/forge-std b/blueprints/incredible-squaring/contracts/lib/forge-std index 1eea5bae..1de6eecf 160000 --- a/blueprints/incredible-squaring/contracts/lib/forge-std +++ b/blueprints/incredible-squaring/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 +Subproject commit 1de6eecf821de7fe2c908cc48d3ab3dced20717f diff --git a/cli/src/deploy.rs b/cli/src/deploy.rs index a245b7fa..c52d1941 100644 --- a/cli/src/deploy.rs +++ b/cli/src/deploy.rs @@ -4,6 +4,8 @@ pub use alloy_signer_local::PrivateKeySigner; use color_eyre::eyre::{self, Context, ContextCompat, OptionExt, Result}; use gadget_blueprint_proc_macro_core::{BlueprintManager, ServiceBlueprint}; use gadget_sdk::clients::tangle::runtime::TangleConfig; +#[cfg(test)] +use gadget_sdk::tx::tangle::TxProgressExt; pub use k256; use std::fmt::Debug; use std::path::PathBuf; @@ -95,6 +97,9 @@ pub async fn deploy_to_tangle( .tx() .sign_and_submit_then_watch_default(&create_blueprint_tx, &signer) .await?; + #[cfg(test)] + let result = progress.wait_for_in_block_success().await?; + #[cfg(not(test))] let result = progress.wait_for_finalized_success().await?; let event = result .find::() diff --git a/sdk/src/tx/tangle.rs b/sdk/src/tx/tangle.rs index a211407b..183be479 100644 --- a/sdk/src/tx/tangle.rs +++ b/sdk/src/tx/tangle.rs @@ -1,5 +1,80 @@ +use subxt::{ + client::OnlineClientT, + error::TransactionError, + tx::{TxInBlock, TxStatus}, +}; + use crate::debug; +/// Extension trait for transaction progress handling. +/// +/// This trait provides additional methods for handling the progress of a transaction, +/// such as waiting for the transaction to be included in a block successfully. +/// +/// # Type Parameters +/// +/// - `T`: The configuration type for the Substrate runtime. +/// - `C`: The client type that implements the `OnlineClientT` trait. +#[async_trait::async_trait] +pub trait TxProgressExt { + /// Wait for the transaction to be in block, and return a [`TxInBlock`] + /// instance when it is, or an error if there was a problem waiting for finalization. + /// + /// **Note:** consumes `self`. If you'd like to perform multiple actions as the state of the + /// transaction progresses, use [`TxProgress::next()`] instead. + /// + /// **Note:** transaction statuses like `Invalid`/`Usurped`/`Dropped` indicate with some + /// probability that the transaction will not make it into a block but there is no guarantee + /// that this is true. In those cases the stream is closed however, so you currently have no way to find + /// out if they finally made it into a block or not. + async fn wait_for_in_block(mut self) -> Result, subxt::Error>; + + /// Wait for the transaction to be finalized, and for the transaction events to indicate + /// that the transaction was successful. Returns the events associated with the transaction, + /// as well as a couple of other details (block hash and extrinsic hash). + /// + /// **Note:** consumes self. If you'd like to perform multiple actions as progress is made, + /// use [`TxProgress::next()`] instead. + /// + /// **Note:** transaction statuses like `Invalid`/`Usurped`/`Dropped` indicate with some + /// probability that the transaction will not make it into a block but there is no guarantee + /// that this is true. In those cases the stream is closed however, so you currently have no way to find + /// out if they finally made it into a block or not. + async fn wait_for_in_block_success( + self, + ) -> Result, subxt::Error>; +} + +#[async_trait::async_trait] +impl> TxProgressExt for subxt::tx::TxProgress { + async fn wait_for_in_block(mut self) -> Result, subxt::Error> { + while let Some(status) = self.next().await { + match status? { + // In Block! Return. + TxStatus::InBestBlock(s) => return Ok(s), + // Error scenarios; return the error. + TxStatus::Error { message } => return Err(TransactionError::Error(message).into()), + TxStatus::Invalid { message } => { + return Err(TransactionError::Invalid(message).into()) + } + TxStatus::Dropped { message } => { + return Err(TransactionError::Dropped(message).into()) + } + // Ignore and wait for next status event: + _ => continue, + } + } + Err(subxt::error::RpcError::SubscriptionDropped.into()) + } + + async fn wait_for_in_block_success( + self, + ) -> Result, subxt::Error> { + let evs = self.wait_for_in_block().await?.wait_for_success().await?; + Ok(evs) + } +} + /// Send a transaction to the Tangle network. /// /// # Errors @@ -27,11 +102,27 @@ where .sign_and_submit_then_watch_default(xt, signer) .await?; - debug!("Waiting for finalized success ..."); - let result = progress.wait_for_finalized_success().await?; - debug!( - "Transaction with hash: {:?} has been finalized", - result.extrinsic_hash() - ); - Ok(result) + #[cfg(not(test))] + { + debug!("Waiting for finalized success ..."); + let result = progress.wait_for_finalized_success().await?; + debug!( + "Transaction with hash: {:?} has been finalized", + result.extrinsic_hash() + ); + Ok(result) + } + #[cfg(test)] + { + // In tests, we don't wait for the transaction to be finalized. + // This is because the test environment we will be using instant sealing. + // Instead, we just wait for the transaction to be included in a block. + debug!("Waiting for in block success ..."); + let result = progress.wait_for_in_block_success().await?; + debug!( + "Transaction with hash: {:?} has been included in a block", + result.extrinsic_hash() + ); + Ok(result) + } }