mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-25 16:27:56 +00:00
Rework Subxt API to support offline and dynamic transactions (#593)
* WIP API changes * debug impls * Get main crate compiling with first round of changes * Some tidy up * Add WithExtrinsicParams, and have SubstrateConfig + PolkadotConfig, not DefaultConfig * move transaction into extrinsic folder * Add runtime updates back to OnlineClient * rework to be 'client first' to fit better with storage + events * add support for events to Client * tidy dupe trait bound * Wire storage into client, but need to remove static reliance * various tidy up and start stripping codegen to remove bits we dont need now * First pass updating calls and constants codegen * WIP storage client updates * First pass migrated runtime storage over to new format * pass over codegen to generate StorageAddresses and throw other stuff out * don't need a Call trait any more * shuffle things around a bit * Various proc_macro fixes to get 'cargo check' working * organise what's exposed from subxt * Get first example working; balance_transfer_with_params * get balance_transfer example compiling * get concurrent_storage_requests.rs example compiling * get fetch_all_accounts example compiling * get a bunch more of the examples compiling * almost get final example working; type mismatch to look into * wee tweaks * move StorageAddress to separate file * pass Defaultable/Iterable info to StorageAddress in codegen * fix storage validation ne, and partial run through example code * Remove static iteration and strip a generic param from everything * fix doc tests in subxt crate * update test utils and start fixing frame tests * fix frame staking tests * fix the rest of the test compile issues, Borrow on storage values * cargo fmt * remove extra logging during tests * Appease clippy and no more need for into_iter on events * cargo fmt * fix dryRun tests by waiting for blocks * wait for blocks instead of sleeping or other test hacks * cargo fmt * Fix doc links * Traitify StorageAddress * remove out-of-date doc comments * optimise decoding storage a little * cleanup tx stuff, trait for TxPayload, remove Err type param and decode at runtime * clippy fixes * fix doc links * fix doc example * constant address trait for consistency * fix a typo and remove EncodeWithMetadata stuff * Put EventDetails behind a proper interface and allow decoding into top level event, too * fix docs * tweak StorageAddress docs * re-export StorageAddress at root for consistency * fix clippy things * Add support for dynamic values * fix double encoding of storage map key after refactor * clippy fix * Fixes and add a dynamic usage example (needs new scale_value release) * bump scale_value version * cargo fmt * Tweak event bits * cargo fmt * Add a test and bump scale-value to 0.4.0 to support this * remove unnecessary vec from dynamic example * Various typo/grammar fixes Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> * Address PR nits * Undo accidental rename in changelog * Small PR nits/tidyups * fix tests; codegen change against latest substrate * tweak storage address util names * move error decoding to DecodeError and expose * impl some basic traits on the extrinsic param builder Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
@@ -4,10 +4,10 @@
|
||||
|
||||
use crate::{
|
||||
node_runtime::{
|
||||
self,
|
||||
balances,
|
||||
runtime_types,
|
||||
system,
|
||||
DispatchError,
|
||||
},
|
||||
pair_signer,
|
||||
test_context,
|
||||
@@ -22,32 +22,36 @@ use sp_runtime::{
|
||||
AccountId32,
|
||||
MultiAddress,
|
||||
};
|
||||
use subxt::Error;
|
||||
use subxt::error::{
|
||||
DispatchError,
|
||||
Error,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn tx_basic_transfer() -> Result<(), subxt::Error<DispatchError>> {
|
||||
async fn tx_basic_transfer() -> 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 ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice_account_addr = node_runtime::storage().system().account(alice.account_id());
|
||||
let bob_account_addr = node_runtime::storage().system().account(bob.account_id());
|
||||
|
||||
let alice_pre = api
|
||||
.storage()
|
||||
.system()
|
||||
.account(alice.account_id(), None)
|
||||
.fetch_or_default(&alice_account_addr, None)
|
||||
.await?;
|
||||
let bob_pre = api
|
||||
.storage()
|
||||
.system()
|
||||
.account(bob.account_id(), None)
|
||||
.fetch_or_default(&bob_account_addr, None)
|
||||
.await?;
|
||||
|
||||
let tx = node_runtime::tx().balances().transfer(bob_address, 10_000);
|
||||
|
||||
let events = api
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(bob_address, 10_000)?
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -69,13 +73,11 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error<DispatchError>> {
|
||||
|
||||
let alice_post = api
|
||||
.storage()
|
||||
.system()
|
||||
.account(alice.account_id(), None)
|
||||
.fetch_or_default(&alice_account_addr, None)
|
||||
.await?;
|
||||
let bob_post = api
|
||||
.storage()
|
||||
.system()
|
||||
.account(bob.account_id(), None)
|
||||
.fetch_or_default(&bob_account_addr, None)
|
||||
.await?;
|
||||
|
||||
assert!(alice_pre.data.free - 10_000 >= alice_post.data.free);
|
||||
@@ -84,26 +86,118 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error<DispatchError>> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multiple_transfers_work_nonce_incremented(
|
||||
) -> Result<(), subxt::Error<DispatchError>> {
|
||||
async fn tx_dynamic_transfer() -> Result<(), subxt::Error> {
|
||||
use subxt::ext::scale_value::{
|
||||
At,
|
||||
Composite,
|
||||
Value,
|
||||
};
|
||||
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let bob = pair_signer(AccountKeyring::Bob.pair());
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice_account_addr = subxt::dynamic::storage(
|
||||
"System",
|
||||
"Account",
|
||||
vec![Value::from_bytes(&alice.account_id())],
|
||||
);
|
||||
let bob_account_addr = subxt::dynamic::storage(
|
||||
"System",
|
||||
"Account",
|
||||
vec![Value::from_bytes(&bob.account_id())],
|
||||
);
|
||||
|
||||
let alice_pre = api
|
||||
.storage()
|
||||
.fetch_or_default(&alice_account_addr, None)
|
||||
.await?;
|
||||
let bob_pre = api
|
||||
.storage()
|
||||
.fetch_or_default(&bob_account_addr, None)
|
||||
.await?;
|
||||
|
||||
let tx = subxt::dynamic::tx(
|
||||
"Balances",
|
||||
"transfer",
|
||||
vec![
|
||||
Value::unnamed_variant("Id", vec![Value::from_bytes(&bob.account_id())]),
|
||||
Value::u128(10_000u128),
|
||||
],
|
||||
);
|
||||
|
||||
let events = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
|
||||
let event_fields = 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(|_| ());
|
||||
|
||||
let expected_fields = Composite::Named(vec![
|
||||
(
|
||||
"from".into(),
|
||||
Value::unnamed_composite(vec![Value::from_bytes(&alice.account_id())]),
|
||||
),
|
||||
(
|
||||
"to".into(),
|
||||
Value::unnamed_composite(vec![Value::from_bytes(&bob.account_id())]),
|
||||
),
|
||||
("amount".into(), Value::u128(10_000)),
|
||||
]);
|
||||
assert_eq!(event_fields, expected_fields);
|
||||
|
||||
let alice_post = api
|
||||
.storage()
|
||||
.fetch_or_default(&alice_account_addr, None)
|
||||
.await?;
|
||||
let bob_post = api
|
||||
.storage()
|
||||
.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 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);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multiple_transfers_work_nonce_incremented() -> Result<(), subxt::Error> {
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let bob = pair_signer(AccountKeyring::Bob.pair());
|
||||
let bob_address: MultiAddress<AccountId32, u32> = bob.account_id().clone().into();
|
||||
let cxt = test_context().await;
|
||||
let api = &cxt.api;
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let bob_account_addr = node_runtime::storage().system().account(bob.account_id());
|
||||
|
||||
let bob_pre = api
|
||||
.storage()
|
||||
.system()
|
||||
.account(bob.account_id(), None)
|
||||
.fetch_or_default(&bob_account_addr, None)
|
||||
.await?;
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.balances()
|
||||
.transfer(bob_address.clone(), 10_000);
|
||||
for _ in 0..3 {
|
||||
api
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(bob_address.clone(), 10_000)?
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await?
|
||||
.wait_for_in_block() // Don't need to wait for finalization; this is quicker.
|
||||
.await?
|
||||
@@ -113,8 +207,7 @@ async fn multiple_transfers_work_nonce_incremented(
|
||||
|
||||
let bob_post = api
|
||||
.storage()
|
||||
.system()
|
||||
.account(bob.account_id(), None)
|
||||
.fetch_or_default(&bob_account_addr, None)
|
||||
.await?;
|
||||
|
||||
assert_eq!(bob_pre.data.free + 30_000, bob_post.data.free);
|
||||
@@ -123,45 +216,40 @@ async fn multiple_transfers_work_nonce_incremented(
|
||||
|
||||
#[tokio::test]
|
||||
async fn storage_total_issuance() {
|
||||
let cxt = test_context().await;
|
||||
let total_issuance = cxt
|
||||
.api
|
||||
.storage()
|
||||
.balances()
|
||||
.total_issuance(None)
|
||||
.await
|
||||
.unwrap();
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let addr = node_runtime::storage().balances().total_issuance();
|
||||
let total_issuance = api.storage().fetch_or_default(&addr, None).await.unwrap();
|
||||
assert_ne!(total_issuance, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn storage_balance_lock() -> Result<(), subxt::Error<DispatchError>> {
|
||||
async fn storage_balance_lock() -> Result<(), subxt::Error> {
|
||||
let bob = pair_signer(AccountKeyring::Bob.pair());
|
||||
let charlie = AccountKeyring::Charlie.to_account_id();
|
||||
let cxt = test_context().await;
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
cxt.api
|
||||
.tx()
|
||||
.staking()
|
||||
.bond(
|
||||
charlie.into(),
|
||||
100_000_000_000_000,
|
||||
runtime_types::pallet_staking::RewardDestination::Stash,
|
||||
)?
|
||||
.sign_and_submit_then_watch_default(&bob)
|
||||
let tx = node_runtime::tx().staking().bond(
|
||||
charlie.into(),
|
||||
100_000_000_000_000,
|
||||
runtime_types::pallet_staking::RewardDestination::Stash,
|
||||
);
|
||||
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &bob)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
.find_first::<system::events::ExtrinsicSuccess>()?
|
||||
.expect("No ExtrinsicSuccess Event found");
|
||||
|
||||
let locked_account = AccountKeyring::Bob.to_account_id();
|
||||
let locks = cxt
|
||||
.api
|
||||
.storage()
|
||||
let locks_addr = node_runtime::storage()
|
||||
.balances()
|
||||
.locks(&locked_account, None)
|
||||
.await?;
|
||||
.locks(&AccountKeyring::Bob.to_account_id());
|
||||
|
||||
let locks = api.storage().fetch_or_default(&locks_addr, None).await?;
|
||||
|
||||
assert_eq!(
|
||||
locks.0,
|
||||
@@ -177,38 +265,37 @@ async fn storage_balance_lock() -> Result<(), subxt::Error<DispatchError>> {
|
||||
|
||||
#[tokio::test]
|
||||
async fn transfer_error() {
|
||||
tracing_subscriber::fmt::try_init().ok();
|
||||
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 ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
ctx.api
|
||||
.tx()
|
||||
let to_hans_tx = node_runtime::tx()
|
||||
.balances()
|
||||
.transfer(hans_address, 100_000_000_000_000_000)
|
||||
.unwrap()
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.transfer(hans_address, 100_000_000_000_000_000);
|
||||
let to_alice_tx = node_runtime::tx()
|
||||
.balances()
|
||||
.transfer(alice_addr, 100_000_000_000_000_000);
|
||||
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&to_hans_tx, &alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let res = ctx
|
||||
.api
|
||||
let res = api
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(alice_addr, 100_000_000_000_000_000)
|
||||
.unwrap()
|
||||
.sign_and_submit_then_watch_default(&hans)
|
||||
.sign_and_submit_then_watch_default(&to_alice_tx, &hans)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
|
||||
if let Err(Error::Module(err)) = res {
|
||||
if let Err(Error::Runtime(DispatchError::Module(err))) = res {
|
||||
assert_eq!(err.pallet, "Balances");
|
||||
assert_eq!(err.error, "InsufficientBalance");
|
||||
} else {
|
||||
@@ -218,19 +305,17 @@ async fn transfer_error() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn transfer_implicit_subscription() {
|
||||
tracing_subscriber::fmt::try_init().ok();
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let bob = AccountKeyring::Bob.to_account_id();
|
||||
let bob_addr = bob.clone().into();
|
||||
let cxt = test_context().await;
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let event = cxt
|
||||
.api
|
||||
let to_bob_tx = node_runtime::tx().balances().transfer(bob_addr, 10_000);
|
||||
|
||||
let event = api
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(bob_addr, 10_000)
|
||||
.unwrap()
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.sign_and_submit_then_watch_default(&to_bob_tx, &alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
@@ -252,19 +337,19 @@ async fn transfer_implicit_subscription() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn constant_existential_deposit() {
|
||||
let cxt = test_context().await;
|
||||
let locked_metadata = cxt.client().metadata();
|
||||
let metadata = locked_metadata.read();
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
// get and decode constant manually via metadata:
|
||||
let metadata = api.metadata();
|
||||
let balances_metadata = metadata.pallet("Balances").unwrap();
|
||||
let constant_metadata = balances_metadata.constant("ExistentialDeposit").unwrap();
|
||||
let existential_deposit = u128::decode(&mut &constant_metadata.value[..]).unwrap();
|
||||
assert_eq!(existential_deposit, 100_000_000_000_000);
|
||||
assert_eq!(
|
||||
existential_deposit,
|
||||
cxt.api
|
||||
.constants()
|
||||
.balances()
|
||||
.existential_deposit()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
// constant address for API access:
|
||||
let addr = node_runtime::constants().balances().existential_deposit();
|
||||
|
||||
// Make sure thetwo are identical:
|
||||
assert_eq!(existential_deposit, api.constants().at(&addr).unwrap());
|
||||
}
|
||||
|
||||
@@ -7,57 +7,46 @@ use sp_keyring::AccountKeyring;
|
||||
use crate::{
|
||||
node_runtime::{
|
||||
self,
|
||||
contracts::{
|
||||
calls::TransactionApi,
|
||||
events,
|
||||
storage,
|
||||
},
|
||||
contracts::events,
|
||||
system,
|
||||
DispatchError,
|
||||
},
|
||||
test_context,
|
||||
NodeRuntimeParams,
|
||||
TestContext,
|
||||
};
|
||||
use sp_core::sr25519::Pair;
|
||||
use sp_runtime::MultiAddress;
|
||||
use subxt::{
|
||||
Client,
|
||||
tx::{
|
||||
PairSigner,
|
||||
TxProgress,
|
||||
},
|
||||
Config,
|
||||
DefaultConfig,
|
||||
Error,
|
||||
PairSigner,
|
||||
TransactionProgress,
|
||||
OnlineClient,
|
||||
SubstrateConfig,
|
||||
};
|
||||
|
||||
struct ContractsTestContext {
|
||||
cxt: TestContext,
|
||||
signer: PairSigner<DefaultConfig, Pair>,
|
||||
signer: PairSigner<SubstrateConfig, Pair>,
|
||||
}
|
||||
|
||||
type Hash = <DefaultConfig as Config>::Hash;
|
||||
type AccountId = <DefaultConfig as Config>::AccountId;
|
||||
type Hash = <SubstrateConfig as Config>::Hash;
|
||||
type AccountId = <SubstrateConfig as Config>::AccountId;
|
||||
|
||||
impl ContractsTestContext {
|
||||
async fn init() -> Self {
|
||||
tracing_subscriber::fmt::try_init().ok();
|
||||
let cxt = test_context().await;
|
||||
let signer = PairSigner::new(AccountKeyring::Alice.pair());
|
||||
|
||||
Self { cxt, signer }
|
||||
}
|
||||
|
||||
fn client(&self) -> &Client<DefaultConfig> {
|
||||
fn client(&self) -> OnlineClient<SubstrateConfig> {
|
||||
self.cxt.client()
|
||||
}
|
||||
|
||||
fn contracts_tx(&self) -> TransactionApi<DefaultConfig, NodeRuntimeParams> {
|
||||
self.cxt.api.tx().contracts()
|
||||
}
|
||||
|
||||
async fn instantiate_with_code(
|
||||
&self,
|
||||
) -> Result<(Hash, AccountId), Error<DispatchError>> {
|
||||
async fn instantiate_with_code(&self) -> Result<(Hash, AccountId), Error> {
|
||||
tracing::info!("instantiate_with_code:");
|
||||
const CONTRACT: &str = r#"
|
||||
(module
|
||||
@@ -67,20 +56,19 @@ impl ContractsTestContext {
|
||||
"#;
|
||||
let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt");
|
||||
|
||||
let instantiate_tx = node_runtime::tx().contracts().instantiate_with_code(
|
||||
100_000_000_000_000_000, // endowment
|
||||
500_000_000_000, // gas_limit
|
||||
None, // storage_deposit_limit
|
||||
code,
|
||||
vec![], // data
|
||||
vec![], // salt
|
||||
);
|
||||
|
||||
let events = self
|
||||
.cxt
|
||||
.api
|
||||
.client()
|
||||
.tx()
|
||||
.contracts()
|
||||
.instantiate_with_code(
|
||||
100_000_000_000_000_000, // endowment
|
||||
500_000_000_000, // gas_limit
|
||||
None, // storage_deposit_limit
|
||||
code,
|
||||
vec![], // data
|
||||
vec![], // salt
|
||||
)?
|
||||
.sign_and_submit_then_watch_default(&self.signer)
|
||||
.sign_and_submit_then_watch_default(&instantiate_tx, &self.signer)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -108,19 +96,21 @@ impl ContractsTestContext {
|
||||
code_hash: Hash,
|
||||
data: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
) -> Result<AccountId, Error<DispatchError>> {
|
||||
) -> Result<AccountId, Error> {
|
||||
// call instantiate extrinsic
|
||||
let instantiate_tx = node_runtime::tx().contracts().instantiate(
|
||||
100_000_000_000_000_000, // endowment
|
||||
500_000_000_000, // gas_limit
|
||||
None, // storage_deposit_limit
|
||||
code_hash,
|
||||
data,
|
||||
salt,
|
||||
);
|
||||
|
||||
let result = self
|
||||
.contracts_tx()
|
||||
.instantiate(
|
||||
100_000_000_000_000_000, // endowment
|
||||
500_000_000_000, // gas_limit
|
||||
None, // storage_deposit_limit
|
||||
code_hash,
|
||||
data,
|
||||
salt,
|
||||
)?
|
||||
.sign_and_submit_then_watch_default(&self.signer)
|
||||
.client()
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&instantiate_tx, &self.signer)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -137,21 +127,20 @@ impl ContractsTestContext {
|
||||
&self,
|
||||
contract: AccountId,
|
||||
input_data: Vec<u8>,
|
||||
) -> Result<
|
||||
TransactionProgress<'_, DefaultConfig, DispatchError, node_runtime::Event>,
|
||||
Error<DispatchError>,
|
||||
> {
|
||||
) -> Result<TxProgress<SubstrateConfig, OnlineClient<SubstrateConfig>>, Error> {
|
||||
tracing::info!("call: {:?}", contract);
|
||||
let call_tx = node_runtime::tx().contracts().call(
|
||||
MultiAddress::Id(contract),
|
||||
0, // value
|
||||
500_000_000, // gas_limit
|
||||
None, // storage_deposit_limit
|
||||
input_data,
|
||||
);
|
||||
|
||||
let result = self
|
||||
.contracts_tx()
|
||||
.call(
|
||||
MultiAddress::Id(contract),
|
||||
0, // value
|
||||
500_000_000, // gas_limit
|
||||
None, // storage_deposit_limit
|
||||
input_data,
|
||||
)?
|
||||
.sign_and_submit_then_watch_default(&self.signer)
|
||||
.client()
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&call_tx, &self.signer)
|
||||
.await?;
|
||||
|
||||
tracing::info!("Call result: {:?}", result);
|
||||
@@ -190,19 +179,17 @@ async fn tx_call() {
|
||||
let cxt = ContractsTestContext::init().await;
|
||||
let (_, contract) = cxt.instantiate_with_code().await.unwrap();
|
||||
|
||||
let contract_info = cxt
|
||||
.cxt
|
||||
.api
|
||||
.storage()
|
||||
let info_addr = node_runtime::storage()
|
||||
.contracts()
|
||||
.contract_info_of(&contract, None)
|
||||
.await;
|
||||
.contract_info_of(&contract);
|
||||
|
||||
let contract_info = cxt.client().storage().fetch(&info_addr, None).await;
|
||||
assert!(contract_info.is_ok());
|
||||
|
||||
let keys = cxt
|
||||
.client()
|
||||
.storage()
|
||||
.fetch_keys::<storage::ContractInfoOf>(5, None, None)
|
||||
.fetch_keys(&info_addr.to_bytes(), 10, None, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.iter()
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
use crate::{
|
||||
node_runtime::{
|
||||
self,
|
||||
runtime_types::pallet_staking::{
|
||||
RewardDestination,
|
||||
ValidatorPrefs,
|
||||
},
|
||||
staking,
|
||||
DispatchError,
|
||||
},
|
||||
pair_signer,
|
||||
test_context,
|
||||
@@ -20,7 +20,10 @@ use sp_core::{
|
||||
Pair,
|
||||
};
|
||||
use sp_keyring::AccountKeyring;
|
||||
use subxt::Error;
|
||||
use subxt::error::{
|
||||
DispatchError,
|
||||
Error,
|
||||
};
|
||||
|
||||
/// Helper function to generate a crypto pair from seed
|
||||
fn get_from_seed(seed: &str) -> sr25519::Pair {
|
||||
@@ -37,14 +40,17 @@ fn default_validator_prefs() -> ValidatorPrefs {
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_with_controller_account() {
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let ctx = test_context().await;
|
||||
ctx.api
|
||||
.tx()
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.validate(default_validator_prefs())
|
||||
.unwrap()
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.validate(default_validator_prefs());
|
||||
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
@@ -53,19 +59,23 @@ async fn validate_with_controller_account() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_not_possible_for_stash_account() -> Result<(), Error<DispatchError>> {
|
||||
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
|
||||
async fn validate_not_possible_for_stash_account() -> Result<(), Error> {
|
||||
let ctx = test_context().await;
|
||||
let announce_validator = ctx
|
||||
.api
|
||||
.tx()
|
||||
let api = ctx.client();
|
||||
|
||||
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.validate(default_validator_prefs())?
|
||||
.sign_and_submit_then_watch_default(&alice_stash)
|
||||
.validate(default_validator_prefs());
|
||||
|
||||
let announce_validator = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice_stash)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
assert_matches!(announce_validator, Err(Error::Module(err)) => {
|
||||
assert_matches!(announce_validator, Err(Error::Runtime(DispatchError::Module(err))) => {
|
||||
assert_eq!(err.pallet, "Staking");
|
||||
assert_eq!(err.error, "NotController");
|
||||
});
|
||||
@@ -74,16 +84,18 @@ async fn validate_not_possible_for_stash_account() -> Result<(), Error<DispatchE
|
||||
|
||||
#[tokio::test]
|
||||
async fn nominate_with_controller_account() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let bob = pair_signer(AccountKeyring::Bob.pair());
|
||||
let ctx = test_context().await;
|
||||
|
||||
ctx.api
|
||||
.tx()
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.nominate(vec![bob.account_id().clone().into()])
|
||||
.unwrap()
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.nominate(vec![bob.account_id().clone().into()]);
|
||||
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
@@ -92,22 +104,26 @@ async fn nominate_with_controller_account() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn nominate_not_possible_for_stash_account() -> Result<(), Error<DispatchError>> {
|
||||
async fn nominate_not_possible_for_stash_account() -> Result<(), Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
|
||||
let bob = pair_signer(AccountKeyring::Bob.pair());
|
||||
let ctx = test_context().await;
|
||||
|
||||
let nomination = ctx
|
||||
.api
|
||||
.tx()
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.nominate(vec![bob.account_id().clone().into()])?
|
||||
.sign_and_submit_then_watch_default(&alice_stash)
|
||||
.await?
|
||||
.nominate(vec![bob.account_id().clone().into()]);
|
||||
|
||||
let nomination = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice_stash)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
|
||||
assert_matches!(nomination, Err(Error::Module(err)) => {
|
||||
assert_matches!(nomination, Err(Error::Runtime(DispatchError::Module(err))) => {
|
||||
assert_eq!(err.pallet, "Staking");
|
||||
assert_eq!(err.error, "NotController");
|
||||
});
|
||||
@@ -115,52 +131,45 @@ async fn nominate_not_possible_for_stash_account() -> Result<(), Error<DispatchE
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn chill_works_for_controller_only() -> Result<(), Error<DispatchError>> {
|
||||
async fn chill_works_for_controller_only() -> Result<(), Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
|
||||
let bob_stash = pair_signer(get_from_seed("Bob//stash"));
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let ctx = test_context().await;
|
||||
|
||||
// this will fail the second time, which is why this is one test, not two
|
||||
ctx.api
|
||||
.tx()
|
||||
let nominate_tx = node_runtime::tx()
|
||||
.staking()
|
||||
.nominate(vec![bob_stash.account_id().clone().into()])?
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.nominate(vec![bob_stash.account_id().clone().into()]);
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&nominate_tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
|
||||
let ledger = ctx
|
||||
.api
|
||||
.storage()
|
||||
.staking()
|
||||
.ledger(alice.account_id(), None)
|
||||
.await?
|
||||
.unwrap();
|
||||
let ledger_addr = node_runtime::storage().staking().ledger(alice.account_id());
|
||||
let ledger = api.storage().fetch(&ledger_addr, None).await?.unwrap();
|
||||
assert_eq!(alice_stash.account_id(), &ledger.stash);
|
||||
|
||||
let chill = ctx
|
||||
.api
|
||||
let chill_tx = node_runtime::tx().staking().chill();
|
||||
|
||||
let chill = api
|
||||
.tx()
|
||||
.staking()
|
||||
.chill()?
|
||||
.sign_and_submit_then_watch_default(&alice_stash)
|
||||
.sign_and_submit_then_watch_default(&chill_tx, &alice_stash)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
|
||||
assert_matches!(chill, Err(Error::Module(err)) => {
|
||||
assert_matches!(chill, Err(Error::Runtime(DispatchError::Module(err))) => {
|
||||
assert_eq!(err.pallet, "Staking");
|
||||
assert_eq!(err.error, "NotController");
|
||||
});
|
||||
|
||||
let is_chilled = ctx
|
||||
.api
|
||||
let is_chilled = api
|
||||
.tx()
|
||||
.staking()
|
||||
.chill()?
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.sign_and_submit_then_watch_default(&chill_tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
@@ -171,43 +180,35 @@ async fn chill_works_for_controller_only() -> Result<(), Error<DispatchError>> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn tx_bond() -> Result<(), Error<DispatchError>> {
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
async fn tx_bond() -> Result<(), Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let bond = ctx
|
||||
.api
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
|
||||
let bond_tx = node_runtime::tx().staking().bond(
|
||||
AccountKeyring::Bob.to_account_id().into(),
|
||||
100_000_000_000_000,
|
||||
RewardDestination::Stash,
|
||||
);
|
||||
|
||||
let bond = api
|
||||
.tx()
|
||||
.staking()
|
||||
.bond(
|
||||
AccountKeyring::Bob.to_account_id().into(),
|
||||
100_000_000_000_000,
|
||||
RewardDestination::Stash,
|
||||
)
|
||||
.unwrap()
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.sign_and_submit_then_watch_default(&bond_tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
|
||||
assert!(bond.is_ok());
|
||||
|
||||
let bond_again = ctx
|
||||
.api
|
||||
let bond_again = api
|
||||
.tx()
|
||||
.staking()
|
||||
.bond(
|
||||
AccountKeyring::Bob.to_account_id().into(),
|
||||
100_000_000_000_000,
|
||||
RewardDestination::Stash,
|
||||
)
|
||||
.unwrap()
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.sign_and_submit_then_watch_default(&bond_tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
|
||||
assert_matches!(bond_again, Err(Error::Module(err)) => {
|
||||
assert_matches!(bond_again, Err(Error::Runtime(DispatchError::Module(err))) => {
|
||||
assert_eq!(err.pallet, "Staking");
|
||||
assert_eq!(err.error, "AlreadyBonded");
|
||||
});
|
||||
@@ -215,35 +216,37 @@ async fn tx_bond() -> Result<(), Error<DispatchError>> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn storage_history_depth() -> Result<(), Error<DispatchError>> {
|
||||
async fn storage_history_depth() -> Result<(), Error> {
|
||||
let ctx = test_context().await;
|
||||
let history_depth = ctx.api.storage().staking().history_depth(None).await?;
|
||||
let api = ctx.client();
|
||||
let history_depth_addr = node_runtime::storage().staking().history_depth();
|
||||
let history_depth = api
|
||||
.storage()
|
||||
.fetch_or_default(&history_depth_addr, None)
|
||||
.await?;
|
||||
assert_eq!(history_depth, 84);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn storage_current_era() -> Result<(), Error<DispatchError>> {
|
||||
async fn storage_current_era() -> Result<(), Error> {
|
||||
let ctx = test_context().await;
|
||||
let _current_era = ctx
|
||||
.api
|
||||
let api = ctx.client();
|
||||
let current_era_addr = node_runtime::storage().staking().current_era();
|
||||
let _current_era = api
|
||||
.storage()
|
||||
.staking()
|
||||
.current_era(None)
|
||||
.fetch(¤t_era_addr, None)
|
||||
.await?
|
||||
.expect("current era always exists");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn storage_era_reward_points() -> Result<(), Error<DispatchError>> {
|
||||
let cxt = test_context().await;
|
||||
let current_era_result = cxt
|
||||
.api
|
||||
.storage()
|
||||
.staking()
|
||||
.eras_reward_points(&0, None)
|
||||
.await;
|
||||
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 current_era_result = api.storage().fetch(&reward_points_addr, None).await;
|
||||
assert!(current_era_result.is_ok());
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -4,35 +4,35 @@
|
||||
|
||||
use crate::{
|
||||
node_runtime::{
|
||||
self,
|
||||
runtime_types,
|
||||
sudo,
|
||||
DispatchError,
|
||||
},
|
||||
pair_signer,
|
||||
test_context,
|
||||
};
|
||||
use sp_keyring::AccountKeyring;
|
||||
|
||||
type Call = runtime_types::node_runtime::Call;
|
||||
type Call = runtime_types::kitchensink_runtime::Call;
|
||||
type BalancesCall = runtime_types::pallet_balances::pallet::Call;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sudo() -> Result<(), subxt::Error<DispatchError>> {
|
||||
async fn test_sudo() -> Result<(), subxt::Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let bob = AccountKeyring::Bob.to_account_id().into();
|
||||
let cxt = test_context().await;
|
||||
|
||||
let call = Call::Balances(BalancesCall::transfer {
|
||||
dest: bob,
|
||||
value: 10_000,
|
||||
});
|
||||
let tx = node_runtime::tx().sudo().sudo(call);
|
||||
|
||||
let found_event = cxt
|
||||
.api
|
||||
let found_event = api
|
||||
.tx()
|
||||
.sudo()
|
||||
.sudo(call)?
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
@@ -43,22 +43,22 @@ async fn test_sudo() -> Result<(), subxt::Error<DispatchError>> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error<DispatchError>> {
|
||||
async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let bob = AccountKeyring::Bob.to_account_id().into();
|
||||
let cxt = test_context().await;
|
||||
|
||||
let call = Call::Balances(BalancesCall::transfer {
|
||||
dest: bob,
|
||||
value: 10_000,
|
||||
});
|
||||
let tx = node_runtime::tx().sudo().sudo_unchecked_weight(call, 0);
|
||||
|
||||
let found_event = cxt
|
||||
.api
|
||||
let found_event = api
|
||||
.tx()
|
||||
.sudo()
|
||||
.sudo_unchecked_weight(call, 0)?
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
use crate::{
|
||||
node_runtime::{
|
||||
self,
|
||||
system,
|
||||
DispatchError,
|
||||
},
|
||||
pair_signer,
|
||||
test_context,
|
||||
@@ -14,15 +14,17 @@ use assert_matches::assert_matches;
|
||||
use sp_keyring::AccountKeyring;
|
||||
|
||||
#[tokio::test]
|
||||
async fn storage_account() -> Result<(), subxt::Error<DispatchError>> {
|
||||
async fn storage_account() -> Result<(), subxt::Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
|
||||
let cxt = test_context().await;
|
||||
let account_info = cxt
|
||||
.api
|
||||
let account_info_addr = node_runtime::storage().system().account(alice.account_id());
|
||||
|
||||
let account_info = api
|
||||
.storage()
|
||||
.system()
|
||||
.account(alice.account_id(), None)
|
||||
.fetch_or_default(&account_info_addr, None)
|
||||
.await;
|
||||
|
||||
assert_matches!(account_info, Ok(_));
|
||||
@@ -30,16 +32,19 @@ async fn storage_account() -> Result<(), subxt::Error<DispatchError>> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn tx_remark_with_event() -> Result<(), subxt::Error<DispatchError>> {
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let cxt = test_context().await;
|
||||
async fn tx_remark_with_event() -> Result<(), subxt::Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let found_event = cxt
|
||||
.api
|
||||
.tx()
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.system()
|
||||
.remark_with_event(b"remarkable".to_vec())?
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.remark_with_event(b"remarkable".to_vec());
|
||||
|
||||
let found_event = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
|
||||
@@ -2,13 +2,20 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::test_context;
|
||||
use crate::{
|
||||
node_runtime,
|
||||
test_context,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn storage_get_current_timestamp() {
|
||||
let cxt = test_context().await;
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let timestamp = cxt.api.storage().timestamp().now(None).await;
|
||||
let timestamp = api
|
||||
.storage()
|
||||
.fetch(&node_runtime::storage().timestamp().now(), None)
|
||||
.await;
|
||||
|
||||
assert!(timestamp.is_ok())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user