From 98fe70548052ade4dc143dc9e21230d1a055d874 Mon Sep 17 00:00:00 2001 From: Daniel Savu Date: Mon, 4 Apr 2022 14:52:51 +0200 Subject: [PATCH 1/8] feat: refactor signing in order to more easily be able to dryrun Co-authored-by: Sander Bosma Co-authored-by: Daniel Savu --- integration-tests/src/frame/balances.rs | 83 +++++++++++++++++++ subxt/src/client.rs | 106 +++++++++++++++++++----- 2 files changed, 168 insertions(+), 21 deletions(-) diff --git a/integration-tests/src/frame/balances.rs b/integration-tests/src/frame/balances.rs index 947afeb33e..592b69ea1f 100644 --- a/integration-tests/src/frame/balances.rs +++ b/integration-tests/src/frame/balances.rs @@ -32,6 +32,8 @@ use sp_core::{ use sp_keyring::AccountKeyring; use sp_runtime::{ AccountId32, + ApplyExtrinsicResult, + DispatchOutcome, MultiAddress, }; use subxt::Error; @@ -95,6 +97,87 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error> { Ok(()) } +#[tokio::test] +async fn dry_run_passes() -> Result<(), subxt::Error> { + let alice = pair_signer(AccountKeyring::Alice.pair()); + let bob = pair_signer(AccountKeyring::Bob.pair()); + let bob_address = bob.account_id().clone().into(); + let cxt = test_context().await; + let api = &cxt.api; + + let signed_extrinsic = api + .tx() + .balances() + .transfer(bob_address, 10_000)? + .create_signed(&alice, Default::default()) + .await?; + + let dry_run_res: ApplyExtrinsicResult = signed_extrinsic.dry_run().await?; + dry_run_res + .expect("expected dryrun transaction to be valid") + .expect("expected dryrunning to be successful"); + signed_extrinsic + .submit_and_watch() + .await? + .wait_for_finalized_success() + .await?; + Ok(()) +} + +#[tokio::test] +async fn dry_run_fails() -> Result<(), subxt::Error> { + let alice = pair_signer(AccountKeyring::Alice.pair()); + let alice_addr = alice.account_id().clone().into(); + let hans = pair_signer(Pair::generate().0); + let hans_address = hans.account_id().clone().into(); + let cxt = test_context().await; + let api = &cxt.api; + + api.tx() + .balances() + .transfer(hans_address, 100_000_000_000_000_000)? + .sign_and_submit_then_watch_default(&alice) + .await + .unwrap() + .wait_for_finalized_success() + .await + .unwrap(); + + let signed_extrinsic = api + .tx() + .balances() + .transfer(alice_addr, 100_000_000_000_000_000)? + .create_signed(&hans, Default::default()) + .await?; + + let dry_run_res: DispatchOutcome = signed_extrinsic + .dry_run() + .await + .expect("dryrunning failed") + .expect("expected dryrun transaction to be valid"); + if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res { + assert_eq!(module_error.index, 6); + assert_eq!(module_error.error, 2); + } else { + panic!("expected a module module error when dryrunning"); + } + + let res = signed_extrinsic + .submit_and_watch() + .await? + .wait_for_finalized_success() + .await; + + if let Err(Error::Module(err)) = res { + assert_eq!(err.pallet, "Balances"); + assert_eq!(err.error, "InsufficientBalance"); + } else { + panic!("expected a runtime module error"); + } + + Ok(()) +} + #[tokio::test] async fn multiple_transfers_work_nonce_incremented( ) -> Result<(), subxt::Error> { diff --git a/subxt/src/client.rs b/subxt/src/client.rs index 89d6cd2315..aa1cd1f7b6 100644 --- a/subxt/src/client.rs +++ b/subxt/src/client.rs @@ -15,8 +15,16 @@ // along with subxt. If not, see . use futures::future; -use sp_runtime::traits::Hash; +use jsonrpsee::{ + core::client::ClientT, + rpc_params, +}; +use sp_core::Bytes; pub use sp_runtime::traits::SignedExtension; +use sp_runtime::{ + traits::Hash, + ApplyExtrinsicResult, +}; use crate::{ error::{ @@ -242,7 +250,7 @@ where /// Returns a [`TransactionProgress`], which can be used to track the status of the transaction /// and obtain details about it, once it has made it into a block. pub async fn sign_and_submit_then_watch_default( - self, + &self, signer: &(dyn Signer + Send + Sync), ) -> Result, BasicError> where @@ -257,22 +265,14 @@ where /// Returns a [`TransactionProgress`], which can be used to track the status of the transaction /// and obtain details about it, once it has made it into a block. pub async fn sign_and_submit_then_watch( - self, + &self, signer: &(dyn Signer + Send + Sync), other_params: X::OtherParams, ) -> Result, BasicError> { - // Sign the call data to create our extrinsic. - let extrinsic = self.create_signed(signer, other_params).await?; - - // Get a hash of the extrinsic (we'll need this later). - let ext_hash = T::Hashing::hash_of(&extrinsic); - - tracing::info!("xt hash: {}", hex::encode(ext_hash.encode())); - - // Submit and watch for transaction progress. - let sub = self.client.rpc().watch_extrinsic(extrinsic).await?; - - Ok(TransactionProgress::new(sub, self.client, ext_hash)) + self.create_signed(signer, other_params) + .await? + .submit_and_watch() + .await } /// Creates and signs an extrinsic and submits to the chain for block inclusion. Passes @@ -286,7 +286,7 @@ where /// Success does not mean the extrinsic has been included in the block, just that it is valid /// and has been included in the transaction pool. pub async fn sign_and_submit_default( - self, + &self, signer: &(dyn Signer + Send + Sync), ) -> Result where @@ -304,12 +304,14 @@ where /// Success does not mean the extrinsic has been included in the block, just that it is valid /// and has been included in the transaction pool. pub async fn sign_and_submit( - self, + &self, signer: &(dyn Signer + Send + Sync), other_params: X::OtherParams, ) -> Result { - let extrinsic = self.create_signed(signer, other_params).await?; - self.client.rpc().submit_extrinsic(extrinsic).await + self.create_signed(signer, other_params) + .await? + .submit() + .await } /// Creates a returns a raw signed extrinsic, without submitting it. @@ -317,7 +319,7 @@ where &self, signer: &(dyn Signer + Send + Sync), other_params: X::OtherParams, - ) -> Result { + ) -> Result, BasicError> { // 1. Get nonce let account_nonce = if let Some(nonce) = signer.nonce() { nonce @@ -404,6 +406,68 @@ where // Wrap in Encoded to ensure that any more "encode" calls leave it in the right state. // maybe we can just return the raw bytes.. - Ok(Encoded(extrinsic)) + Ok(SignedSubmittableExtrinsic { + client: self.client, + encoded: Encoded(extrinsic), + marker: self.marker, + }) + } +} + +pub struct SignedSubmittableExtrinsic<'client, T: Config, X, E: Decode, Evs: Decode> { + client: &'client Client, + encoded: Encoded, + marker: std::marker::PhantomData<(X, E, Evs)>, +} + +impl<'client, T, X, E, Evs> SignedSubmittableExtrinsic<'client, T, X, E, Evs> +where + T: Config, + X: ExtrinsicParams, + E: Decode + HasModuleError, + Evs: Decode, +{ + /// Submits the extrinsic to the chain. + /// + /// Returns a [`TransactionProgress`], which can be used to track the status of the transaction + /// and obtain details about it, once it has made it into a block. + pub async fn submit_and_watch( + &self, + ) -> Result, BasicError> { + // Get a hash of the extrinsic (we'll need this later). + let ext_hash = T::Hashing::hash_of(&self.encoded); + + // Submit and watch for transaction progress. + let sub = self.client.rpc().watch_extrinsic(&self.encoded).await?; + + Ok(TransactionProgress::new(sub, self.client, ext_hash)) + } + + /// Submits the extrinsic to the chain for block inclusion. + /// + /// Returns `Ok` with the extrinsic hash if it is valid extrinsic. + /// + /// # Note + /// + /// Success does not mean the extrinsic has been included in the block, just that it is valid + /// and has been included in the transaction pool. + pub async fn submit(&self) -> Result { + self.client.rpc().submit_extrinsic(&self.encoded).await + } + + /// Submits the extrinsic to the dry_run RPC, to test if it would succeed. + /// + /// Returns `Ok` with an [`ApplyExtrinsicResult`], which is the result of applying of an extrinsic. + pub async fn dry_run(&self) -> Result { + let params = rpc_params![Bytes::from(self.encoded.0.clone())]; + let result_bytes: Bytes = self + .client + .rpc() + .client + .request("system_dryRun", params) + .await?; + let data: ApplyExtrinsicResult = + codec::Decode::decode(&mut result_bytes.0.as_slice())?; + Ok(data) } } From 252ac0a7782fe54545c296bbe2b1fc63dba667bb Mon Sep 17 00:00:00 2001 From: Daniel Savu Date: Mon, 30 May 2022 17:29:19 +0100 Subject: [PATCH 2/8] chore: move dry_run to rpc file --- integration-tests/src/client/mod.rs | 92 ++++++++++++++++++++++++- integration-tests/src/frame/balances.rs | 83 ---------------------- subxt/src/client.rs | 54 +++------------ subxt/src/rpc.rs | 78 ++++++++++----------- 4 files changed, 136 insertions(+), 171 deletions(-) diff --git a/integration-tests/src/client/mod.rs b/integration-tests/src/client/mod.rs index 050ef2e32b..27f6037434 100644 --- a/integration-tests/src/client/mod.rs +++ b/integration-tests/src/client/mod.rs @@ -17,14 +17,18 @@ use crate::{ test_node_process, test_node_process_with, - utils::node_runtime::system, + utils::{node_runtime::system, pair_signer, test_context}, }; -use sp_core::storage::{ +use sp_core::{storage::{ well_known_keys, StorageKey, +}, sr25519::Pair, + Pair as _, }; use sp_keyring::AccountKeyring; +use sp_runtime::DispatchOutcome; +use subxt::Error; #[tokio::test] async fn insert_key() { @@ -131,3 +135,87 @@ async fn fetch_system_info() { assert_eq!(client.rpc().system_name().await.unwrap(), "Substrate Node"); assert!(!client.rpc().system_version().await.unwrap().is_empty()); } + +#[tokio::test] +async fn dry_run_passes() { + let node_process = test_node_process().await; + let client = node_process.client(); + + let alice = pair_signer(AccountKeyring::Alice.pair()); + let bob = pair_signer(AccountKeyring::Bob.pair()); + let bob_address = bob.account_id().clone().into(); + let cxt = test_context().await; + let api = &cxt.api; + + let signed_extrinsic = api + .tx() + .balances() + .transfer(bob_address, 10_000).unwrap() + .create_signed(&alice, Default::default()) + .await.unwrap(); + + client.rpc().dry_run(&signed_extrinsic, None) + .await + .expect("dryrunning failed") + .expect("expected dryrunning to be successful") + .unwrap(); + signed_extrinsic + .submit_and_watch() + .await.unwrap() + .wait_for_finalized_success() + .await.unwrap(); +} + +#[tokio::test] +async fn dry_run_fails() { + let node_process = test_node_process().await; + let client = node_process.client(); + + let alice = pair_signer(AccountKeyring::Alice.pair()); + let alice_addr = alice.account_id().clone().into(); + let hans = pair_signer(Pair::generate().0); + let hans_address = hans.account_id().clone().into(); + let cxt = test_context().await; + let api = &cxt.api; + + api.tx() + .balances() + .transfer(hans_address, 100_000_000_000_000_000).unwrap() + .sign_and_submit_then_watch_default(&alice) + .await + .unwrap() + .wait_for_finalized_success() + .await + .unwrap(); + + let signed_extrinsic = api + .tx() + .balances() + .transfer(alice_addr, 100_000_000_000_000_000).unwrap() + .create_signed(&hans, Default::default()) + .await.unwrap(); + + let dry_run_res: DispatchOutcome = client.rpc().dry_run(&signed_extrinsic, None) + .await + .expect("dryrunning failed") + .expect("expected dryrun transaction to be valid"); + if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res { + assert_eq!(module_error.index, 6); + assert_eq!(module_error.error, 2); + } else { + panic!("expected a module module error when dryrunning"); + } + + let res = signed_extrinsic + .submit_and_watch() + .await.unwrap() + .wait_for_finalized_success() + .await; + + if let Err(Error::Module(err)) = res { + assert_eq!(err.pallet, "Balances"); + assert_eq!(err.error, "InsufficientBalance"); + } else { + panic!("expected a runtime module error"); + } +} diff --git a/integration-tests/src/frame/balances.rs b/integration-tests/src/frame/balances.rs index 592b69ea1f..947afeb33e 100644 --- a/integration-tests/src/frame/balances.rs +++ b/integration-tests/src/frame/balances.rs @@ -32,8 +32,6 @@ use sp_core::{ use sp_keyring::AccountKeyring; use sp_runtime::{ AccountId32, - ApplyExtrinsicResult, - DispatchOutcome, MultiAddress, }; use subxt::Error; @@ -97,87 +95,6 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] -async fn dry_run_passes() -> Result<(), subxt::Error> { - let alice = pair_signer(AccountKeyring::Alice.pair()); - let bob = pair_signer(AccountKeyring::Bob.pair()); - let bob_address = bob.account_id().clone().into(); - let cxt = test_context().await; - let api = &cxt.api; - - let signed_extrinsic = api - .tx() - .balances() - .transfer(bob_address, 10_000)? - .create_signed(&alice, Default::default()) - .await?; - - let dry_run_res: ApplyExtrinsicResult = signed_extrinsic.dry_run().await?; - dry_run_res - .expect("expected dryrun transaction to be valid") - .expect("expected dryrunning to be successful"); - signed_extrinsic - .submit_and_watch() - .await? - .wait_for_finalized_success() - .await?; - Ok(()) -} - -#[tokio::test] -async fn dry_run_fails() -> Result<(), subxt::Error> { - let alice = pair_signer(AccountKeyring::Alice.pair()); - let alice_addr = alice.account_id().clone().into(); - let hans = pair_signer(Pair::generate().0); - let hans_address = hans.account_id().clone().into(); - let cxt = test_context().await; - let api = &cxt.api; - - api.tx() - .balances() - .transfer(hans_address, 100_000_000_000_000_000)? - .sign_and_submit_then_watch_default(&alice) - .await - .unwrap() - .wait_for_finalized_success() - .await - .unwrap(); - - let signed_extrinsic = api - .tx() - .balances() - .transfer(alice_addr, 100_000_000_000_000_000)? - .create_signed(&hans, Default::default()) - .await?; - - let dry_run_res: DispatchOutcome = signed_extrinsic - .dry_run() - .await - .expect("dryrunning failed") - .expect("expected dryrun transaction to be valid"); - if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res { - assert_eq!(module_error.index, 6); - assert_eq!(module_error.error, 2); - } else { - panic!("expected a module module error when dryrunning"); - } - - let res = signed_extrinsic - .submit_and_watch() - .await? - .wait_for_finalized_success() - .await; - - if let Err(Error::Module(err)) = res { - assert_eq!(err.pallet, "Balances"); - assert_eq!(err.error, "InsufficientBalance"); - } else { - panic!("expected a runtime module error"); - } - - Ok(()) -} - #[tokio::test] async fn multiple_transfers_work_nonce_incremented( ) -> Result<(), subxt::Error> { diff --git a/subxt/src/client.rs b/subxt/src/client.rs index aa1cd1f7b6..241b06b727 100644 --- a/subxt/src/client.rs +++ b/subxt/src/client.rs @@ -15,45 +15,19 @@ // along with subxt. If not, see . use futures::future; -use jsonrpsee::{ - core::client::ClientT, - rpc_params, -}; -use sp_core::Bytes; pub use sp_runtime::traits::SignedExtension; -use sp_runtime::{ - traits::Hash, - ApplyExtrinsicResult, -}; +use sp_runtime::traits::Hash; use crate::{ - error::{ - BasicError, - HasModuleError, - }, - extrinsic::{ - ExtrinsicParams, - Signer, - }, - rpc::{ - Rpc, - RpcClient, - RuntimeVersion, - SystemProperties, - }, + error::{BasicError, HasModuleError}, + extrinsic::{ExtrinsicParams, Signer}, + rpc::{Rpc, RpcClient, RuntimeVersion, SystemProperties}, storage::StorageClient, transaction::TransactionProgress, updates::UpdateClient, - Call, - Config, - Encoded, - Metadata, -}; -use codec::{ - Compact, - Decode, - Encode, + Call, Config, Encoded, Metadata, }; +use codec::{Compact, Decode, Encode}; use derivative::Derivative; use parking_lot::RwLock; use std::sync::Arc; @@ -455,19 +429,7 @@ where self.client.rpc().submit_extrinsic(&self.encoded).await } - /// Submits the extrinsic to the dry_run RPC, to test if it would succeed. - /// - /// Returns `Ok` with an [`ApplyExtrinsicResult`], which is the result of applying of an extrinsic. - pub async fn dry_run(&self) -> Result { - let params = rpc_params![Bytes::from(self.encoded.0.clone())]; - let result_bytes: Bytes = self - .client - .rpc() - .client - .request("system_dryRun", params) - .await?; - let data: ApplyExtrinsicResult = - codec::Decode::decode(&mut result_bytes.0.as_slice())?; - Ok(data) + pub fn encoded(&self) -> Encoded { + self.encoded.clone() } } diff --git a/subxt/src/rpc.rs b/subxt/src/rpc.rs index 9882367e6e..57429cc0f1 100644 --- a/subxt/src/rpc.rs +++ b/subxt/src/rpc.rs @@ -21,62 +21,36 @@ // Related: https://github.com/paritytech/subxt/issues/66 #![allow(irrefutable_let_patterns)] -use std::{ - collections::HashMap, - sync::Arc, -}; +use std::{collections::HashMap, sync::Arc}; use crate::{ - error::BasicError, - storage::StorageKeyPrefix, - Config, - Metadata, - PhantomDataSendSync, -}; -use codec::{ - Decode, - Encode, + client::SignedSubmittableExtrinsic, error::BasicError, extrinsic::ExtrinsicParams, + storage::StorageKeyPrefix, Config, HasModuleError, Metadata, PhantomDataSendSync, }; +use codec::{Decode, Encode}; use frame_metadata::RuntimeMetadataPrefixed; pub use jsonrpsee::{ client_transport::ws::{ - InvalidUri, - Receiver as WsReceiver, - Sender as WsSender, - Uri, + InvalidUri, Receiver as WsReceiver, Sender as WsSender, Uri, WsTransportClientBuilder, }, core::{ client::{ - Client as RpcClient, - ClientBuilder as RpcClientBuilder, - ClientT, - Subscription, - SubscriptionClientT, + Client as RpcClient, ClientBuilder as RpcClientBuilder, ClientT, + Subscription, SubscriptionClientT, }, - to_json_value, - DeserializeOwned, - Error as RpcError, - JsonValue, + to_json_value, DeserializeOwned, Error as RpcError, JsonValue, }, rpc_params, }; -use serde::{ - Deserialize, - Serialize, -}; +use serde::{Deserialize, Serialize}; use sp_core::{ - storage::{ - StorageChangeSet, - StorageData, - StorageKey, - }, - Bytes, - U256, + storage::{StorageChangeSet, StorageData, StorageKey}, + Bytes, U256, }; -use sp_runtime::generic::{ - Block, - SignedBlock, +use sp_runtime::{ + generic::{Block, SignedBlock}, + ApplyExtrinsicResult, }; /// A number type that can be serialized both as a number or a string that encodes a number in a @@ -572,6 +546,30 @@ impl Rpc { let params = rpc_params![public_key, key_type]; Ok(self.client.request("author_hasKey", params).await?) } + + /// Submits the extrinsic to the dry_run RPC, to test if it would succeed. + /// + /// Returns `Ok` with an [`ApplyExtrinsicResult`], which is the result of applying of an extrinsic. + pub async fn dry_run<'client, Y, X, E, Evs>( + &self, + signed_submittable_extrinsic: &SignedSubmittableExtrinsic<'client, Y, X, E, Evs>, + at: Option, + ) -> Result + where + Y: Config, + X: ExtrinsicParams, + E: Decode + HasModuleError, + Evs: Decode, + { + let params = rpc_params![ + Bytes::from(signed_submittable_extrinsic.encoded().0.clone()), + at + ]; + let result_bytes: Bytes = self.client.request("system_dryRun", params).await?; + let data: ApplyExtrinsicResult = + codec::Decode::decode(&mut result_bytes.0.as_slice())?; + Ok(data) + } } /// Build WS RPC client from URL From e86f5985703ccf2ecf09b8a111bf3633395e81b3 Mon Sep 17 00:00:00 2001 From: Daniel Savu Date: Wed, 8 Jun 2022 18:49:01 +0100 Subject: [PATCH 3/8] fix: failing dryrun test --- integration-tests/src/client/mod.rs | 57 +++++++++++++++-------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/integration-tests/src/client/mod.rs b/integration-tests/src/client/mod.rs index 27f6037434..0b4a5f2038 100644 --- a/integration-tests/src/client/mod.rs +++ b/integration-tests/src/client/mod.rs @@ -15,15 +15,13 @@ // along with subxt. If not, see . use crate::{ - test_node_process, - test_node_process_with, + test_node_process, test_node_process_with, utils::{node_runtime::system, pair_signer, test_context}, }; -use sp_core::{storage::{ - well_known_keys, - StorageKey, -}, sr25519::Pair, +use sp_core::{ + sr25519::Pair, + storage::{well_known_keys, StorageKey}, Pair as _, }; use sp_keyring::AccountKeyring; @@ -150,20 +148,26 @@ async fn dry_run_passes() { let signed_extrinsic = api .tx() .balances() - .transfer(bob_address, 10_000).unwrap() + .transfer(bob_address, 10_000) + .unwrap() .create_signed(&alice, Default::default()) - .await.unwrap(); + .await + .unwrap(); - client.rpc().dry_run(&signed_extrinsic, None) + client + .rpc() + .dry_run(&signed_extrinsic, None) .await .expect("dryrunning failed") .expect("expected dryrunning to be successful") .unwrap(); signed_extrinsic .submit_and_watch() - .await.unwrap() + .await + .unwrap() .wait_for_finalized_success() - .await.unwrap(); + .await + .unwrap(); } #[tokio::test] @@ -172,43 +176,40 @@ async fn dry_run_fails() { let client = node_process.client(); let alice = pair_signer(AccountKeyring::Alice.pair()); - let alice_addr = alice.account_id().clone().into(); let hans = pair_signer(Pair::generate().0); let hans_address = hans.account_id().clone().into(); let cxt = test_context().await; let api = &cxt.api; - api.tx() + let signed_extrinsic = api + .tx() .balances() - .transfer(hans_address, 100_000_000_000_000_000).unwrap() - .sign_and_submit_then_watch_default(&alice) - .await + .transfer( + hans_address, + 100_000_000_000_000_000_000_000_000_000_000_000, + ) .unwrap() - .wait_for_finalized_success() + .create_signed(&alice, Default::default()) .await .unwrap(); - let signed_extrinsic = api - .tx() - .balances() - .transfer(alice_addr, 100_000_000_000_000_000).unwrap() - .create_signed(&hans, Default::default()) - .await.unwrap(); - - let dry_run_res: DispatchOutcome = client.rpc().dry_run(&signed_extrinsic, None) + let dry_run_res: DispatchOutcome = client + .rpc() + .dry_run(&signed_extrinsic, None) .await .expect("dryrunning failed") .expect("expected dryrun transaction to be valid"); - if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res { + if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res { assert_eq!(module_error.index, 6); assert_eq!(module_error.error, 2); } else { - panic!("expected a module module error when dryrunning"); + panic!("expected a module error when dryrunning"); } let res = signed_extrinsic .submit_and_watch() - .await.unwrap() + .await + .unwrap() .wait_for_finalized_success() .await; From bb291d2f86df6598d33083c2948db2988d61d17f Mon Sep 17 00:00:00 2001 From: Daniel Savu Date: Wed, 8 Jun 2022 22:49:44 +0100 Subject: [PATCH 4/8] fix: run cargo fmt --- integration-tests/src/client/mod.rs | 14 ++++++-- subxt/src/client.rs | 30 +++++++++++++---- subxt/src/rpc.rs | 51 ++++++++++++++++++++--------- 3 files changed, 71 insertions(+), 24 deletions(-) diff --git a/integration-tests/src/client/mod.rs b/integration-tests/src/client/mod.rs index 0b4a5f2038..657ec56bb3 100644 --- a/integration-tests/src/client/mod.rs +++ b/integration-tests/src/client/mod.rs @@ -15,13 +15,21 @@ // along with subxt. If not, see . use crate::{ - test_node_process, test_node_process_with, - utils::{node_runtime::system, pair_signer, test_context}, + test_node_process, + test_node_process_with, + utils::{ + node_runtime::system, + pair_signer, + test_context, + }, }; use sp_core::{ sr25519::Pair, - storage::{well_known_keys, StorageKey}, + storage::{ + well_known_keys, + StorageKey, + }, Pair as _, }; use sp_keyring::AccountKeyring; diff --git a/subxt/src/client.rs b/subxt/src/client.rs index 241b06b727..df33032c6b 100644 --- a/subxt/src/client.rs +++ b/subxt/src/client.rs @@ -15,19 +15,37 @@ // along with subxt. If not, see . use futures::future; -pub use sp_runtime::traits::SignedExtension; use sp_runtime::traits::Hash; +pub use sp_runtime::traits::SignedExtension; use crate::{ - error::{BasicError, HasModuleError}, - extrinsic::{ExtrinsicParams, Signer}, - rpc::{Rpc, RpcClient, RuntimeVersion, SystemProperties}, + error::{ + BasicError, + HasModuleError, + }, + extrinsic::{ + ExtrinsicParams, + Signer, + }, + rpc::{ + Rpc, + RpcClient, + RuntimeVersion, + SystemProperties, + }, storage::StorageClient, transaction::TransactionProgress, updates::UpdateClient, - Call, Config, Encoded, Metadata, + Call, + Config, + Encoded, + Metadata, +}; +use codec::{ + Compact, + Decode, + Encode, }; -use codec::{Compact, Decode, Encode}; use derivative::Derivative; use parking_lot::RwLock; use std::sync::Arc; diff --git a/subxt/src/rpc.rs b/subxt/src/rpc.rs index 2b32e08ebe..d617103d83 100644 --- a/subxt/src/rpc.rs +++ b/subxt/src/rpc.rs @@ -21,16 +21,19 @@ // Related: https://github.com/paritytech/subxt/issues/66 #![allow(irrefutable_let_patterns)] -use std::{collections::HashMap, sync::Arc}; +use std::{ + collections::HashMap, + sync::Arc, +}; use crate::{ - error::BasicError, client::SignedSubmittableExtrinsic, + error::BasicError, extrinsic::ExtrinsicParams, Config, + HasModuleError, Metadata, PhantomDataSendSync, - HasModuleError }; use codec::{ Decode, @@ -39,25 +42,45 @@ use codec::{ use frame_metadata::RuntimeMetadataPrefixed; pub use jsonrpsee::{ client_transport::ws::{ - InvalidUri, Receiver as WsReceiver, Sender as WsSender, Uri, + InvalidUri, + Receiver as WsReceiver, + Sender as WsSender, + Uri, WsTransportClientBuilder, }, core::{ client::{ - Client as RpcClient, ClientBuilder as RpcClientBuilder, ClientT, - Subscription, SubscriptionClientT, + Client as RpcClient, + ClientBuilder as RpcClientBuilder, + ClientT, + Subscription, + SubscriptionClientT, }, - to_json_value, DeserializeOwned, Error as RpcError, JsonValue, + to_json_value, + DeserializeOwned, + Error as RpcError, + JsonValue, }, rpc_params, }; -use serde::{Deserialize, Serialize}; +use serde::{ + Deserialize, + Serialize, +}; use sp_core::{ - storage::{StorageChangeSet, StorageData, StorageKey}, - Bytes, U256, + storage::{ + StorageChangeSet, + StorageData, + StorageKey, + }, + Bytes, + U256, }; use sp_runtime::{ - generic::{Block, SignedBlock}, + generic::{ + Block, + SignedBlock, + }, ApplyExtrinsicResult, }; @@ -568,10 +591,8 @@ impl Rpc { E: Decode + HasModuleError, Evs: Decode, { - let params = rpc_params![ - Bytes::from(signed_submittable_extrinsic.encoded().0), - at - ]; + let params = + rpc_params![Bytes::from(signed_submittable_extrinsic.encoded().0), at]; let result_bytes: Bytes = self.client.request("system_dryRun", params).await?; let data: ApplyExtrinsicResult = codec::Decode::decode(&mut result_bytes.0.as_slice())?; From 6b62164b27b33e3263a43613d584c28c1f03a7bb Mon Sep 17 00:00:00 2001 From: Daniel Savu Date: Mon, 13 Jun 2022 17:41:58 +0100 Subject: [PATCH 5/8] chore(dryrun): Replace complex SubmittableExtrinsic type with bytes array --- integration-tests/src/client/mod.rs | 63 +++++++++++++---------------- subxt/src/client.rs | 33 ++++----------- subxt/src/rpc.rs | 62 ++++++++-------------------- 3 files changed, 51 insertions(+), 107 deletions(-) diff --git a/integration-tests/src/client/mod.rs b/integration-tests/src/client/mod.rs index 657ec56bb3..60886f5353 100644 --- a/integration-tests/src/client/mod.rs +++ b/integration-tests/src/client/mod.rs @@ -17,19 +17,13 @@ use crate::{ test_node_process, test_node_process_with, - utils::{ - node_runtime::system, - pair_signer, - test_context, - }, + utils::{node_runtime::system, pair_signer, test_context}, }; -use sp_core::{ - sr25519::Pair, - storage::{ - well_known_keys, - StorageKey, - }, +use sp_core::{storage::{ + well_known_keys, + StorageKey, +}, sr25519::Pair, Pair as _, }; use sp_keyring::AccountKeyring; @@ -156,26 +150,20 @@ async fn dry_run_passes() { let signed_extrinsic = api .tx() .balances() - .transfer(bob_address, 10_000) - .unwrap() + .transfer(bob_address, 10_000).unwrap() .create_signed(&alice, Default::default()) - .await - .unwrap(); + .await.unwrap(); - client - .rpc() - .dry_run(&signed_extrinsic, None) + client.rpc().dry_run(signed_extrinsic.encoded(), None) .await .expect("dryrunning failed") .expect("expected dryrunning to be successful") .unwrap(); signed_extrinsic .submit_and_watch() - .await - .unwrap() + .await.unwrap() .wait_for_finalized_success() - .await - .unwrap(); + .await.unwrap(); } #[tokio::test] @@ -184,40 +172,43 @@ async fn dry_run_fails() { let client = node_process.client(); let alice = pair_signer(AccountKeyring::Alice.pair()); + let alice_addr = alice.account_id().clone().into(); let hans = pair_signer(Pair::generate().0); let hans_address = hans.account_id().clone().into(); let cxt = test_context().await; let api = &cxt.api; - let signed_extrinsic = api - .tx() + api.tx() .balances() - .transfer( - hans_address, - 100_000_000_000_000_000_000_000_000_000_000_000, - ) + .transfer(hans_address, 100_000_000_000_000_000).unwrap() + .sign_and_submit_then_watch_default(&alice) + .await .unwrap() - .create_signed(&alice, Default::default()) + .wait_for_finalized_success() .await .unwrap(); - let dry_run_res: DispatchOutcome = client - .rpc() - .dry_run(&signed_extrinsic, None) + let signed_extrinsic = api + .tx() + .balances() + .transfer(alice_addr, 100_000_000_000_000_000).unwrap() + .create_signed(&hans, Default::default()) + .await.unwrap(); + + let dry_run_res: DispatchOutcome = client.rpc().dry_run(signed_extrinsic.encoded(), None) .await .expect("dryrunning failed") .expect("expected dryrun transaction to be valid"); - if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res { + if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res { assert_eq!(module_error.index, 6); assert_eq!(module_error.error, 2); } else { - panic!("expected a module error when dryrunning"); + panic!("expected a module module error when dryrunning"); } let res = signed_extrinsic .submit_and_watch() - .await - .unwrap() + .await.unwrap() .wait_for_finalized_success() .await; diff --git a/subxt/src/client.rs b/subxt/src/client.rs index df33032c6b..9ac4f4ad6c 100644 --- a/subxt/src/client.rs +++ b/subxt/src/client.rs @@ -19,33 +19,15 @@ use sp_runtime::traits::Hash; pub use sp_runtime::traits::SignedExtension; use crate::{ - error::{ - BasicError, - HasModuleError, - }, - extrinsic::{ - ExtrinsicParams, - Signer, - }, - rpc::{ - Rpc, - RpcClient, - RuntimeVersion, - SystemProperties, - }, + error::{BasicError, HasModuleError}, + extrinsic::{ExtrinsicParams, Signer}, + rpc::{Rpc, RpcClient, RuntimeVersion, SystemProperties}, storage::StorageClient, transaction::TransactionProgress, updates::UpdateClient, - Call, - Config, - Encoded, - Metadata, -}; -use codec::{ - Compact, - Decode, - Encode, + Call, Config, Encoded, Metadata, }; +use codec::{Compact, Decode, Encode}; use derivative::Derivative; use parking_lot::RwLock; use std::sync::Arc; @@ -447,7 +429,8 @@ where self.client.rpc().submit_extrinsic(&self.encoded).await } - pub fn encoded(&self) -> Encoded { - self.encoded.clone() + /// Returns the SCALE encoded extrinsic bytes. + pub fn encoded(&self) -> &[u8] { + &self.encoded.0 } } diff --git a/subxt/src/rpc.rs b/subxt/src/rpc.rs index d617103d83..dac9366e74 100644 --- a/subxt/src/rpc.rs +++ b/subxt/src/rpc.rs @@ -21,17 +21,11 @@ // Related: https://github.com/paritytech/subxt/issues/66 #![allow(irrefutable_let_patterns)] -use std::{ - collections::HashMap, - sync::Arc, -}; +use std::{collections::HashMap, sync::Arc}; use crate::{ - client::SignedSubmittableExtrinsic, error::BasicError, - extrinsic::ExtrinsicParams, Config, - HasModuleError, Metadata, PhantomDataSendSync, }; @@ -42,45 +36,25 @@ use codec::{ use frame_metadata::RuntimeMetadataPrefixed; pub use jsonrpsee::{ client_transport::ws::{ - InvalidUri, - Receiver as WsReceiver, - Sender as WsSender, - Uri, + InvalidUri, Receiver as WsReceiver, Sender as WsSender, Uri, WsTransportClientBuilder, }, core::{ client::{ - Client as RpcClient, - ClientBuilder as RpcClientBuilder, - ClientT, - Subscription, - SubscriptionClientT, + Client as RpcClient, ClientBuilder as RpcClientBuilder, ClientT, + Subscription, SubscriptionClientT, }, - to_json_value, - DeserializeOwned, - Error as RpcError, - JsonValue, + to_json_value, DeserializeOwned, Error as RpcError, JsonValue, }, rpc_params, }; -use serde::{ - Deserialize, - Serialize, -}; +use serde::{Deserialize, Serialize}; use sp_core::{ - storage::{ - StorageChangeSet, - StorageData, - StorageKey, - }, - Bytes, - U256, + storage::{StorageChangeSet, StorageData, StorageKey}, + Bytes, U256, }; use sp_runtime::{ - generic::{ - Block, - SignedBlock, - }, + generic::{Block, SignedBlock}, ApplyExtrinsicResult, }; @@ -580,19 +554,15 @@ impl Rpc { /// Submits the extrinsic to the dry_run RPC, to test if it would succeed. /// /// Returns `Ok` with an [`ApplyExtrinsicResult`], which is the result of applying of an extrinsic. - pub async fn dry_run<'client, Y, X, E, Evs>( + pub async fn dry_run( &self, - signed_submittable_extrinsic: &SignedSubmittableExtrinsic<'client, Y, X, E, Evs>, + encoded_signed: &[u8], at: Option, - ) -> Result - where - Y: Config, - X: ExtrinsicParams, - E: Decode + HasModuleError, - Evs: Decode, - { - let params = - rpc_params![Bytes::from(signed_submittable_extrinsic.encoded().0), at]; + ) -> Result { + let params = rpc_params![ + hex::encode(encoded_signed), + at + ]; let result_bytes: Bytes = self.client.request("system_dryRun", params).await?; let data: ApplyExtrinsicResult = codec::Decode::decode(&mut result_bytes.0.as_slice())?; From 7132c87afcbd6d5902e1f16ceb26b6bb0d929a45 Mon Sep 17 00:00:00 2001 From: Daniel Savu Date: Mon, 13 Jun 2022 17:45:29 +0100 Subject: [PATCH 6/8] cargo fmt --- integration-tests/src/client/mod.rs | 50 ++++++++++++++++++++--------- subxt/src/client.rs | 28 +++++++++++++--- subxt/src/rpc.rs | 46 ++++++++++++++++++-------- 3 files changed, 90 insertions(+), 34 deletions(-) diff --git a/integration-tests/src/client/mod.rs b/integration-tests/src/client/mod.rs index 60886f5353..04df959304 100644 --- a/integration-tests/src/client/mod.rs +++ b/integration-tests/src/client/mod.rs @@ -17,13 +17,19 @@ use crate::{ test_node_process, test_node_process_with, - utils::{node_runtime::system, pair_signer, test_context}, + utils::{ + node_runtime::system, + pair_signer, + test_context, + }, }; -use sp_core::{storage::{ - well_known_keys, - StorageKey, -}, sr25519::Pair, +use sp_core::{ + sr25519::Pair, + storage::{ + well_known_keys, + StorageKey, + }, Pair as _, }; use sp_keyring::AccountKeyring; @@ -150,20 +156,26 @@ async fn dry_run_passes() { let signed_extrinsic = api .tx() .balances() - .transfer(bob_address, 10_000).unwrap() + .transfer(bob_address, 10_000) + .unwrap() .create_signed(&alice, Default::default()) - .await.unwrap(); + .await + .unwrap(); - client.rpc().dry_run(signed_extrinsic.encoded(), None) + client + .rpc() + .dry_run(signed_extrinsic.encoded(), None) .await .expect("dryrunning failed") .expect("expected dryrunning to be successful") .unwrap(); signed_extrinsic .submit_and_watch() - .await.unwrap() + .await + .unwrap() .wait_for_finalized_success() - .await.unwrap(); + .await + .unwrap(); } #[tokio::test] @@ -180,7 +192,8 @@ async fn dry_run_fails() { api.tx() .balances() - .transfer(hans_address, 100_000_000_000_000_000).unwrap() + .transfer(hans_address, 100_000_000_000_000_000) + .unwrap() .sign_and_submit_then_watch_default(&alice) .await .unwrap() @@ -191,15 +204,19 @@ async fn dry_run_fails() { let signed_extrinsic = api .tx() .balances() - .transfer(alice_addr, 100_000_000_000_000_000).unwrap() + .transfer(alice_addr, 100_000_000_000_000_000) + .unwrap() .create_signed(&hans, Default::default()) - .await.unwrap(); + .await + .unwrap(); - let dry_run_res: DispatchOutcome = client.rpc().dry_run(signed_extrinsic.encoded(), None) + let dry_run_res: DispatchOutcome = client + .rpc() + .dry_run(signed_extrinsic.encoded(), None) .await .expect("dryrunning failed") .expect("expected dryrun transaction to be valid"); - if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res { + if let Err(sp_runtime::DispatchError::Module(module_error)) = dry_run_res { assert_eq!(module_error.index, 6); assert_eq!(module_error.error, 2); } else { @@ -208,7 +225,8 @@ async fn dry_run_fails() { let res = signed_extrinsic .submit_and_watch() - .await.unwrap() + .await + .unwrap() .wait_for_finalized_success() .await; diff --git a/subxt/src/client.rs b/subxt/src/client.rs index 9ac4f4ad6c..a113b3f9b4 100644 --- a/subxt/src/client.rs +++ b/subxt/src/client.rs @@ -19,15 +19,33 @@ use sp_runtime::traits::Hash; pub use sp_runtime::traits::SignedExtension; use crate::{ - error::{BasicError, HasModuleError}, - extrinsic::{ExtrinsicParams, Signer}, - rpc::{Rpc, RpcClient, RuntimeVersion, SystemProperties}, + error::{ + BasicError, + HasModuleError, + }, + extrinsic::{ + ExtrinsicParams, + Signer, + }, + rpc::{ + Rpc, + RpcClient, + RuntimeVersion, + SystemProperties, + }, storage::StorageClient, transaction::TransactionProgress, updates::UpdateClient, - Call, Config, Encoded, Metadata, + Call, + Config, + Encoded, + Metadata, +}; +use codec::{ + Compact, + Decode, + Encode, }; -use codec::{Compact, Decode, Encode}; use derivative::Derivative; use parking_lot::RwLock; use std::sync::Arc; diff --git a/subxt/src/rpc.rs b/subxt/src/rpc.rs index dac9366e74..1dc88e5dff 100644 --- a/subxt/src/rpc.rs +++ b/subxt/src/rpc.rs @@ -21,7 +21,10 @@ // Related: https://github.com/paritytech/subxt/issues/66 #![allow(irrefutable_let_patterns)] -use std::{collections::HashMap, sync::Arc}; +use std::{ + collections::HashMap, + sync::Arc, +}; use crate::{ error::BasicError, @@ -36,25 +39,45 @@ use codec::{ use frame_metadata::RuntimeMetadataPrefixed; pub use jsonrpsee::{ client_transport::ws::{ - InvalidUri, Receiver as WsReceiver, Sender as WsSender, Uri, + InvalidUri, + Receiver as WsReceiver, + Sender as WsSender, + Uri, WsTransportClientBuilder, }, core::{ client::{ - Client as RpcClient, ClientBuilder as RpcClientBuilder, ClientT, - Subscription, SubscriptionClientT, + Client as RpcClient, + ClientBuilder as RpcClientBuilder, + ClientT, + Subscription, + SubscriptionClientT, }, - to_json_value, DeserializeOwned, Error as RpcError, JsonValue, + to_json_value, + DeserializeOwned, + Error as RpcError, + JsonValue, }, rpc_params, }; -use serde::{Deserialize, Serialize}; +use serde::{ + Deserialize, + Serialize, +}; use sp_core::{ - storage::{StorageChangeSet, StorageData, StorageKey}, - Bytes, U256, + storage::{ + StorageChangeSet, + StorageData, + StorageKey, + }, + Bytes, + U256, }; use sp_runtime::{ - generic::{Block, SignedBlock}, + generic::{ + Block, + SignedBlock, + }, ApplyExtrinsicResult, }; @@ -559,10 +582,7 @@ impl Rpc { encoded_signed: &[u8], at: Option, ) -> Result { - let params = rpc_params![ - hex::encode(encoded_signed), - at - ]; + let params = rpc_params![hex::encode(encoded_signed), at]; let result_bytes: Bytes = self.client.request("system_dryRun", params).await?; let data: ApplyExtrinsicResult = codec::Decode::decode(&mut result_bytes.0.as_slice())?; From 1c037f5331430264e45db703573dc0e6adfd4819 Mon Sep 17 00:00:00 2001 From: Daniel Savu Date: Thu, 16 Jun 2022 16:35:36 +0100 Subject: [PATCH 7/8] feat: add dry_run method to signed submittable extrinsic --- integration-tests/src/client/mod.rs | 27 +++++++-------------------- subxt/src/client.rs | 9 ++++++++- subxt/src/rpc.rs | 5 ++++- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/integration-tests/src/client/mod.rs b/integration-tests/src/client/mod.rs index 04df959304..ea3461f112 100644 --- a/integration-tests/src/client/mod.rs +++ b/integration-tests/src/client/mod.rs @@ -152,7 +152,6 @@ async fn dry_run_passes() { let bob_address = bob.account_id().clone().into(); let cxt = test_context().await; let api = &cxt.api; - let signed_extrinsic = api .tx() .balances() @@ -184,29 +183,19 @@ async fn dry_run_fails() { let client = node_process.client(); let alice = pair_signer(AccountKeyring::Alice.pair()); - let alice_addr = alice.account_id().clone().into(); let hans = pair_signer(Pair::generate().0); let hans_address = hans.account_id().clone().into(); let cxt = test_context().await; let api = &cxt.api; - - api.tx() - .balances() - .transfer(hans_address, 100_000_000_000_000_000) - .unwrap() - .sign_and_submit_then_watch_default(&alice) - .await - .unwrap() - .wait_for_finalized_success() - .await - .unwrap(); - let signed_extrinsic = api .tx() .balances() - .transfer(alice_addr, 100_000_000_000_000_000) + .transfer( + hans_address, + 100_000_000_000_000_000_000_000_000_000_000_000, + ) .unwrap() - .create_signed(&hans, Default::default()) + .create_signed(&alice, Default::default()) .await .unwrap(); @@ -220,20 +209,18 @@ async fn dry_run_fails() { assert_eq!(module_error.index, 6); assert_eq!(module_error.error, 2); } else { - panic!("expected a module module error when dryrunning"); + panic!("expected a module error when dryrunning"); } - let res = signed_extrinsic .submit_and_watch() .await .unwrap() .wait_for_finalized_success() .await; - if let Err(Error::Module(err)) = res { assert_eq!(err.pallet, "Balances"); assert_eq!(err.error, "InsufficientBalance"); } else { panic!("expected a runtime module error"); } -} +} \ No newline at end of file diff --git a/subxt/src/client.rs b/subxt/src/client.rs index a113b3f9b4..42b84e8210 100644 --- a/subxt/src/client.rs +++ b/subxt/src/client.rs @@ -15,7 +15,7 @@ // along with subxt. If not, see . use futures::future; -use sp_runtime::traits::Hash; +use sp_runtime::{traits::Hash, ApplyExtrinsicResult}; pub use sp_runtime::traits::SignedExtension; use crate::{ @@ -447,6 +447,13 @@ where self.client.rpc().submit_extrinsic(&self.encoded).await } + /// Submits the extrinsic to the dry_run RPC, to test if it would succeed. + /// + /// Returns `Ok` with an [`ApplyExtrinsicResult`], which is the result of applying of an extrinsic. + pub async fn dry_run(&self, at: Option) -> Result { + self.client.rpc().dry_run(self.encoded(), at).await + } + /// Returns the SCALE encoded extrinsic bytes. pub fn encoded(&self) -> &[u8] { &self.encoded.0 diff --git a/subxt/src/rpc.rs b/subxt/src/rpc.rs index 1dc88e5dff..c8d9da3b21 100644 --- a/subxt/src/rpc.rs +++ b/subxt/src/rpc.rs @@ -582,7 +582,10 @@ impl Rpc { encoded_signed: &[u8], at: Option, ) -> Result { - let params = rpc_params![hex::encode(encoded_signed), at]; + let params = rpc_params![ + format!("0x{}", hex::encode(encoded_signed)), + at + ]; let result_bytes: Bytes = self.client.request("system_dryRun", params).await?; let data: ApplyExtrinsicResult = codec::Decode::decode(&mut result_bytes.0.as_slice())?; From 355c3407b7f947a9131480b94146c4c8b9e6a5e7 Mon Sep 17 00:00:00 2001 From: Daniel Savu Date: Fri, 17 Jun 2022 14:22:06 +0200 Subject: [PATCH 8/8] fmt --- integration-tests/src/client/mod.rs | 2 +- subxt/src/client.rs | 10 ++++++++-- subxt/src/rpc.rs | 5 +---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/integration-tests/src/client/mod.rs b/integration-tests/src/client/mod.rs index ea3461f112..f06708f05a 100644 --- a/integration-tests/src/client/mod.rs +++ b/integration-tests/src/client/mod.rs @@ -223,4 +223,4 @@ async fn dry_run_fails() { } else { panic!("expected a runtime module error"); } -} \ No newline at end of file +} diff --git a/subxt/src/client.rs b/subxt/src/client.rs index 42b84e8210..eaf3854ab8 100644 --- a/subxt/src/client.rs +++ b/subxt/src/client.rs @@ -15,8 +15,11 @@ // along with subxt. If not, see . use futures::future; -use sp_runtime::{traits::Hash, ApplyExtrinsicResult}; pub use sp_runtime::traits::SignedExtension; +use sp_runtime::{ + traits::Hash, + ApplyExtrinsicResult, +}; use crate::{ error::{ @@ -450,7 +453,10 @@ where /// Submits the extrinsic to the dry_run RPC, to test if it would succeed. /// /// Returns `Ok` with an [`ApplyExtrinsicResult`], which is the result of applying of an extrinsic. - pub async fn dry_run(&self, at: Option) -> Result { + pub async fn dry_run( + &self, + at: Option, + ) -> Result { self.client.rpc().dry_run(self.encoded(), at).await } diff --git a/subxt/src/rpc.rs b/subxt/src/rpc.rs index c8d9da3b21..73d99fe158 100644 --- a/subxt/src/rpc.rs +++ b/subxt/src/rpc.rs @@ -582,10 +582,7 @@ impl Rpc { encoded_signed: &[u8], at: Option, ) -> Result { - let params = rpc_params![ - format!("0x{}", hex::encode(encoded_signed)), - at - ]; + let params = rpc_params![format!("0x{}", hex::encode(encoded_signed)), at]; let result_bytes: Bytes = self.client.request("system_dryRun", params).await?; let data: ApplyExtrinsicResult = codec::Decode::decode(&mut result_bytes.0.as_slice())?;