Merge v0.50.x to master (#2127)

* v0.50.0: Integrate frame-decode, redo storage APIs and break up Error. (#2100)

* WIP integrating new frame-decode and working out new storage APIS

* WIP: first pass adding new storage things to subxt-core

* Second pass over Address type and start impl in Subxt

* WIP new storage APIs

* WIP New storage APIs roughly completed, lots of errors still

* Remove PlainorMap enum; plain and map values now use same struct to simplify usage

* Begin 'fixing' errors

* WIP splitting errors and tidying payload/address traits

* Get subxt-core compiling

* Small fixes in subxt-core and remove metadata mod

* subxt-core: cargo check --all-targets passes

* Fix test

* WIP starting to update subxt from subxt-core changes

* WIP splitting up subxt errors into smaller variants

* WIP errors: add DispatchError errors

* Port new Storage APIs to subxt-core

* cargo check -p subxt passes

* Quick-fix errors in subxt-cli (explore subcommand)

* fmt

* Finish fixing codegen up and start fixing examples

* get Subxt examples compiling and bytes_at for constants

* Add some arcs to limit lifetimes in subxt/subxt-core storage APIs

* A little Arcing to allow more method chaining in Storage APIs, aligning with Subxt

* Update codegen test

* cargo check --all-targets passing

* cargo check --features 'unstable-light-client' passing

* clippy

* Remove unused dep in subxt

* use published frame-decode

* fix wasm-example

* Add new tx extension to fix daily tests

* Remove unused subxt_core::dynamic::DecodedValue type

* Update book to match changes

* Update docs to fix more broken bits

* Add missing docs

* fmt

* allow larger result errs for now

* Add missing alloc imports in subxt-core

* Fix doc tests and fix bug getting constant info

* Fix V14 -> Metadata transform for storage & constants

* Fix parachain example

* Fix FFI example

* BlockLength decodes t ostruct, not u128

* use fetch/iter shorthands rather than entry in most storage tests

* Fix some integration tests

* Fix Runtime codegen tests

* Expose the dynamic custom_value selecter and use in a UI test

* Update codegen metadata

* Tidy CLI storage query and support (str,str) as a storage address

* Add (str,str) as valid constant address too

* Show string tuple in constants example

* Via the magic of traits, avoid needing any clones of queries/addresses and accept references to them

* clippy

* [v0.50] update scale-info-legacy and frame-decode to latest (#2119)

* bump scale-info-legacy and frame-decode to latest

* Remove something we don't need in this PR

* Fully remove unused for now dep

* [v0.50] Convert historic metadata to subxt::Metadata (#2120)

* First pass converting historic metadatas to our subxt::Metadata type

* use published frame-decode

* fmt and rename legacy metadata macro

* Enable legacy feature where needed in subxt_metadata so it compiles on its own

* Use cargo hack more in CI and fix subxt-metadata features

* Add tests for metadata conversion (need to optimise; some too expensive right now

* Address performance and equality issues in metadata conversion testing

* fmt

* fmt all

* clippy

* Fix a doc link

* Test codegen and fixes to make it work

* Remove local frame-decode patch

* bump frame-decode to latest

* [v0.50.0] Allow visiting extrinsic fields in subxt_historic (#2124)

* Allow visiting extrinsic fields

* fmt

* Don't use local scale-decode dep

* Clippy and tidy

* Extend 'subxt codegen' CLI to work with legacy metadatas

* Simplify historic extrinsics example now that AccountId32s have paths/names

* clippy

* clippy

* clippy..

* Allow visiting storage values, too, and clean up extrinsic visiting a little by narrowing lifetime

* Try to fix flaky test

* Add custom value decode to extrinsics example

* Remove useless else branch ra thought I needed

* Simplify examples

* Prep to release v0.0.5 (#2126)
This commit is contained in:
James Wilson
2025-11-22 10:44:03 +00:00
committed by GitHub
parent 586b814ecd
commit 8203679cbd
158 changed files with 13736 additions and 16451 deletions
@@ -158,7 +158,7 @@ async fn runtime_api_call() -> Result<(), subxt::Error> {
let mut sub = api.blocks().subscribe_best().await?;
let block = sub.next().await.unwrap()?;
let rt = block.runtime_api().await?;
let rt = block.runtime_api().await;
// get metadata via raw state_call.
let meta_bytes = rt.call_raw("Metadata_metadata", None).await?;
@@ -401,7 +401,7 @@ async fn decode_block_mortality() {
}
// Explicit Mortal:
for for_n_blocks in [4, 16, 128] {
for for_n_blocks in [16, 64, 128] {
let tx = submit_extrinsic_and_get_it_back(
&api,
DefaultExtrinsicParamsBuilder::new().mortal(for_n_blocks),
@@ -75,7 +75,6 @@ async fn archive_v1_call() {
let subxt_metadata_versions = block
.runtime_api()
.await
.unwrap()
.call(node_runtime::apis().metadata().metadata_versions())
.await
.unwrap()
@@ -184,20 +183,15 @@ async fn archive_v1_storage() {
while let Some(block) = blocks.next().await {
let block_hash = block.hash();
let account_info_addr = {
let alice: AccountId32 = dev::alice().public_key().into();
let addr = node_runtime::storage().system().account(alice);
api.storage().address_bytes(&addr).unwrap()
};
let alice: AccountId32 = dev::alice().public_key().into();
let addr = node_runtime::storage().system().account();
// Fetch raw value using Subxt to compare against
let subxt_account_info = api
.storage()
.at(block.reference())
.fetch_raw(account_info_addr.clone())
.await
.unwrap()
.unwrap();
// Fetch value using Subxt to compare against
let storage_at = api.storage().at(block.reference());
let storage_entry = storage_at.entry(addr).unwrap();
let subxt_account_info = storage_entry.fetch((alice.clone(),)).await.unwrap();
let subxt_account_info_bytes = subxt_account_info.bytes();
let account_info_addr = storage_entry.key((alice,)).unwrap();
// Construct archive query; ask for item then hash of item.
let storage_query = vec![
@@ -223,7 +217,7 @@ async fn archive_v1_storage() {
query_item,
ArchiveStorageEventItem {
key: Bytes(account_info_addr.clone()),
value: Some(Bytes(subxt_account_info.clone())),
value: Some(Bytes(subxt_account_info_bytes.to_vec())),
hash: None,
closest_descendant_merkle_value: None,
child_trie_key: None
@@ -238,7 +232,7 @@ async fn archive_v1_storage() {
ArchiveStorageEventItem {
key: Bytes(account_info_addr),
value: None,
hash: Some(hasher.hash(&subxt_account_info)),
hash: Some(hasher.hash(subxt_account_info_bytes)),
closest_descendant_merkle_value: None,
child_trie_key: None
}
@@ -134,8 +134,12 @@ async fn chainhead_v1_storage() {
let sub_id = blocks.subscription_id().unwrap();
let alice: AccountId32 = dev::alice().public_key().into();
let addr = node_runtime::storage().system().account(alice);
let addr_bytes = api.storage().address_bytes(&addr).unwrap();
let addr_bytes = {
let storage_at = api.storage().at_latest().await.unwrap();
let addr = node_runtime::storage().system().account();
storage_at.entry(addr).unwrap().key((alice,)).unwrap()
};
let items = vec![StorageQuery {
key: addr_bytes.as_slice(),
@@ -13,7 +13,7 @@ use futures::StreamExt;
use subxt::{
backend::BackendExt,
error::{DispatchError, Error},
error::{DispatchError, TransactionEventsError, TransactionFinalizedSuccessError},
tx::{TransactionInvalid, ValidationResult},
};
use subxt_signer::sr25519::dev;
@@ -27,42 +27,16 @@ mod chain_head_rpcs;
#[cfg(fullclient)]
#[subxt_test]
async fn storage_fetch_raw_keys() {
async fn storage_iter() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let addr = node_runtime::storage().system().account_iter();
let len = api
.storage()
.at_latest()
.await
.unwrap()
.fetch_raw_keys(addr.to_root_bytes())
.await
.unwrap()
.filter_map(async |r| r.ok())
.count()
.await;
let addr = node_runtime::storage().system().account();
let storage = api.storage().at_latest().await.unwrap();
let entry = storage.entry(addr)?;
assert_eq!(len, 17)
}
#[cfg(fullclient)]
#[subxt_test]
async fn storage_iter() {
let ctx = test_context().await;
let api = ctx.client();
let addr = node_runtime::storage().system().account_iter();
let addr_bytes = api.storage().address_bytes(&addr).unwrap();
assert_eq!(addr_bytes, addr.to_root_bytes());
let len = api
.storage()
.at_latest()
.await
.unwrap()
.iter(addr)
let len = entry
.iter(())
.await
.unwrap()
.filter_map(async |r| r.ok())
@@ -70,17 +44,18 @@ async fn storage_iter() {
.await;
assert_eq!(len, 17);
Ok(())
}
#[cfg(fullclient)]
#[subxt_test]
async fn storage_child_values_same_across_backends() {
async fn storage_child_values_same_across_backends() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let chainhead_client = ctx.chainhead_backend().await;
let legacy_client = ctx.legacy_backend().await;
let addr = node_runtime::storage().system().account_iter();
let addr = node_runtime::storage().system().account();
let block_ref = legacy_client
.blocks()
.at_latest()
@@ -88,18 +63,17 @@ async fn storage_child_values_same_across_backends() {
.unwrap()
.reference();
let a: Vec<_> = chainhead_client
.storage()
.at(block_ref.clone())
.iter(addr.clone())
let chainhead_storage = chainhead_client.storage().at(block_ref.clone());
let a: Vec<_> = chainhead_storage
.iter(&addr, ())
.await
.unwrap()
.collect()
.await;
let b: Vec<_> = legacy_client
.storage()
.at(block_ref.clone())
.iter(addr)
let legacy_storage = legacy_client.storage().at(block_ref.clone());
let b: Vec<_> = legacy_storage
.iter(&addr, ())
.await
.unwrap()
.collect()
@@ -109,8 +83,10 @@ async fn storage_child_values_same_across_backends() {
let a = a.unwrap();
let b = b.unwrap();
assert_eq!(a, b);
assert_eq!(a.key_bytes(), b.key_bytes());
assert_eq!(a.value().bytes(), b.value().bytes());
}
Ok(())
}
#[subxt_test]
@@ -273,7 +249,10 @@ async fn decode_a_module_error() {
.await
.expect_err("an 'unknown asset' error");
let Error::Runtime(DispatchError::Module(module_err)) = err else {
let TransactionFinalizedSuccessError::SuccessError(TransactionEventsError::ExtrinsicFailed(
DispatchError::Module(module_err),
)) = err
else {
panic!("Expected a ModuleError, got {err:?}");
};
File diff suppressed because it is too large Load Diff
@@ -8,7 +8,8 @@ use crate::{
};
use codec::Decode;
use subxt::{
error::{DispatchError, Error, TokenError},
error::{DispatchError, TokenError, TransactionEventsError, TransactionFinalizedSuccessError},
ext::scale_decode::DecodeAsType,
utils::{AccountId32, MultiAddress},
};
use subxt_signer::sr25519::dev;
@@ -21,25 +22,20 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let alice_account_addr = node_runtime::storage()
.system()
.account(alice.public_key().to_account_id());
let bob_account_addr = node_runtime::storage()
.system()
.account(bob.public_key().to_account_id());
let account_addr = node_runtime::storage().system().account();
let alice_pre = api
.storage()
.at_latest()
let storage_at_pre = api.storage().at_latest().await?;
let account_entry_pre = storage_at_pre.entry(&account_addr)?;
let alice_pre = account_entry_pre
.fetch((alice.public_key().to_account_id(),))
.await?
.fetch_or_default(&alice_account_addr)
.await?;
let bob_pre = api
.storage()
.at_latest()
.decode()?;
let bob_pre = account_entry_pre
.fetch((bob.public_key().to_account_id(),))
.await?
.fetch_or_default(&bob_account_addr)
.await?;
.decode()?;
let tx = node_runtime::tx()
.balances()
@@ -73,18 +69,18 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error> {
};
assert_eq!(event, expected_event);
let alice_post = api
.storage()
.at_latest()
let storage_at_post = api.storage().at_latest().await?;
let account_entry_post = storage_at_post.entry(&account_addr)?;
let alice_post = account_entry_post
.fetch((alice.public_key().to_account_id(),))
.await?
.fetch_or_default(&alice_account_addr)
.await?;
let bob_post = api
.storage()
.at_latest()
.decode()?;
let bob_post = account_entry_post
.fetch((bob.public_key().to_account_id(),))
.await?
.fetch_or_default(&bob_account_addr)
.await?;
.decode()?;
assert!(alice_pre.data.free - 10_000 >= alice_post.data.free);
assert_eq!(bob_pre.data.free + 10_000, bob_post.data.free);
@@ -94,36 +90,27 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error> {
#[cfg(fullclient)]
#[subxt_test]
async fn tx_dynamic_transfer() -> Result<(), subxt::Error> {
use subxt::ext::scale_value::{At, Composite, Value};
use subxt::ext::scale_value::{At, Value};
let alice = dev::alice();
let bob = dev::bob();
let ctx = test_context().await;
let api = ctx.client();
let alice_account_addr = subxt::dynamic::storage(
"System",
"Account",
vec![Value::from_bytes(alice.public_key().to_account_id())],
);
let bob_account_addr = subxt::dynamic::storage(
"System",
"Account",
vec![Value::from_bytes(bob.public_key().to_account_id())],
);
let account_addr = subxt::dynamic::storage::<(Value,), Value>("System", "Account");
let alice_pre = api
.storage()
.at_latest()
let storage_at_pre = api.storage().at_latest().await?;
let account_entry_pre = storage_at_pre.entry(&account_addr)?;
let alice_pre = account_entry_pre
.fetch((Value::from_bytes(alice.public_key().to_account_id()),))
.await?
.fetch_or_default(&alice_account_addr)
.await?;
let bob_pre = api
.storage()
.at_latest()
.decode()?;
let bob_pre = account_entry_pre
.fetch((Value::from_bytes(bob.public_key().to_account_id()),))
.await?
.fetch_or_default(&bob_account_addr)
.await?;
.decode()?;
let tx = subxt::dynamic::tx(
"Balances",
@@ -144,69 +131,48 @@ async fn tx_dynamic_transfer() -> Result<(), subxt::Error> {
.wait_for_finalized_success()
.await?;
let event_fields = events
let actual_transfer_event = events
.iter()
.filter_map(|ev| ev.ok())
.find(|ev| ev.pallet_name() == "Balances" && ev.variant_name() == "Transfer")
.expect("Failed to find Transfer event")
.field_values()?
.map_context(|_| ());
.decode_as_fields::<DecodedTransferEvent>()
.expect("Failed to decode event fields");
let expected_fields = Composite::Named(vec![
(
"from".into(),
Value::unnamed_composite(vec![Value::from_bytes(alice.public_key().to_account_id())]),
),
(
"to".into(),
Value::unnamed_composite(vec![Value::from_bytes(bob.public_key().to_account_id())]),
),
("amount".into(), Value::u128(10_000)),
]);
assert_eq!(event_fields, expected_fields);
#[derive(DecodeAsType, Debug, PartialEq)]
#[decode_as_type(crate_path = "::subxt::ext::scale_decode")]
struct DecodedTransferEvent {
from: AccountId32,
to: AccountId32,
amount: u128,
}
let alice_post = api
.storage()
.at_latest()
let expected_transfer_event = DecodedTransferEvent {
from: alice.public_key().to_account_id(),
to: bob.public_key().to_account_id(),
amount: 10000,
};
assert_eq!(actual_transfer_event, expected_transfer_event);
let storage_at_post = api.storage().at_latest().await?;
let account_entry_post = storage_at_post.entry(&account_addr)?;
let alice_post = account_entry_post
.fetch((Value::from_bytes(alice.public_key().to_account_id()),))
.await?
.fetch_or_default(&alice_account_addr)
.await?;
let bob_post = api
.storage()
.at_latest()
.decode()?;
let bob_post = account_entry_post
.fetch((Value::from_bytes(bob.public_key().to_account_id()),))
.await?
.fetch_or_default(&bob_account_addr)
.await?;
.decode()?;
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 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 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();
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();
assert!(alice_pre_free - 10_000 >= alice_post_free);
assert_eq!(bob_pre_free + 10_000, bob_post_free);
@@ -222,16 +188,16 @@ async fn multiple_sequential_transfers_work() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let bob_account_info_addr = node_runtime::storage()
.system()
.account(bob.public_key().to_account_id());
let bob_pre = api
.storage()
.at_latest()
.await?
.fetch_or_default(&bob_account_info_addr)
.await?;
.fetch(
node_runtime::storage().system().account(),
(bob.public_key().to_account_id(),),
)
.await?
.decode()?;
// Do a transfer several times. If this works, it indicates that the
// nonce is properly incremented each time.
@@ -246,8 +212,7 @@ async fn multiple_sequential_transfers_work() -> Result<(), subxt::Error> {
signed_extrinsic
.submit_and_watch()
.await
.unwrap()
.await?
.wait_for_finalized_success()
.await?;
}
@@ -256,8 +221,12 @@ async fn multiple_sequential_transfers_work() -> Result<(), subxt::Error> {
.storage()
.at_latest()
.await?
.fetch_or_default(&bob_account_info_addr)
.await?;
.fetch(
node_runtime::storage().system().account(),
(bob.public_key().to_account_id(),),
)
.await?
.decode()?;
assert_eq!(bob_pre.data.free + 30_000, bob_post.data.free);
Ok(())
@@ -274,8 +243,12 @@ async fn storage_total_issuance() {
.at_latest()
.await
.unwrap()
.fetch_or_default(&addr)
.entry(addr)
.unwrap()
.fetch()
.await
.unwrap()
.decode()
.unwrap();
assert_ne!(total_issuance, 0);
}
@@ -286,14 +259,15 @@ async fn storage_balance_lock() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let holds_addr = node_runtime::storage().balances().holds(bob);
let holds_addr = node_runtime::storage().balances().holds();
let holds = api
.storage()
.at_latest()
.await?
.fetch_or_default(&holds_addr)
.fetch(holds_addr, (bob,))
.await?
.decode()?
.0;
assert_eq!(holds.len(), 0);
@@ -345,13 +319,18 @@ async fn transfer_error() {
.wait_for_finalized_success()
.await;
assert!(
matches!(
res,
Err(Error::Runtime(DispatchError::Token(
// Check that we get a FundsUnavailable error
let is_funds_unavailable = matches!(
res,
Err(TransactionFinalizedSuccessError::SuccessError(
TransactionEventsError::ExtrinsicFailed(DispatchError::Token(
TokenError::FundsUnavailable
)))
),
)),
))
);
assert!(
is_funds_unavailable,
"Expected an insufficient balance, got {res:?}"
);
}
@@ -75,7 +75,8 @@ impl ContractsTestContext {
let code_stored = events
.find_first::<events::CodeStored>()?
.ok_or_else(|| Error::Other("Failed to find a CodeStored event".into()))?;
.ok_or_else(|| Error::other_str("Failed to find a CodeStored event"))?;
Ok(code_stored.code_hash)
}
@@ -109,13 +110,13 @@ impl ContractsTestContext {
let code_stored = events
.find_first::<events::CodeStored>()?
.ok_or_else(|| Error::Other("Failed to find a CodeStored event".into()))?;
.ok_or_else(|| Error::other_str("Failed to find a CodeStored event"))?;
let instantiated = events
.find_first::<events::Instantiated>()?
.ok_or_else(|| Error::Other("Failed to find a Instantiated event".into()))?;
.ok_or_else(|| Error::other_str("Failed to find a Instantiated event"))?;
let _extrinsic_success = events
.find_first::<system::events::ExtrinsicSuccess>()?
.ok_or_else(|| Error::Other("Failed to find a ExtrinsicSuccess event".into()))?;
.ok_or_else(|| Error::other_str("Failed to find a ExtrinsicSuccess event"))?;
tracing::info!(" Code hash: {:?}", code_stored.code_hash);
tracing::info!(" Contract address: {:?}", instantiated.contract);
@@ -156,7 +157,7 @@ impl ContractsTestContext {
tracing::info!("Instantiate result: {:?}", result);
let instantiated = result
.find_first::<events::Instantiated>()?
.ok_or_else(|| Error::Other("Failed to find a Instantiated event".into()))?;
.ok_or_else(|| Error::other_str("Failed to find a Instantiated event"))?;
Ok(instantiated.contract)
}
@@ -218,37 +219,26 @@ async fn tx_call() {
let cxt = ContractsTestContext::init().await;
let (_, contract) = cxt.instantiate_with_code().await.unwrap();
let info_addr = node_runtime::storage()
.contracts()
.contract_info_of(contract.clone());
let storage_at = cxt.client().storage().at_latest().await.unwrap();
let contract_info = cxt
.client()
.storage()
.at_latest()
let contract_info_addr = node_runtime::storage().contracts().contract_info_of();
let contract_info = storage_at
.fetch(&contract_info_addr, (contract.clone(),))
.await
.unwrap()
.fetch(&info_addr)
.await;
.unwrap();
assert!(
contract_info.is_ok(),
contract_info.decode().is_ok(),
"Contract info is not ok, is: {contract_info:?}"
);
let info_addr_iter = node_runtime::storage().contracts().contract_info_of_iter();
let mut iter = storage_at.iter(contract_info_addr, ()).await.unwrap();
let keys_and_values = cxt
.client()
.storage()
.at_latest()
.await
.unwrap()
.iter(info_addr_iter)
.await
.unwrap()
.collect::<Vec<_>>()
.await;
let mut keys_and_values = Vec::new();
while let Some(kv) = iter.next().await {
keys_and_values.push(kv);
}
assert_eq!(keys_and_values.len(), 1);
println!("keys+values post: {keys_and_values:?}");
@@ -13,8 +13,9 @@ use crate::{
},
subxt_test, test_context,
};
use assert_matches::assert_matches;
use subxt::error::{DispatchError, Error};
use subxt::error::{
DispatchError, Error, TransactionEventsError, TransactionFinalizedSuccessError,
};
use subxt_signer::{
SecretUri,
sr25519::{self, dev},
@@ -83,11 +84,16 @@ async fn validate_not_possible_for_controller_account() -> Result<(), Error> {
.wait_for_finalized_success()
.await;
assert_matches!(announce_validator, Err(Error::Runtime(DispatchError::Module(err))) => {
if let Err(TransactionFinalizedSuccessError::SuccessError(
TransactionEventsError::ExtrinsicFailed(DispatchError::Module(err)),
)) = announce_validator
{
let details = err.details().unwrap();
assert_eq!(details.pallet.name(), "Staking");
assert_eq!(&details.variant.name, "NotController");
});
} else {
panic!("Expected an error");
}
Ok(())
}
@@ -142,11 +148,16 @@ async fn nominate_not_possible_for_controller_account() -> Result<(), Error> {
.wait_for_finalized_success()
.await;
assert_matches!(nomination, Err(Error::Runtime(DispatchError::Module(err))) => {
if let Err(TransactionFinalizedSuccessError::SuccessError(
TransactionEventsError::ExtrinsicFailed(DispatchError::Module(err)),
)) = nomination
{
let details = err.details().unwrap();
assert_eq!(details.pallet.name(), "Staking");
assert_eq!(&details.variant.name, "NotController");
});
} else {
panic!("Expected an error");
}
Ok(())
}
@@ -175,16 +186,14 @@ async fn chill_works_for_stash_only() -> Result<(), Error> {
.wait_for_finalized_success()
.await?;
let ledger_addr = node_runtime::storage()
.staking()
.ledger(alice_stash.public_key().to_account_id());
let ledger_addr = node_runtime::storage().staking().ledger();
let ledger = api
.storage()
.at_latest()
.await?
.fetch(&ledger_addr)
.fetch(ledger_addr, (alice_stash.public_key().to_account_id(),))
.await?
.unwrap();
.decode()?;
assert_eq!(alice_stash.public_key().to_account_id(), ledger.stash);
let chill_tx = node_runtime::tx().staking().chill();
@@ -201,11 +210,16 @@ async fn chill_works_for_stash_only() -> Result<(), Error> {
.wait_for_finalized_success()
.await;
assert_matches!(chill, Err(Error::Runtime(DispatchError::Module(err))) => {
if let Err(TransactionFinalizedSuccessError::SuccessError(
TransactionEventsError::ExtrinsicFailed(DispatchError::Module(err)),
)) = chill
{
let details = err.details().unwrap();
assert_eq!(details.pallet.name(), "Staking");
assert_eq!(&details.variant.name, "NotController");
});
} else {
panic!("Expected an error");
}
let signed_extrinsic = api
.tx()
@@ -256,11 +270,16 @@ async fn tx_bond() -> Result<(), Error> {
.wait_for_finalized_success()
.await;
assert_matches!(bond_again, Err(Error::Runtime(DispatchError::Module(err))) => {
if let Err(TransactionFinalizedSuccessError::SuccessError(
TransactionEventsError::ExtrinsicFailed(DispatchError::Module(err)),
)) = bond_again
{
let details = err.details().unwrap();
assert_eq!(details.pallet.name(), "Staking");
assert_eq!(&details.variant.name, "AlreadyBonded");
});
} else {
panic!("Expected an error");
}
Ok(())
}
@@ -283,9 +302,9 @@ async fn storage_current_era() -> Result<(), Error> {
.storage()
.at_latest()
.await?
.fetch(&current_era_addr)
.fetch(current_era_addr, ())
.await?
.expect("current era always exists");
.decode()?;
Ok(())
}
@@ -293,13 +312,14 @@ async fn storage_current_era() -> Result<(), Error> {
async fn storage_era_reward_points() -> Result<(), Error> {
let ctx = test_context().await;
let api = ctx.client();
let reward_points_addr = node_runtime::storage().staking().eras_reward_points(0);
let reward_points_addr = node_runtime::storage().staking().eras_reward_points();
let current_era_result = api
.storage()
.at_latest()
.await?
.fetch(&reward_points_addr)
.await;
.fetch(reward_points_addr, (0,))
.await?
.decode();
assert!(current_era_result.is_ok());
Ok(())
@@ -6,7 +6,6 @@ use crate::{
node_runtime::{self, system},
subxt_test, test_context,
};
use assert_matches::assert_matches;
use subxt_signer::sr25519::dev;
#[subxt_test]
@@ -16,18 +15,16 @@ async fn storage_account() -> Result<(), subxt::Error> {
let alice = dev::alice();
let account_info_addr = node_runtime::storage()
.system()
.account(alice.public_key().to_account_id());
let account_info_addr = node_runtime::storage().system().account();
let account_info = api
let _account_info = api
.storage()
.at_latest()
.await?
.fetch_or_default(&account_info_addr)
.await;
.fetch(account_info_addr, (alice.public_key().to_account_id(),))
.await?
.decode()?;
assert_matches!(account_info, Ok(_));
Ok(())
}
@@ -9,13 +9,14 @@ async fn storage_get_current_timestamp() {
let ctx = test_context().await;
let api = ctx.client();
let timestamp = api
.storage()
.at_latest()
.await
.unwrap()
.fetch(&node_runtime::storage().timestamp().now())
.await;
let storage_at = api.storage().at_latest().await.unwrap();
assert!(timestamp.is_ok())
let timestamp_value = storage_at
.entry(node_runtime::storage().timestamp().now())
.unwrap()
.fetch()
.await
.unwrap();
assert!(timestamp_value.decode().is_ok())
}
@@ -50,7 +50,7 @@ async fn metadata_to_api(metadata: Metadata, ctx: &TestContext) -> OfflineClient
fn v15_to_metadata(v15: RuntimeMetadataV15) -> Metadata {
let subxt_md: subxt_metadata::Metadata = v15.try_into().unwrap();
subxt_md.into()
subxt_md
}
fn default_pallet() -> PalletMetadata {
@@ -167,9 +167,9 @@ async fn constant_values_are_not_validated() {
.expect("ExistentialDeposit constant must be present");
// Modifying a constant value should not lead to an error:
existential.value = vec![0u8; 32];
existential.value = vec![0u8; 16];
// Build our API again, this time form the metadata we've tweaked.
// Build our API again, this time from the metadata we've tweaked.
let api_from_modified_metadata = {
let metadata_before = v15_to_metadata(v15_metadata);
metadata_to_api(metadata_before, &ctx).await
@@ -3,6 +3,7 @@
// see LICENSE for license details.
use crate::{node_runtime, subxt_test, test_context, utils::wait_for_blocks};
use futures::StreamExt;
#[cfg(fullclient)]
use subxt::utils::AccountId32;
@@ -23,8 +24,9 @@ async fn storage_plain_lookup() -> Result<(), subxt::Error> {
.storage()
.at_latest()
.await?
.fetch_or_default(&addr)
.await?;
.fetch(addr, ())
.await?
.decode()?;
assert!(entry > 0);
Ok(())
@@ -48,13 +50,14 @@ async fn storage_map_lookup() -> Result<(), subxt::Error> {
.await?;
// Look up the nonce for the user (we expect it to be 1).
let nonce_addr = node_runtime::storage().system().account(alice);
let nonce_addr = node_runtime::storage().system().account();
let entry = api
.storage()
.at_latest()
.await?
.fetch_or_default(&nonce_addr)
.await?;
.fetch(nonce_addr, (alice,))
.await?
.decode()?;
assert_eq!(entry.nonce, 1);
Ok(())
@@ -70,10 +73,13 @@ async fn storage_n_mapish_key_is_properly_created() -> Result<(), subxt::Error>
let api = ctx.client();
// This is what the generated code hashes a `session().key_owner(..)` key into:
let actual_key = node_runtime::storage()
.session()
.key_owner((KeyTypeId([1, 2, 3, 4]), vec![5, 6, 7, 8]));
let actual_key_bytes = api.storage().address_bytes(&actual_key)?;
let storage_addr = node_runtime::storage().session().key_owner();
let actual_key_bytes = api
.storage()
.at_latest()
.await?
.entry(storage_addr)?
.key(((KeyTypeId([1, 2, 3, 4]), vec![5, 6, 7, 8]),))?;
// Let's manually hash to what we assume it should be and compare:
let expected_key_bytes = {
@@ -124,9 +130,15 @@ async fn storage_n_map_storage_lookup() -> Result<(), subxt::Error> {
.await?;
// The actual test; look up this approval in storage:
let addr = node_runtime::storage().assets().approvals(99, alice, bob);
let entry = api.storage().at_latest().await?.fetch(&addr).await?;
assert_eq!(entry.map(|a| a.amount), Some(123));
let addr = node_runtime::storage().assets().approvals();
let entry = api
.storage()
.at_latest()
.await?
.fetch(addr, (99, alice, bob))
.await?
.decode()?;
assert_eq!(entry.amount, 123);
Ok(())
}
@@ -168,14 +180,18 @@ async fn storage_partial_lookup() -> Result<(), subxt::Error> {
}
// Check all approvals.
let addr = node_runtime::storage().assets().approvals_iter();
let addr_bytes = api.storage().address_bytes(&addr)?;
let mut results = api.storage().at_latest().await?.iter(addr).await?;
let approvals_addr = node_runtime::storage().assets().approvals();
let storage_at = api.storage().at_latest().await?;
let approvals_entry = storage_at.entry(approvals_addr)?;
let mut results = approvals_entry.iter(()).await?;
let mut approvals = Vec::new();
while let Some(Ok(kv)) = results.next().await {
assert!(kv.key_bytes.starts_with(&addr_bytes));
approvals.push(kv.value);
while let Some(kv) = results.next().await {
let kv = kv?;
assert!(kv.key_bytes().starts_with(&approvals_entry.key_prefix()));
approvals.push(kv.value().decode()?);
}
assert_eq!(approvals.len(), assets.len());
let mut amounts = approvals.iter().map(|a| a.amount).collect::<Vec<_>>();
amounts.sort();
@@ -185,17 +201,13 @@ async fn storage_partial_lookup() -> Result<(), subxt::Error> {
// Check all assets starting with ID 99.
for (asset_id, _, _, amount) in assets.clone() {
let addr = node_runtime::storage().assets().approvals_iter1(asset_id);
let second_addr_bytes = api.storage().address_bytes(&addr)?;
// Keys must be different, since we are adding to the root key.
assert_ne!(addr_bytes, second_addr_bytes);
let mut results = api.storage().at_latest().await?.iter(addr).await?;
let mut results = approvals_entry.iter((asset_id,)).await?;
let mut approvals = Vec::new();
while let Some(Ok(kv)) = results.next().await {
assert!(kv.key_bytes.starts_with(&addr_bytes));
approvals.push(kv.value);
while let Some(kv) = results.next().await {
let kv = kv?;
assert!(kv.key_bytes().starts_with(&approvals_entry.key_prefix()));
approvals.push(kv.value().decode()?);
}
assert_eq!(approvals.len(), 1);
assert_eq!(approvals[0].amount, amount);
@@ -241,21 +253,14 @@ async fn storage_iter_decode_keys() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let storage_static = node_runtime::storage().system().account_iter();
let results_static = api
.storage()
.at_latest()
.await?
.iter(storage_static)
.await?;
let storage_static = node_runtime::storage().system().account();
let storage_at_static = api.storage().at_latest().await?;
let results_static = storage_at_static.iter(storage_static, ()).await?;
let storage_dynamic = subxt::dynamic::storage("System", "Account", vec![]);
let results_dynamic = api
.storage()
.at_latest()
.await?
.iter(storage_dynamic)
.await?;
let storage_dynamic =
subxt::dynamic::storage::<(scale_value::Value,), scale_value::Value>("System", "Account");
let storage_at_dynamic = api.storage().at_latest().await?;
let results_dynamic = storage_at_dynamic.iter(storage_dynamic, ()).await?;
// Even the testing node should have more than 3 accounts registered.
let results_static = results_static.take(3).collect::<Vec<_>>().await;
@@ -272,9 +277,9 @@ async fn storage_iter_decode_keys() -> Result<(), subxt::Error> {
let dynamic_kv = dynamic_kv?;
// We only care about the underlying key bytes.
assert_eq!(static_kv.key_bytes, dynamic_kv.key_bytes);
assert_eq!(static_kv.key_bytes(), dynamic_kv.key_bytes());
let bytes = static_kv.key_bytes;
let bytes = static_kv.key_bytes();
assert!(bytes.len() > 32);
// The first 16 bytes should be the twox hash of "System" and the next 16 bytes should be the twox hash of "Account".
@@ -4,7 +4,6 @@
use crate::utils::node_runtime;
use crate::{subxt_test, test_context};
use core::ops::Deref;
use frame_decode::extrinsics::ExtrinsicType;
use subxt_signer::sr25519::dev;
@@ -25,12 +24,9 @@ async fn v4_unsigned_encode_decode() -> Result<(), subxt::Error> {
let tx_bytes = api.tx().create_v4_unsigned(&call).unwrap().into_encoded();
let tx_bytes_cursor = &mut &*tx_bytes;
let decoded = frame_decode::extrinsics::decode_extrinsic(
tx_bytes_cursor,
md.deref(),
api.metadata().types(),
)
.unwrap();
let decoded =
frame_decode::extrinsics::decode_extrinsic(tx_bytes_cursor, &md, api.metadata().types())
.unwrap();
assert_eq!(tx_bytes_cursor.len(), 0);
assert_eq!(decoded.version(), 4);
@@ -55,12 +51,8 @@ async fn v5_bare_encode_decode() -> Result<(), subxt::Error> {
let tx_bytes = api.tx().create_v5_bare(&call).unwrap().into_encoded();
let tx_bytes_cursor = &mut &*tx_bytes;
let decoded = frame_decode::extrinsics::decode_extrinsic(
tx_bytes_cursor,
md.deref(),
api.metadata().types(),
)
.unwrap();
let decoded =
frame_decode::extrinsics::decode_extrinsic(tx_bytes_cursor, &md, md.types()).unwrap();
assert_eq!(tx_bytes_cursor.len(), 0);
assert_eq!(decoded.version(), 5);
@@ -92,12 +84,8 @@ async fn v4_signed_encode_decode() -> Result<(), subxt::Error> {
.into_encoded();
let tx_bytes_cursor = &mut &*tx_bytes;
let decoded = frame_decode::extrinsics::decode_extrinsic(
tx_bytes_cursor,
md.deref(),
api.metadata().types(),
)
.unwrap();
let decoded =
frame_decode::extrinsics::decode_extrinsic(tx_bytes_cursor, &md, md.types()).unwrap();
assert_eq!(tx_bytes_cursor.len(), 0);
assert_eq!(decoded.version(), 4);
@@ -129,12 +117,8 @@ async fn v5_general_encode_decode() -> Result<(), subxt::Error> {
.into_encoded();
let tx_bytes_cursor = &mut &*tx_bytes;
let decoded = frame_decode::extrinsics::decode_extrinsic(
tx_bytes_cursor,
md.deref(),
api.metadata().types(),
)
.unwrap();
let decoded =
frame_decode::extrinsics::decode_extrinsic(tx_bytes_cursor, &md, md.types()).unwrap();
assert_eq!(tx_bytes_cursor.len(), 0);
assert_eq!(decoded.version(), 5);
@@ -32,6 +32,7 @@ use codec::Compact;
use std::sync::Arc;
use subxt::backend::chain_head::ChainHeadBackend;
use subxt::backend::rpc::RpcClient;
use subxt::dynamic::Value;
use subxt::{client::OnlineClient, config::PolkadotConfig, lightclient::LightClient};
use subxt_metadata::Metadata;
@@ -104,7 +105,7 @@ async fn runtime_api_call(api: &Client) -> Result<(), subxt::Error> {
let block = sub.next().await.unwrap()?;
tracing::trace!("First block took {:?}", now.elapsed());
let rt = block.runtime_api().await?;
let rt = block.runtime_api().await;
// get metadata via state_call. if it decodes ok, it's probably all good.
let result_bytes = rt.call_raw("Metadata_metadata", None).await?;
@@ -120,13 +121,10 @@ async fn storage_plain_lookup(api: &Client) -> Result<(), subxt::Error> {
let now = std::time::Instant::now();
tracing::trace!("Check storage_plain_lookup");
let storage_at = api.storage().at_latest().await?;
let addr = node_runtime::storage().timestamp().now();
let entry = api
.storage()
.at_latest()
.await?
.fetch_or_default(&addr)
.await?;
let entry = storage_at.fetch(addr, ()).await?.decode()?;
tracing::trace!("Storage lookup took {:?}\n", now.elapsed());
@@ -140,7 +138,7 @@ async fn dynamic_constant_query(api: &Client) -> Result<(), subxt::Error> {
let now = std::time::Instant::now();
tracing::trace!("Check dynamic_constant_query");
let constant_query = subxt::dynamic::constant("System", "BlockLength");
let constant_query = subxt::dynamic::constant::<Value>("System", "BlockLength");
let _value = api.constants().at(&constant_query)?;
tracing::trace!("Dynamic constant query took {:?}\n", now.elapsed());
@@ -3,8 +3,8 @@
// see LICENSE for license details.
use subxt::{
Config, Error, OnlineClient, SubstrateConfig, backend::StreamOf, blocks::Block,
client::OnlineClientT,
Config, OnlineClient, SubstrateConfig, backend::StreamOf, blocks::Block, client::OnlineClientT,
error::BackendError,
};
/// Wait for blocks to be produced before running tests. Specifically, we
@@ -32,7 +32,9 @@ pub async fn wait_for_number_of_blocks<C: Config>(
/// This may be useful on the unstable backend when the initial blocks may be large
/// and one relies on something to included in finalized block in ner future.
pub async fn consume_initial_blocks(
blocks: &mut StreamOf<Result<Block<SubstrateConfig, OnlineClient<SubstrateConfig>>, Error>>,
blocks: &mut StreamOf<
Result<Block<SubstrateConfig, OnlineClient<SubstrateConfig>>, BackendError>,
>,
) {
use tokio::time::{Duration, Instant, interval_at};
const MAX_DURATION: Duration = Duration::from_millis(200);