Skip to content

Commit 6852bf5

Browse files
authored
Add Runtime API and dynamic examples, and util methods to StorageKeyPart (#2161)
* Add Runtime API and dynamic examples, and util methods to StorageKey * clippy
1 parent d123c3a commit 6852bf5

5 files changed

Lines changed: 198 additions & 8 deletions

File tree

subxt/examples/dynamic.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//! We can use dyanmic addresses/payloads to dynamically decode/encode things.
2+
//! Prefer to avoid scale_value::Value and decode into well defined types where
3+
//! possible.
4+
use scale_decode::DecodeAsType;
5+
use subxt::dynamic::{self, Value};
6+
use subxt::{Error, OnlineClient, PolkadotConfig};
7+
8+
#[tokio::main]
9+
#[allow(dead_code)]
10+
async fn main() -> Result<(), Error> {
11+
// Create a new API client, configured to talk to Polkadot nodes.
12+
let api = OnlineClient::<PolkadotConfig>::from_url("wss://rpc.polkadot.io").await?;
13+
let api_at_block = api.at_block(20_000_000u32).await?;
14+
15+
//// Storage: TimeStamp.Now
16+
{
17+
// Here we create a storage address with no keys `()` and a `u64` value type.
18+
let timestamp_addr = dynamic::storage::<(), u64>("Timestamp", "Now");
19+
let timestamp_value = api_at_block.storage().fetch(timestamp_addr, ()).await?;
20+
21+
// .decode() decodes as the default value type:
22+
let timestamp_u64 = timestamp_value.decode()?;
23+
println!("Block timestamp u64: {timestamp_u64}");
24+
25+
// Despite the default u64 value type we can decode as something else if we prefer:
26+
let timestamp_val = timestamp_value.decode_as::<Value>()?;
27+
println!("Block timestamp Value: {timestamp_val}");
28+
}
29+
30+
//// Storage: System.Account
31+
{
32+
// Let's define a type for our account details to decode into:
33+
#[derive(Debug, DecodeAsType)]
34+
struct AccountInfo {
35+
data: AccountInfoData,
36+
}
37+
#[derive(Debug, DecodeAsType)]
38+
struct AccountInfoData {
39+
// We only have to add the fields we're interested in:
40+
free: u128,
41+
}
42+
43+
// Now, we set the expected value to the above type, and require one key
44+
// to be provided; a [u8;32] which corresponds to an AccountId32.
45+
let account_info_addr = dynamic::storage::<([u8; 32],), AccountInfo>("System", "Account");
46+
47+
// Fetch this entry for some random AccountId:
48+
let account_id = demo_account_id();
49+
let account_value = api_at_block
50+
.storage()
51+
.fetch(account_info_addr, (account_id,))
52+
.await?;
53+
54+
let account_details = account_value.decode()?;
55+
println!("Account details: {account_details:?}");
56+
}
57+
58+
//// Constants
59+
{
60+
// Define the expected return type when creating the address:
61+
let deposit_addr = dynamic::constant::<u128>("Balances", "ExistentialDeposit");
62+
let deposit = api_at_block.constants().entry(deposit_addr)?;
63+
println!("Existential Deposit amount: {deposit}");
64+
65+
// As with other dynamic address types, you can use a "catch-all" type
66+
// like scale_value::Value if you don't know what the type is, but this is
67+
// less performant so should be avoided if the flexibility isn't needed:
68+
let block_weights_addr = dynamic::constant::<Value>("System", "BlockWeights");
69+
let block_weights = api_at_block.constants().entry(block_weights_addr)?;
70+
71+
// Using the Value type makes sense if you just want to print it out (and can be used
72+
// to then help you define a proper struct to decode a type into):
73+
println!("Block weights: {block_weights}");
74+
75+
// Like this...
76+
#[derive(Debug, DecodeAsType)]
77+
struct BlockWeights {
78+
// Again; you can ignore all fields you don't care about;
79+
// DecodeAsType understands how to skip them for you.
80+
max_block: BlockWeightInfo,
81+
}
82+
#[derive(Debug, DecodeAsType)]
83+
struct BlockWeightInfo {
84+
ref_time: u128,
85+
proof_size: u128,
86+
}
87+
let block_weights_addr = dynamic::constant::<BlockWeights>("System", "BlockWeights");
88+
let block_weights = api_at_block.constants().entry(block_weights_addr)?;
89+
println!("Max total block weights: {:?}", block_weights.max_block);
90+
}
91+
92+
//// Runtime APIs
93+
{
94+
// Define the arguments for this payload and the return type. Here we accept an
95+
// AccountId like type (32 bytes) and the return is a u32. Since we provide the
96+
// arguments in the payload, we can often omit the argument types.
97+
let runtime_api_payload = dynamic::runtime_api_call::<_, u32>(
98+
"AccountNonceApi",
99+
"account_nonce",
100+
(demo_account_id(),),
101+
);
102+
103+
let account_nonce = api_at_block
104+
.runtime_apis()
105+
.call(runtime_api_payload)
106+
.await?;
107+
println!("Account nonce for demo account: {account_nonce}");
108+
}
109+
110+
//// Transactions
111+
{
112+
use subxt_signer::sr25519::dev;
113+
let alice = dev::alice();
114+
let bob = dev::bob();
115+
116+
// As with Runtime APIs, to submit a transaction you define the payload. There
117+
// is no response though; just the input values you wish to provide; here something
118+
// which looks like a `MultiAddress::Id([u8; 32])` and a value to transfer.
119+
let tx_payload = dynamic::transaction(
120+
"Balances",
121+
"transfer_keep_alive",
122+
(alice.public_key().to_address::<()>(), 12_000_000u128),
123+
);
124+
125+
// This won't work when pointed at a real node since it uses a dev account:
126+
api_at_block
127+
.transactions()
128+
.sign_and_submit_then_watch_default(&tx_payload, &bob)
129+
.await?
130+
.wait_for_finalized_success()
131+
.await?;
132+
}
133+
134+
Ok(())
135+
}
136+
137+
fn demo_account_id() -> [u8; 32] {
138+
let account_id_hex = "9a4d0faa2ba8c3cc5711852960940793acf55bf195b6eecf88fa78e961d0ce4a";
139+
let account_id: [u8; 32] = hex::decode(account_id_hex).unwrap().try_into().unwrap();
140+
account_id
141+
}

subxt/examples/runtime_apis.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//! Calling Runtime APIs
2+
use subxt::{Error, OnlineClient, PolkadotConfig};
3+
use subxt_signer::sr25519::dev;
4+
5+
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")]
6+
mod polkadot {}
7+
8+
#[tokio::main]
9+
async fn main() -> Result<(), Error> {
10+
let api = OnlineClient::<PolkadotConfig>::new().await?;
11+
let at_block = api.at_current_block().await?;
12+
13+
// Runtime version:
14+
let version_payload = polkadot::runtime_apis().core().version();
15+
let version = at_block.runtime_apis().call(version_payload).await?;
16+
println!("Version: {version:?}");
17+
18+
// Account nonce for some account:
19+
let account_nonce_payload = polkadot::runtime_apis()
20+
.account_nonce_api()
21+
.account_nonce(dev::alice().public_key().to_account_id());
22+
let account_nonce = at_block.runtime_apis().call(account_nonce_payload).await?;
23+
println!("Account nonce: {account_nonce}");
24+
25+
Ok(())
26+
}

subxt/src/storage.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::borrow::Cow;
1919

2020
pub use address::{Address, DynamicAddress, StaticAddress, dynamic};
2121
pub use prefix_of::PrefixOf;
22-
pub use storage_entry::StorageEntry;
22+
pub use storage_entry::{StorageEntries, StorageEntry};
2323
pub use storage_key::{StorageKey, StorageKeyPart};
2424
pub use storage_key_value::StorageKeyValue;
2525
pub use storage_value::StorageValue;
@@ -123,11 +123,7 @@ impl<'atblock, T: Config, Client: OnlineClientAtBlockT<T>> StorageClient<'atbloc
123123
&self,
124124
addr: Addr,
125125
key_parts: KeyParts,
126-
) -> Result<
127-
impl futures::Stream<Item = Result<StorageKeyValue<'atblock, Addr>, StorageError>>
128-
+ use<'atblock, Addr, Client, T, KeyParts>,
129-
StorageError,
130-
> {
126+
) -> Result<StorageEntries<'atblock, Addr>, StorageError> {
131127
let entry = self.entry(addr)?;
132128
entry.iter(key_parts).await
133129
}

