diff --git a/crates/e2e/src/client.rs b/crates/e2e/src/client.rs index d38433e6e4c..5dd976b1917 100644 --- a/crates/e2e/src/client.rs +++ b/crates/e2e/src/client.rs @@ -21,10 +21,6 @@ use super::{ log_error, log_info, sr25519, - xts::{ - Call, - InstantiateWithCode, - }, CodeUploadResult, ContractExecResult, ContractInstantiateResult, @@ -35,6 +31,7 @@ use contract_metadata::ContractMetadata; use ink_env::Environment; use ink_primitives::MessageResult; +use sp_core::Pair; use sp_runtime::traits::{ IdentifyAccount, Verify, @@ -55,7 +52,10 @@ use subxt::{ ValueDef, }, }, - tx::ExtrinsicParams, + tx::{ + ExtrinsicParams, + PairSigner, + }, }; /// Result of a contract instantiation. @@ -300,11 +300,8 @@ where E: Environment, E::AccountId: Debug, - E::Balance: Debug + scale::Encode + serde::Serialize, + E::Balance: Debug + scale::HasCompact + serde::Serialize, E::Hash: Debug + scale::Encode, - - Call: scale::Encode, - InstantiateWithCode: scale::Encode, { /// Creates a new [`Client`] instance. pub async fn new(url: &str, contracts: impl IntoIterator) -> Self { @@ -342,6 +339,52 @@ where } } + /// Generate a new keypair and fund with the given amount from the origin account. + /// + /// Because many tests may execute this in parallel, transfers may fail due to a race condition + /// with account indices. Therefore this will reattempt transfers a number of times. + pub async fn create_and_fund_account( + &self, + origin: &Signer, + amount: E::Balance, + ) -> Signer + where + E::Balance: Clone, + C::AccountId: Clone + core::fmt::Display, + { + let (pair, _, _) = ::generate_with_phrase(None); + let account_id = + ::Signer::from(pair.public()).into_account(); + + for _ in 0..6 { + let transfer_result = self + .api + .try_transfer_balance(origin, account_id.clone(), amount) + .await; + match transfer_result { + Ok(_) => { + log_info(&format!( + "transfer from {} to {} succeeded", + origin.account_id(), + account_id, + )); + break + } + Err(err) => { + log_info(&format!( + "transfer from {} to {} failed with {:?}", + origin.account_id(), + account_id, + err + )); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + } + } + + PairSigner::new(pair) + } + /// This function extracts the metadata of the contract at the file path /// `target/ink/$contract_name.contract`. /// diff --git a/crates/e2e/src/xts.rs b/crates/e2e/src/xts.rs index 9fea3e270f2..eab05f73ae0 100644 --- a/crates/e2e/src/xts.rs +++ b/crates/e2e/src/xts.rs @@ -68,6 +68,25 @@ pub struct Call { data: Vec, } +/// A raw call to `pallet-contracts`'s `call`. +#[derive(Debug, scale::Encode, scale::Decode)] +pub struct Call2 { + dest: sp_runtime::MultiAddress, + #[codec(compact)] + value: B, + gas_limit: Weight, + storage_deposit_limit: Option, + data: Vec, +} + +/// A raw call to `pallet-contracts`'s `call`. +#[derive(Debug, scale::Encode, scale::Decode)] +pub struct Transfer { + dest: C::Address, + #[codec(compact)] + value: E::Balance, +} + #[derive( Debug, Clone, Copy, scale::Encode, scale::Decode, PartialEq, Eq, serde::Serialize, )] @@ -167,10 +186,7 @@ where sr25519::Signature: Into, E: Environment, - E::Balance: scale::Encode + serde::Serialize, - - Call: scale::Encode, - InstantiateWithCode: scale::Encode, + E::Balance: scale::HasCompact + serde::Serialize, { /// Creates a new [`ContractsApi`] instance. pub async fn new(client: OnlineClient, url: &str) -> Self { @@ -189,6 +205,40 @@ where } } + /// Attempt to transfer the `value` from `origin` to `dest`. + /// + /// Returns `Ok` on success, and a [`subxt::Error`] if the extrinsic is + /// invalid (e.g. out of date nonce) + pub async fn try_transfer_balance( + &self, + origin: &Signer, + dest: C::AccountId, + value: E::Balance, + ) -> Result<(), subxt::Error> { + let call = subxt::tx::StaticTxPayload::new( + "Balances", + "transfer", + Transfer:: { + dest: dest.into(), + value, + }, + Default::default(), + ) + .unvalidated(); + + let tx_progress = self + .client + .tx() + .sign_and_submit_then_watch_default(&call, origin) + .await?; + + tx_progress.wait_for_in_block().await.unwrap_or_else(|err| { + panic!("error on call `wait_for_in_block`: {:?}", err); + }); + + Ok(()) + } + /// Dry runs the instantiation of the given `code`. pub async fn instantiate_with_code_dry_run( &self, diff --git a/examples/lang-err-integration-tests/call-builder/lib.rs b/examples/lang-err-integration-tests/call-builder/lib.rs index fd2b80d35e6..874138c29ed 100755 --- a/examples/lang-err-integration-tests/call-builder/lib.rs +++ b/examples/lang-err-integration-tests/call-builder/lib.rs @@ -173,22 +173,20 @@ mod call_builder { async fn e2e_invalid_message_selector_can_be_handled( mut client: ink_e2e::Client, ) -> E2EResult<()> { + let origin = client + .create_and_fund_account(&ink_e2e::alice(), 10_000_000_000_000) + .await; + let constructor = CallBuilderTestRef::new(); let contract_acc_id = client - .instantiate("call_builder", &ink_e2e::alice(), constructor, 0, None) + .instantiate("call_builder", &origin, constructor, 0, None) .await .expect("instantiate failed") .account_id; let flipper_constructor = FlipperRef::new_default(); let flipper_acc_id = client - .instantiate( - "integration_flipper", - &ink_e2e::alice(), - flipper_constructor, - 0, - None, - ) + .instantiate("integration_flipper", &origin, flipper_constructor, 0, None) .await .expect("instantiate `flipper` failed") .account_id; @@ -196,7 +194,7 @@ mod call_builder { let flipper_get = build_message::(flipper_acc_id) .call(|contract| contract.get()); let get_call_result = client - .call(&ink_e2e::alice(), flipper_get, 0, None) + .call(&origin, flipper_get, 0, None) .await .expect("Calling `flipper::get` failed"); let initial_value = get_call_result.return_value(); @@ -205,7 +203,7 @@ mod call_builder { let call = build_message::(contract_acc_id) .call(|contract| contract.call(flipper_acc_id, selector)); let call_result = client - .call(&ink_e2e::alice(), call, 0, None) + .call(&origin, call, 0, None) .await .expect("Calling `call_builder::call` failed"); @@ -219,7 +217,7 @@ mod call_builder { let flipper_get = build_message::(flipper_acc_id) .call(|contract| contract.get()); let get_call_result = client - .call(&ink_e2e::alice(), flipper_get, 0, None) + .call(&origin, flipper_get, 0, None) .await .expect("Calling `flipper::get` failed"); let flipped_value = get_call_result.return_value(); @@ -232,22 +230,20 @@ mod call_builder { async fn e2e_invalid_message_selector_panics_on_invoke( mut client: ink_e2e::Client, ) -> E2EResult<()> { + let origin = client + .create_and_fund_account(&ink_e2e::bob(), 10_000_000_000_000) + .await; + let constructor = CallBuilderTestRef::new(); let contract_acc_id = client - .instantiate("call_builder", &ink_e2e::ferdie(), constructor, 0, None) + .instantiate("call_builder", &origin, constructor, 0, None) .await .expect("instantiate failed") .account_id; let flipper_constructor = FlipperRef::new_default(); let flipper_acc_id = client - .instantiate( - "integration_flipper", - &ink_e2e::ferdie(), - flipper_constructor, - 0, - None, - ) + .instantiate("integration_flipper", &origin, flipper_constructor, 0, None) .await .expect("instantiate `flipper` failed") .account_id; @@ -257,7 +253,7 @@ mod call_builder { let invalid_selector = [0x00, 0x00, 0x00, 0x00]; let call = build_message::(contract_acc_id) .call(|contract| contract.invoke(flipper_acc_id, invalid_selector)); - let call_result = client.call(&ink_e2e::ferdie(), call, 0, None).await; + let call_result = client.call(&origin, call, 0, None).await; assert!(call_result.is_err()); let contains_err_msg = match call_result.unwrap_err() { @@ -276,15 +272,19 @@ mod call_builder { async fn e2e_create_builder_works_with_valid_selector( mut client: ink_e2e::Client, ) -> E2EResult<()> { + let origin = client + .create_and_fund_account(&ink_e2e::bob(), 10_000_000_000_000) + .await; + let constructor = CallBuilderTestRef::new(); let contract_acc_id = client - .instantiate("call_builder", &ink_e2e::bob(), constructor, 0, None) + .instantiate("call_builder", &origin, constructor, 0, None) .await .expect("instantiate failed") .account_id; let code_hash = client - .upload("constructors_return_value", &ink_e2e::bob(), None) + .upload("constructors_return_value", &origin, None) .await .expect("upload `constructors_return_value` failed") .code_hash; @@ -296,7 +296,7 @@ mod call_builder { contract.call_instantiate(code_hash, selector, init_value) }); let call_result = client - .call(&ink_e2e::bob(), call, 0, None) + .call(&origin, call, 0, None) .await .expect("Client failed to call `call_builder::call_instantiate`.") .return_value(); @@ -313,15 +313,19 @@ mod call_builder { async fn e2e_create_builder_fails_with_invalid_selector( mut client: ink_e2e::Client, ) -> E2EResult<()> { + let origin = client + .create_and_fund_account(&ink_e2e::bob(), 10_000_000_000_000) + .await; + let constructor = CallBuilderTestRef::new(); let contract_acc_id = client - .instantiate("call_builder", &ink_e2e::charlie(), constructor, 0, None) + .instantiate("call_builder", &origin, constructor, 0, None) .await .expect("instantiate failed") .account_id; let code_hash = client - .upload("constructors_return_value", &ink_e2e::charlie(), None) + .upload("constructors_return_value", &origin, None) .await .expect("upload `constructors_return_value` failed") .code_hash; @@ -333,7 +337,7 @@ mod call_builder { contract.call_instantiate(code_hash, selector, init_value) }); let call_result = client - .call(&ink_e2e::charlie(), call, 0, None) + .call(&origin, call, 0, None) .await .expect("Client failed to call `call_builder::call_instantiate`.") .return_value(); @@ -350,15 +354,19 @@ mod call_builder { async fn e2e_create_builder_with_infallible_revert_constructor_encodes_ok( mut client: ink_e2e::Client, ) -> E2EResult<()> { + let origin = client + .create_and_fund_account(&ink_e2e::bob(), 10_000_000_000_000) + .await; + let constructor = CallBuilderTestRef::new(); let contract_acc_id = client - .instantiate("call_builder", &ink_e2e::dave(), constructor, 0, None) + .instantiate("call_builder", &origin, constructor, 0, None) .await .expect("instantiate failed") .account_id; let code_hash = client - .upload("constructors_return_value", &ink_e2e::dave(), None) + .upload("constructors_return_value", &origin, None) .await .expect("upload `constructors_return_value` failed") .code_hash; @@ -370,7 +378,7 @@ mod call_builder { contract.call_instantiate(code_hash, selector, init_value) }); - let call_result = client.call(&mut ink_e2e::dave(), call, 0, None).await; + let call_result = client.call(&origin, call, 0, None).await; assert!( call_result.is_err(), "Call execution should've failed, but didn't." @@ -394,15 +402,19 @@ mod call_builder { async fn e2e_create_builder_can_handle_fallible_constructor_success( mut client: ink_e2e::Client, ) -> E2EResult<()> { + let origin = client + .create_and_fund_account(&ink_e2e::bob(), 10_000_000_000_000) + .await; + let constructor = CallBuilderTestRef::new(); let contract_acc_id = client - .instantiate("call_builder", &ink_e2e::eve(), constructor, 0, None) + .instantiate("call_builder", &origin, constructor, 0, None) .await .expect("instantiate failed") .account_id; let code_hash = client - .upload("constructors_return_value", &ink_e2e::eve(), None) + .upload("constructors_return_value", &origin, None) .await .expect("upload `constructors_return_value` failed") .code_hash; @@ -414,7 +426,7 @@ mod call_builder { contract.call_instantiate_fallible(code_hash, selector, init_value) }); let call_result = client - .call(&mut ink_e2e::eve(), call, 0, None) + .call(&origin, call, 0, None) .await .expect("Calling `call_builder::call_instantiate_fallible` failed") .return_value(); @@ -431,15 +443,19 @@ mod call_builder { async fn e2e_create_builder_can_handle_fallible_constructor_error( mut client: ink_e2e::Client, ) -> E2EResult<()> { + let origin = client + .create_and_fund_account(&ink_e2e::bob(), 10_000_000_000_000) + .await; + let constructor = CallBuilderTestRef::new(); let contract_acc_id = client - .instantiate("call_builder", &ink_e2e::ferdie(), constructor, 0, None) + .instantiate("call_builder", &origin, constructor, 0, None) .await .expect("instantiate failed") .account_id; let code_hash = client - .upload("constructors_return_value", &ink_e2e::ferdie(), None) + .upload("constructors_return_value", &origin, None) .await .expect("upload `constructors_return_value` failed") .code_hash; @@ -451,7 +467,7 @@ mod call_builder { contract.call_instantiate_fallible(code_hash, selector, init_value) }); let call_result = client - .call(&mut ink_e2e::ferdie(), call, 0, None) + .call(&origin, call, 0, None) .await .expect("Calling `call_builder::call_instantiate_fallible` failed") .return_value(); @@ -475,15 +491,19 @@ mod call_builder { async fn e2e_create_builder_with_fallible_revert_constructor_encodes_ok( mut client: ink_e2e::Client, ) -> E2EResult<()> { + let origin = client + .create_and_fund_account(&ink_e2e::bob(), 10_000_000_000_000) + .await; + let constructor = CallBuilderTestRef::new(); let contract_acc_id = client - .instantiate("call_builder", &ink_e2e::alice(), constructor, 0, None) + .instantiate("call_builder", &origin, constructor, 0, None) .await .expect("instantiate failed") .account_id; let code_hash = client - .upload("constructors_return_value", &ink_e2e::alice(), None) + .upload("constructors_return_value", &origin, None) .await .expect("upload `constructors_return_value` failed") .code_hash; @@ -494,7 +514,7 @@ mod call_builder { build_message::(contract_acc_id).call(|contract| { contract.call_instantiate_fallible(code_hash, selector, init_value) }); - let call_result = client.call(&mut ink_e2e::alice(), call, 0, None).await; + let call_result = client.call(&origin, call, 0, None).await; assert!( call_result.is_err(), @@ -520,15 +540,19 @@ mod call_builder { async fn e2e_create_builder_with_fallible_revert_constructor_encodes_err( mut client: ink_e2e::Client, ) -> E2EResult<()> { + let origin = client + .create_and_fund_account(&ink_e2e::bob(), 10_000_000_000_000) + .await; + let constructor = CallBuilderTestRef::new(); let contract_acc_id = client - .instantiate("call_builder", &ink_e2e::bob(), constructor, 0, None) + .instantiate("call_builder", &origin, constructor, 0, None) .await .expect("instantiate failed") .account_id; let code_hash = client - .upload("constructors_return_value", &ink_e2e::bob(), None) + .upload("constructors_return_value", &origin, None) .await .expect("upload `constructors_return_value` failed") .code_hash; @@ -540,7 +564,7 @@ mod call_builder { contract.call_instantiate_fallible(code_hash, selector, init_value) }); let call_result = client - .call(&mut ink_e2e::bob(), call, 0, None) + .call(&origin, call, 0, None) .await .expect( "Client failed to call `call_builder::call_instantiate_fallible`.",