Skip to content

Commit b1b7173

Browse files
authored
New Event Subscription API (paritytech#442)
* Add reworked event types * first pass implementing event subscriptions * make clear that some methods are private * comment tidy * use Events in transaction stuff * align transaction and event APIs * remove __private_ prefixes; they are ugly * fix examples and remove old events and subscription code * better comments on hidden event functions * re-add find_first_event; it's used a bunch in tests and examples * cargo check --all-targets now passes * Fix up existing event tests * cargo fmt * change todo to note * clippy and doc niggles * revert to find_first_event * Add specific subscription related tests * cargo fmt * Update tests and add/fix examples * cargo fmt * add a little to subscribe_all_events example * cargo fmt * move an example comment * easy access to root mod for more clarity * add a couple of tests to ensure that events properly decoded until naff bytes * Simplify EventSubscription Stream impl a little * Address some PR feedback
1 parent 7615b25 commit b1b7173

27 files changed

+11598
-5287
lines changed

codegen/src/api/calls.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub fn generate_calls(
9090
pub fn #fn_name(
9191
&self,
9292
#( #call_fn_args, )*
93-
) -> ::subxt::SubmittableExtrinsic<'a, T, X, A, #call_struct_name, DispatchError> {
93+
) -> ::subxt::SubmittableExtrinsic<'a, T, X, A, #call_struct_name, DispatchError, root_mod::Event> {
9494
let call = #call_struct_name { #( #call_args, )* };
9595
::subxt::SubmittableExtrinsic::new(self.client, call)
9696
}
@@ -101,6 +101,7 @@ pub fn generate_calls(
101101

102102
quote! {
103103
pub mod calls {
104+
use super::root_mod;
104105
use super::#types_mod_ident;
105106

106107
type DispatchError = #types_mod_ident::sp_runtime::DispatchError;

codegen/src/api/mod.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ impl RuntimeGenerator {
209209

210210
quote! {
211211
pub mod #mod_name {
212+
use super::root_mod;
212213
use super::#types_mod_ident;
213214
#calls
214215
#event
@@ -275,6 +276,9 @@ impl RuntimeGenerator {
275276
quote! {
276277
#[allow(dead_code, unused_imports, non_camel_case_types)]
277278
pub mod #mod_ident {
279+
// Make it easy to access the root via `root_mod` at different levels:
280+
use super::#mod_ident as root_mod;
281+
278282
#outer_event
279283
#( #modules )*
280284
#types_mod
@@ -323,12 +327,33 @@ impl RuntimeGenerator {
323327
pub fn tx(&'a self) -> TransactionApi<'a, T, X, A> {
324328
TransactionApi { client: &self.client, marker: ::core::marker::PhantomData }
325329
}
330+
331+
pub fn events(&'a self) -> EventsApi<'a, T> {
332+
EventsApi { client: &self.client }
333+
}
334+
}
335+
336+
pub struct EventsApi<'a, T: ::subxt::Config> {
337+
client: &'a ::subxt::Client<T>,
338+
}
339+
340+
impl <'a, T: ::subxt::Config> EventsApi<'a, T> {
341+
pub async fn at(&self, block_hash: T::Hash) -> Result<::subxt::events::Events<'a, T, Event>, ::subxt::BasicError> {
342+
::subxt::events::at::<T, Event>(self.client, block_hash).await
343+
}
344+
345+
pub async fn subscribe(&self) -> Result<::subxt::events::EventSubscription<'a, T, Event>, ::subxt::BasicError> {
346+
::subxt::events::subscribe::<T, Event>(self.client).await
347+
}
348+
349+
pub async fn subscribe_finalized(&self) -> Result<::subxt::events::EventSubscription<'a, T, Event>, ::subxt::BasicError> {
350+
::subxt::events::subscribe_finalized::<T, Event>(self.client).await
351+
}
326352
}
327353

328354
pub struct ConstantsApi;
329355

330-
impl ConstantsApi
331-
{
356+
impl ConstantsApi {
332357
#(
333358
pub fn #pallets_with_constants(&self) -> #pallets_with_constants::constants::ConstantsApi {
334359
#pallets_with_constants::constants::ConstantsApi

examples/examples/polkadot_balance_transfer.rs renamed to examples/examples/balance_transfer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
1616

17-
//! To run this example, a local polkadot node should be running.
17+
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
1818
//!
1919
//! E.g.
2020
//! ```bash
21-
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location
21+
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
2222
//! polkadot --dev --tmp
2323
//! ```
2424

examples/examples/custom_config.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
1616

17+
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
18+
//!
19+
//! E.g.
20+
//! ```bash
21+
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
22+
//! polkadot --dev --tmp
23+
//! ```
24+
1725
use sp_keyring::AccountKeyring;
1826
use subxt::{
1927
ClientBuilder,

examples/examples/custom_type_derives.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
1616

17+
//! Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
18+
1719
#![allow(clippy::redundant_clone)]
1820

1921
#[subxt::subxt(
2022
runtime_metadata_path = "examples/polkadot_metadata.scale",
2123
// We can add (certain) custom derives to the generated types by providing
22-
// a comma separated list to the below attribute. Most useful for adding `Clone`:
23-
generated_type_derives = "Clone, Hash"
24+
// a comma separated list to the below attribute. Most useful for adding `Clone`.
25+
// The derives that we can add ultiamtely is limited to the traits that the base
26+
// types relied upon by the codegen implement.
27+
generated_type_derives = "Clone, PartialEq"
2428
)]
2529
pub mod polkadot {}
2630

examples/examples/fetch_all_accounts.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
1616

17-
//! To run this example, a local polkadot node should be running.
17+
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
1818
//!
1919
//! E.g.
2020
//! ```bash
21-
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location
21+
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
2222
//! polkadot --dev --tmp
2323
//! ```
2424
44.4 KB
Binary file not shown.

examples/examples/fetch_remote.rs renamed to examples/examples/rpc_call.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
1616

17+
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
18+
//!
19+
//! E.g.
20+
//! ```bash
21+
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
22+
//! polkadot --dev --tmp
23+
//! ```
24+
1725
use subxt::{
1826
ClientBuilder,
1927
DefaultConfig,
@@ -42,9 +50,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4250
.await?;
4351

4452
if let Some(hash) = block_hash {
45-
println!("Block hash for block number {}: {}", block_number, hash);
53+
println!("Block hash for block number {block_number}: {hash}");
4654
} else {
47-
println!("Block number {} not found.", block_number);
55+
println!("Block number {block_number} not found.");
4856
}
4957

5058
Ok(())

examples/examples/submit_and_watch.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
1616

17-
//! To run this example, a local polkadot node should be running.
17+
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
1818
//!
1919
//! E.g.
2020
//! ```bash
21-
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location
21+
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
2222
//! polkadot --dev --tmp
2323
//! ```
2424
@@ -70,7 +70,7 @@ async fn simple_transfer() -> Result<(), Box<dyn std::error::Error>> {
7070
balance_transfer.find_first_event::<polkadot::balances::events::Transfer>()?;
7171

7272
if let Some(event) = transfer_event {
73-
println!("Balance transfer success: value: {:?}", event.2);
73+
println!("Balance transfer success: {event:?}");
7474
} else {
7575
println!("Failed to find Balances::Transfer Event");
7676
}
@@ -119,7 +119,7 @@ async fn simple_transfer_separate_events() -> Result<(), Box<dyn std::error::Err
119119
let transfer_event =
120120
events.find_first_event::<polkadot::balances::events::Transfer>()?;
121121
if let Some(event) = transfer_event {
122-
println!("Balance transfer success: value: {:?}", event.2);
122+
println!("Balance transfer success: {event:?}");
123123
} else {
124124
println!("Failed to find Balances::Transfer Event");
125125
}
@@ -165,8 +165,7 @@ async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> {
165165

166166
if let Some(event) = transfer_event {
167167
println!(
168-
"Balance transfer is now in block (but not finalized): value: {:?}",
169-
event.2
168+
"Balance transfer is now in block (but not finalized): {event:?}"
170169
);
171170
} else {
172171
println!("Failed to find Balances::Transfer Event");
@@ -185,7 +184,7 @@ async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> {
185184
events.find_first_event::<polkadot::balances::events::Transfer>()?;
186185

187186
if let Some(event) = transfer_event {
188-
println!("Balance transfer success: value: {:?}", event.2);
187+
println!("Balance transfer success: {event:?}");
189188
} else {
190189
println!("Failed to find Balances::Transfer Event");
191190
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
2+
// This file is part of subxt.
3+
//
4+
// subxt is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// subxt is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
16+
17+
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
18+
//!
19+
//! E.g.
20+
//! ```bash
21+
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
22+
//! polkadot --dev --tmp
23+
//! ```
24+
25+
use futures::StreamExt;
26+
use sp_keyring::AccountKeyring;
27+
use std::time::Duration;
28+
use subxt::{
29+
ClientBuilder,
30+
DefaultConfig,
31+
DefaultExtra,
32+
PairSigner,
33+
};
34+
35+
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
36+
pub mod polkadot {}
37+
38+
/// Subscribe to all events, and then manually look through them and
39+
/// pluck out the events that we care about.
40+
#[async_std::main]
41+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
42+
env_logger::init();
43+
44+
let api = ClientBuilder::new()
45+
.build()
46+
.await?
47+
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
48+
49+
// Subscribe to any events that occur:
50+
let mut event_sub = api.events().subscribe().await?;
51+
52+
// While this subscription is active, balance transfers are made somewhere:
53+
async_std::task::spawn(async {
54+
let signer = PairSigner::new(AccountKeyring::Alice.pair());
55+
let api = ClientBuilder::new()
56+
.build()
57+
.await
58+
.unwrap()
59+
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
60+
61+
let mut transfer_amount = 1_000_000_000;
62+
63+
// Make small balance transfers from Alice to Bob in a loop:
64+
loop {
65+
api.tx()
66+
.balances()
67+
.transfer(AccountKeyring::Bob.to_account_id().into(), transfer_amount)
68+
.sign_and_submit(&signer)
69+
.await
70+
.unwrap();
71+
72+
async_std::task::sleep(Duration::from_secs(10)).await;
73+
transfer_amount += 100_000_000;
74+
}
75+
});
76+
77+
// Our subscription will see the events emitted as a result of this:
78+
while let Some(events) = event_sub.next().await {
79+
let events = events?;
80+
let block_hash = events.block_hash();
81+
82+
// We can iterate, statically decoding all events if we want:
83+
println!("All events in block {block_hash:?}:");
84+
println!(" Static event details:");
85+
for event in events.iter() {
86+
let event = event?;
87+
println!(" {event:?}");
88+
}
89+
90+
// Or we can dynamically decode events:
91+
println!(" Dynamic event details: {block_hash:?}:");
92+
for event in events.iter_raw() {
93+
let event = event?;
94+
let is_balance_transfer = event
95+
.as_event::<polkadot::balances::events::Transfer>()?
96+
.is_some();
97+
let pallet = event.pallet;
98+
let variant = event.variant;
99+
println!(
100+
" {pallet}::{variant} (is balance transfer? {is_balance_transfer})"
101+
);
102+
}
103+
104+
// Or we can dynamically find the first transfer event, ignoring any others:
105+
let transfer_event =
106+
events.find_first_event::<polkadot::balances::events::Transfer>()?;
107+
108+
if let Some(ev) = transfer_event {
109+
println!(" - Balance transfer success: value: {:?}", ev.amount);
110+
} else {
111+
println!(" - No balance transfer event found in this block");
112+
}
113+
}
114+
115+
Ok(())
116+
}

0 commit comments

Comments
 (0)