subxt/src/storage/storage_key.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub struct StorageKeyPart<'info> {
9494
}
9595

9696
impl<'info> StorageKeyPart<'info> {
97-
/// Get the raw bytes for this part of the storage key.
97+
/// Get all of the raw bytes for this part of the storage key.
9898
pub fn bytes(&self) -> &[u8] {
9999
let part = &self.info[self.index];
100100
let hash_range = part.hash_range();
@@ -109,6 +109,34 @@ impl<'info> StorageKeyPart<'info> {
109109
&self.bytes[combined_range]
110110
}
111111

112+
/// Get the bytes corresponding to the hash part of the key.
113+
///
114+
/// - For `Blake2_128Concat` and `Twox64Concat` hashers, this will return only the
115+
/// BlakeTwo128 or Twox64 hash and _not_ the value that's been concatenated onto it.
116+
/// - For the `Identity` hasher, no bytes will be returned (since it contains only the
117+
/// encoded value and no hash)
118+
/// - For other hashers, all of the bytes will be returned since they all correspond to
119+
/// the hash and there is no additional value concatenated.
120+
pub fn hash_bytes(&self) -> &[u8] {
121+
let part = &self.info[self.index];
122+
let hash_range = part.hash_range();
123+
&self.bytes[hash_range]
124+
}
125+
126+
/// Get the bytes corresponding to the value part of the key.
127+
///
128+
/// - For `Blake2_128Concat` and `Twox64Concat` hashers, this will return only the
129+
/// bytes corresponding to the value that is concatenated after the hash.
130+
/// - For the `Identity` hasher, all of the bytes are returned since they all
131+
/// correspond to the value.
132+
/// - For other hashers, no bytes will be returned since there is no value concatenated.
133+
pub fn value_bytes(&self) -> &[u8] {
134+
let part = &self.info[self.index];
135+
part.value()
136+
.map(|v| &self.bytes[v.range()])
137+
.unwrap_or_default()
138+
}
139+
112140
/// Get the hasher that was used to construct this part of the storage key.
113141
pub fn hasher(&self) -> StorageHasher {
114142
self.info[self.index].hasher()

testing/integration-tests/src/full_client/frame/contracts.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use crate::{
1212
},
1313
subxt_test, test_context,
1414
};
15-
use subxt::ext::futures::StreamExt;
1615
use subxt::{
1716
Error,
1817
client::OnlineClientAtBlockImpl,

0 commit comments

Comments
 (0)