-
Notifications
You must be signed in to change notification settings - Fork 258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expand the subscribe_and_watch
example
#361
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,17 @@ pub mod polkadot {} | |
async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
env_logger::init(); | ||
|
||
simple_transfer().await?; | ||
simple_transfer_separate_events().await?; | ||
handle_transfer_events().await?; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// This is the highest level approach to using this API. We use `wait_for_finalized_success` | ||
/// to wait for the transaction to make it into a finalized block, and also ensure that the | ||
/// transaction was successful according to the associated events. | ||
async fn simple_transfer() -> Result<(), Box<dyn std::error::Error>> { | ||
let signer = PairSigner::new(AccountKeyring::Alice.pair()); | ||
let dest = AccountKeyring::Bob.to_account_id().into(); | ||
|
||
|
@@ -62,3 +73,124 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { | |
} | ||
Ok(()) | ||
} | ||
|
||
/// This is very similar to `simple_transfer`, except to show that we can handle | ||
/// waiting for the transaction to be finalized separately from obtaining and checking | ||
/// for success on the events. | ||
async fn simple_transfer_separate_events() -> Result<(), Box<dyn std::error::Error>> { | ||
let signer = PairSigner::new(AccountKeyring::Alice.pair()); | ||
let dest = AccountKeyring::Bob.to_account_id().into(); | ||
|
||
let api = ClientBuilder::new() | ||
.build() | ||
.await? | ||
.to_runtime_api::<polkadot::RuntimeApi<polkadot::DefaultConfig>>(); | ||
|
||
let balance_transfer = api | ||
.tx() | ||
.balances() | ||
.transfer(dest, 10_000) | ||
.sign_and_submit_then_watch(&signer) | ||
.await? | ||
.wait_for_finalized() | ||
.await?; | ||
|
||
// Now we know it's been finalized, we can get hold of a couple of | ||
// details, including events. Calling `wait_for_finalized_success` is | ||
// equivalent to calling `wait_for_finalized` and then `wait_for_success`: | ||
let _events = balance_transfer.wait_for_success().await?; | ||
|
||
// Alternately, we could just `fetch_events`, which grabs all of the events like | ||
// the above, but does not check for success, and leaves it up to you: | ||
let events = balance_transfer.fetch_events().await?; | ||
|
||
let failed_event = | ||
events.find_first_event::<polkadot::system::events::ExtrinsicFailed>()?; | ||
|
||
if let Some(_ev) = failed_event { | ||
// We found a failed event; the transfer didn't succeed. | ||
println!("Balance transfer failed"); | ||
} else { | ||
// We didn't find a failed event; the transfer succeeded. Find | ||
// more details about it to report.. | ||
let transfer_event = | ||
events.find_first_event::<polkadot::balances::events::Transfer>()?; | ||
if let Some(event) = transfer_event { | ||
println!("Balance transfer success: value: {:?}", event.2); | ||
} else { | ||
println!("Failed to find Balances::Transfer Event"); | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
/// If we need more visibility into the state of the transaction, we can also ditch | ||
/// `wait_for_finalized` entirely and stream the transaction progress events, handling | ||
/// them more manually. | ||
async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> { | ||
let signer = PairSigner::new(AccountKeyring::Alice.pair()); | ||
let dest = AccountKeyring::Bob.to_account_id().into(); | ||
|
||
let api = ClientBuilder::new() | ||
.build() | ||
.await? | ||
.to_runtime_api::<polkadot::RuntimeApi<polkadot::DefaultConfig>>(); | ||
|
||
let mut balance_transfer_progress = api | ||
.tx() | ||
.balances() | ||
.transfer(dest, 10_000) | ||
.sign_and_submit_then_watch(&signer) | ||
.await?; | ||
|
||
while let Some(ev) = balance_transfer_progress.next().await? { | ||
use subxt::TransactionStatus::*; | ||
|
||
// Made it into a block, but not finalized. | ||
if let InBlock(details) = ev { | ||
println!( | ||
"Transaction {:?} made it into block {:?}", | ||
details.extrinsic_hash(), | ||
details.block_hash() | ||
); | ||
|
||
let events = details.wait_for_success().await?; | ||
let transfer_event = | ||
events.find_first_event::<polkadot::balances::events::Transfer>()?; | ||
|
||
if let Some(event) = transfer_event { | ||
println!( | ||
"Balance transfer is now in block (but not finalized): value: {:?}", | ||
event.2 | ||
); | ||
} else { | ||
println!("Failed to find Balances::Transfer Event"); | ||
} | ||
} | ||
// Finalized! | ||
else if let Finalized(details) = ev { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wasn't aware of this syntax but I prefer matching regardless :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aah, I kept flicking between a |
||
println!( | ||
"Transaction {:?} is finalized in block {:?}", | ||
details.extrinsic_hash(), | ||
details.block_hash() | ||
); | ||
|
||
let events = details.wait_for_success().await?; | ||
let transfer_event = | ||
events.find_first_event::<polkadot::balances::events::Transfer>()?; | ||
|
||
if let Some(event) = transfer_event { | ||
println!("Balance transfer success: value: {:?}", event.2); | ||
} else { | ||
println!("Failed to find Balances::Transfer Event"); | ||
} | ||
} | ||
// Report other statuses we see. | ||
else { | ||
println!("Current transaction status: {:?}", ev); | ||
} | ||
} | ||
|
||
Ok(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -414,15 +414,17 @@ impl<T: Config> TransactionEvents<T> { | |
&self.events | ||
} | ||
|
||
/// Find all of the events matching the event type provided as a generic parameter. | ||
/// Find all of the events matching the event type provided as a generic parameter. This | ||
/// will return an error if a matching event is found but cannot be properly decoded. | ||
pub fn find_events<E: crate::Event>(&self) -> Result<Vec<E>, Error> { | ||
self.events | ||
.iter() | ||
.filter_map(|e| e.as_event::<E>().map_err(Into::into).transpose()) | ||
.collect() | ||
} | ||
|
||
/// Find the first event that matches the event type provided as a generic parameter. | ||
/// Find the first event that matches the event type provided as a generic parameter. This | ||
/// will return an error if a matching event is found but cannot be properly decoded. | ||
/// | ||
/// Use [`TransactionEvents::find_events`], or iterate over [`TransactionEvents`] yourself | ||
/// if you'd like to handle multiple events of the same type. | ||
|
@@ -436,7 +438,7 @@ impl<T: Config> TransactionEvents<T> { | |
} | ||
|
||
/// Find an event. Returns true if it was found. | ||
pub fn has_event<E: crate::Event>(self) -> Result<bool, Error> { | ||
pub fn has_event<E: crate::Event>(&self) -> Result<bool, Error> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
Ok(self.find_first_event::<E>()?.is_some()) | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth mentioning how
find_first_event
could return error?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's worth an explicit shout out in the example really, but I added this note to the docs for the relevant methods because it wasn't clear enough there :)