diff --git a/examples/examples/dynamic_queries.rs b/examples/examples/dynamic_queries.rs index bc1683c6e9..08e67ea461 100644 --- a/examples/examples/dynamic_queries.rs +++ b/examples/examples/dynamic_queries.rs @@ -51,7 +51,7 @@ async fn main() -> Result<(), Box> { // 2. Dynamic constant access (the dynamic equivalent to the fetch_constants example). let constant_address = subxt::dynamic::constant("Balances", "ExistentialDeposit"); - let existential_deposit = api.constants().at(&constant_address)?; + let existential_deposit = api.constants().at(&constant_address)?.to_value()?; println!("Existential Deposit: {}", existential_deposit); // 3. Dynamic storage access @@ -67,7 +67,8 @@ async fn main() -> Result<(), Box> { let account = api .storage() .fetch_or_default(&storage_address, None) - .await?; + .await? + .to_value()?; println!("Bob's account details: {account}"); // 4. Dynamic storage iteration (the dynamic equivalent to the fetch_all_accounts example). @@ -75,7 +76,7 @@ async fn main() -> Result<(), Box> { let storage_address = subxt::dynamic::storage_root("System", "Account"); let mut iter = api.storage().iter(storage_address, 10, None).await?; while let Some((key, account)) = iter.next().await? { - println!("{}: {}", hex::encode(key), account); + println!("{}: {}", hex::encode(key), account.to_value()?); } Ok(()) diff --git a/subxt/src/constants/constant_address.rs b/subxt/src/constants/constant_address.rs index 545c25afc7..cb3cdbdfa7 100644 --- a/subxt/src/constants/constant_address.rs +++ b/subxt/src/constants/constant_address.rs @@ -3,7 +3,7 @@ // see LICENSE for license details. use crate::{ - dynamic::DecodedValue, + dynamic::DecodedValueThunk, metadata::DecodeWithMetadata, }; use std::borrow::Cow; @@ -97,7 +97,7 @@ pub fn dynamic<'a>( } impl<'a> ConstantAddress for DynamicConstantAddress<'a> { - type Target = DecodedValue; + type Target = DecodedValueThunk; fn pallet_name(&self) -> &str { &self.pallet_name diff --git a/subxt/src/dynamic.rs b/subxt/src/dynamic.rs index 29dd3dc262..db42ba7f41 100644 --- a/subxt/src/dynamic.rs +++ b/subxt/src/dynamic.rs @@ -5,6 +5,14 @@ //! This module provides the entry points to create dynamic //! transactions, storage and constant lookups. +use crate::{ + error::Error, + metadata::{ + DecodeWithMetadata, + Metadata, + }, +}; + pub use scale_value::Value; /// A [`scale_value::Value`] type endowed with contextual information @@ -24,3 +32,50 @@ pub use crate::storage::{ dynamic as storage, dynamic_root as storage_root, }; + +/// This is the result of making a dynamic request to a node. From this, +/// we can return the raw SCALE bytes that we were handed back, or we can +/// complete the decoding of the bytes into a [`DecodedValue`] type. +pub struct DecodedValueThunk { + type_id: u32, + metadata: Metadata, + scale_bytes: Vec, +} + +impl DecodeWithMetadata for DecodedValueThunk { + type Target = Self; + + fn decode_with_metadata( + bytes: &mut &[u8], + type_id: u32, + metadata: &Metadata, + ) -> Result { + let mut v = Vec::with_capacity(bytes.len()); + v.extend_from_slice(*bytes); + *bytes = &[]; + Ok(DecodedValueThunk { + type_id, + metadata: metadata.clone(), + scale_bytes: v, + }) + } +} + +impl DecodedValueThunk { + /// Return the SCALE encoded bytes handed back from the node. + pub fn to_encoded(self) -> Vec { + self.scale_bytes + } + /// Return the SCALE encoded bytes handed back from the node without taking ownership of them. + pub fn encoded(&self) -> &[u8] { + &self.scale_bytes + } + /// Decode the SCALE encoded storage entry into a dynamic [`DecodedValue`] type. + pub fn to_value(&self) -> Result { + DecodedValue::decode_with_metadata( + &mut &*self.scale_bytes, + self.type_id, + &self.metadata, + ) + } +} diff --git a/subxt/src/storage/storage_address.rs b/subxt/src/storage/storage_address.rs index 238b26f6e4..e0c80ba384 100644 --- a/subxt/src/storage/storage_address.rs +++ b/subxt/src/storage/storage_address.rs @@ -5,7 +5,7 @@ use super::storage_map_key::StorageMapKey; use crate::{ dynamic::{ - DecodedValue, + DecodedValueThunk, Value, }, error::{ @@ -194,7 +194,7 @@ impl<'a, Encodable> StorageAddress for DynamicStorageAddress<'a, Encodable> where Encodable: EncodeWithMetadata, { - type Target = DecodedValue; + type Target = DecodedValueThunk; // For dynamic types, we have no static guarantees about any of // this stuff, so we just allow it and let it fail at runtime: diff --git a/testing/integration-tests/src/frame/balances.rs b/testing/integration-tests/src/frame/balances.rs index 189c7e8946..40b20ac3c9 100644 --- a/testing/integration-tests/src/frame/balances.rs +++ b/testing/integration-tests/src/frame/balances.rs @@ -164,11 +164,35 @@ async fn tx_dynamic_transfer() -> Result<(), subxt::Error> { .fetch_or_default(&bob_account_addr, None) .await?; - let alice_pre_free = alice_pre.at("data").at("free").unwrap().as_u128().unwrap(); - let alice_post_free = alice_post.at("data").at("free").unwrap().as_u128().unwrap(); + let alice_pre_free = alice_pre + .to_value()? + .at("data") + .at("free") + .unwrap() + .as_u128() + .unwrap(); + let alice_post_free = alice_post + .to_value()? + .at("data") + .at("free") + .unwrap() + .as_u128() + .unwrap(); - let bob_pre_free = bob_pre.at("data").at("free").unwrap().as_u128().unwrap(); - let bob_post_free = bob_post.at("data").at("free").unwrap().as_u128().unwrap(); + let bob_pre_free = bob_pre + .to_value()? + .at("data") + .at("free") + .unwrap() + .as_u128() + .unwrap(); + let bob_post_free = bob_post + .to_value()? + .at("data") + .at("free") + .unwrap() + .as_u128() + .unwrap(); assert!(alice_pre_free - 10_000 >= alice_post_free); assert_eq!(bob_pre_free + 10_000, bob_post_free);