diff --git a/Cargo.lock b/Cargo.lock index 7039818b8..f89afe053 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -914,6 +914,7 @@ dependencies = [ "sp-weights", "substrate-build-script-utils", "subxt", + "subxt-signer", "tempfile", "tokio", "tracing", @@ -1209,6 +1210,7 @@ dependencies = [ "contract-build", "contract-metadata", "contract-transcode", + "derivative", "futures", "hex", "ink", diff --git a/crates/cargo-contract/Cargo.toml b/crates/cargo-contract/Cargo.toml index 57bcc7d58..2a27c4213 100644 --- a/crates/cargo-contract/Cargo.toml +++ b/crates/cargo-contract/Cargo.toml @@ -47,6 +47,7 @@ subxt = "0.34.0" sp-core = "28.0.0" sp-weights = "27.0.0" hex = "0.4.3" +subxt-signer = { version = "0.34.0", features = ["subxt", "sr25519"] } [build-dependencies] anyhow = "1.0.80" diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index 0c83b9e0c..fc1b94e1d 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -24,6 +24,7 @@ use ink_env::{ use std::fmt::Debug; use super::{ + create_signer, display_contract_exec_result, display_contract_exec_result_debug, display_dry_run_result_warning, @@ -44,7 +45,9 @@ use contract_extrinsics::{ BalanceVariant, CallCommandBuilder, CallExec, + DisplayEvents, ExtrinsicOptsBuilder, + TokenMetadata, }; use contract_transcode::Value; use sp_weights::Weight; @@ -52,7 +55,7 @@ use subxt::{ Config, PolkadotConfig as DefaultConfig, }; - +use subxt_signer::sr25519::Keypair; #[derive(Debug, clap::Args)] #[clap(name = "call", about = "Call a contract")] pub struct CallCommand { @@ -92,24 +95,32 @@ impl CallCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { - let extrinsic_opts = ExtrinsicOptsBuilder::default() + let token_metadata = + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + + let signer = create_signer(&self.extrinsic_cli_opts.suri)?; + let extrinsic_opts = ExtrinsicOptsBuilder::new(signer) .file(self.extrinsic_cli_opts.file.clone()) .manifest_path(self.extrinsic_cli_opts.manifest_path.clone()) .url(self.extrinsic_cli_opts.url.clone()) - .suri(self.extrinsic_cli_opts.suri.clone()) - .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) + .storage_deposit_limit( + self.extrinsic_cli_opts + .storage_deposit_limit + .clone() + .map(|bv| bv.denominate_balance(&token_metadata)) + .transpose()?, + ) .verbosity(self.extrinsic_cli_opts.verbosity()?) .done(); - let call_exec = CallCommandBuilder::default() - .contract(self.contract.clone()) - .message(self.message.clone()) - .args(self.args.clone()) - .extrinsic_opts(extrinsic_opts) - .gas_limit(self.gas_limit) - .proof_size(self.proof_size) - .value(self.value.clone()) - .done() - .await?; + let call_exec = + CallCommandBuilder::new(self.contract.clone(), &self.message, extrinsic_opts) + .args(self.args.clone()) + .gas_limit(self.gas_limit) + .proof_size(self.proof_size) + .value(self.value.denominate_balance(&token_metadata)?) + .done() + .await?; + let metadata = call_exec.client().metadata(); if !self.extrinsic_cli_opts.execute { let result = call_exec.call_dry_run().await?; @@ -143,7 +154,6 @@ impl CallCommand { }; } Err(ref err) => { - let metadata = call_exec.client().metadata(); let object = ErrorVariant::from_dispatch_error(err, &metadata)?; if self.output_json() { return Err(object) @@ -179,13 +189,18 @@ impl CallCommand { ); })?; } - let display_events = call_exec.call(Some(gas_limit)).await?; + let events = call_exec.call(Some(gas_limit)).await?; + let display_events = DisplayEvents::from_events::< + DefaultConfig, + DefaultEnvironment, + >(&events, None, &metadata)?; + let output = if self.output_json() { display_events.to_json()? } else { display_events.display_events::( self.extrinsic_cli_opts.verbosity().unwrap(), - call_exec.token_metadata(), + &token_metadata, )? }; println!("{output}"); @@ -196,7 +211,7 @@ impl CallCommand { /// A helper function to estimate the gas required for a contract call. async fn pre_submit_dry_run_gas_estimate_call( - call_exec: &CallExec, + call_exec: &CallExec, output_json: bool, skip_dry_run: bool, ) -> Result { diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index 82ad40c90..58de3da4b 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -15,6 +15,7 @@ // along with cargo-contract. If not, see . use super::{ + create_signer, display_contract_exec_result, display_contract_exec_result_debug, display_dry_run_result_warning, @@ -47,6 +48,7 @@ use contract_extrinsics::{ InstantiateCommandBuilder, InstantiateDryRunResult, InstantiateExecResult, + TokenMetadata, }; use ink_env::{ DefaultEnvironment, @@ -55,6 +57,7 @@ use ink_env::{ use sp_core::Bytes; use std::fmt::Debug; use subxt::PolkadotConfig as DefaultConfig; +use subxt_signer::sr25519::Keypair; #[derive(Debug, clap::Args)] pub struct InstantiateCommand { @@ -100,24 +103,35 @@ impl InstantiateCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { - let extrinsic_opts = ExtrinsicOptsBuilder::default() + let token_metadata = + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + + let signer = create_signer(&self.extrinsic_cli_opts.suri)?; + let extrinsic_opts = ExtrinsicOptsBuilder::new(signer) .file(self.extrinsic_cli_opts.file.clone()) .manifest_path(self.extrinsic_cli_opts.manifest_path.clone()) .url(self.extrinsic_cli_opts.url.clone()) - .suri(self.extrinsic_cli_opts.suri.clone()) - .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) + .storage_deposit_limit( + self.extrinsic_cli_opts + .storage_deposit_limit + .clone() + .map(|bv| bv.denominate_balance(&token_metadata)) + .transpose()?, + ) .done(); - let instantiate_exec: InstantiateExec = - InstantiateCommandBuilder::default() - .constructor(self.constructor.clone()) - .args(self.args.clone()) - .extrinsic_opts(extrinsic_opts) - .value(self.value.clone()) - .gas_limit(self.gas_limit) - .proof_size(self.proof_size) - .salt(self.salt.clone()) - .done() - .await?; + let instantiate_exec: InstantiateExec< + DefaultConfig, + DefaultEnvironment, + Keypair, + > = InstantiateCommandBuilder::new(extrinsic_opts) + .constructor(self.constructor.clone()) + .args(self.args.clone()) + .value(self.value.denominate_balance(&token_metadata)?) + .gas_limit(self.gas_limit) + .proof_size(self.proof_size) + .salt(self.salt.clone()) + .done() + .await?; if !self.extrinsic_cli_opts.execute { let result = instantiate_exec.instantiate_dry_run().await?; @@ -171,6 +185,7 @@ impl InstantiateCommand { display_result( &instantiate_exec, instantiate_result, + &token_metadata, self.output_json(), self.extrinsic_cli_opts.verbosity().unwrap(), ) @@ -182,7 +197,7 @@ impl InstantiateCommand { /// A helper function to estimate the gas required for a contract instantiation. async fn pre_submit_dry_run_gas_estimate_instantiate( - instantiate_exec: &InstantiateExec, + instantiate_exec: &InstantiateExec, output_json: bool, skip_dry_run: bool, ) -> Result { @@ -238,13 +253,14 @@ async fn pre_submit_dry_run_gas_estimate_instantiate( /// Displays the results of contract instantiation, including contract address, /// events, and optional code hash. pub async fn display_result( - instantiate_exec: &InstantiateExec, + instantiate_exec: &InstantiateExec, instantiate_exec_result: InstantiateExecResult, + token_metadata: &TokenMetadata, output_json: bool, verbosity: Verbosity, ) -> Result<(), ErrorVariant> { let events = DisplayEvents::from_events::( - &instantiate_exec_result.result, + &instantiate_exec_result.events, Some(instantiate_exec.transcoder()), &instantiate_exec.client().metadata(), )?; @@ -261,10 +277,7 @@ pub async fn display_result( } else { println!( "{}", - events.display_events::( - verbosity, - &instantiate_exec_result.token_metadata - )? + events.display_events::(verbosity, token_metadata)? ); if let Some(code_hash) = instantiate_exec_result.code_hash { name_value_println!("Code hash", format!("{code_hash:?}")); @@ -275,7 +288,7 @@ pub async fn display_result( } pub fn print_default_instantiate_preview( - instantiate_exec: &InstantiateExec, + instantiate_exec: &InstantiateExec, gas_limit: Weight, ) { name_value_println!( diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index 384d0489b..f0fb9f125 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -84,6 +84,10 @@ pub use subxt::{ Config, PolkadotConfig as DefaultConfig, }; +use subxt_signer::{ + sr25519::Keypair, + SecretUri, +}; /// Arguments required for creating and sending an extrinsic to a substrate node. #[derive(Clone, Debug, clap::Args)] @@ -271,6 +275,13 @@ pub fn display_all_contracts(contracts: &[::AccountId]) .for_each(|e: &::AccountId| println!("{}", e)) } +/// Create a Signer from a secret URI. +pub fn create_signer(suri: &str) -> Result { + let uri = ::from_str(suri)?; + let keypair = Keypair::from_uri(&uri)?; + Ok(keypair) +} + /// Parse a hex encoded 32 byte hash. Returns error if not exactly 32 bytes. pub fn parse_code_hash(input: &str) -> Result<::Hash> { let bytes = contract_build::util::decode_hex(input)?; diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index 28480017b..ad7c7e8d1 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -18,21 +18,25 @@ use crate::ErrorVariant; use std::fmt::Debug; use super::{ + create_signer, parse_code_hash, CLIExtrinsicOpts, }; use anyhow::Result; use contract_build::name_value_println; use contract_extrinsics::{ + DisplayEvents, ExtrinsicOptsBuilder, RemoveCommandBuilder, RemoveExec, + TokenMetadata, }; use ink_env::DefaultEnvironment; use subxt::{ Config, PolkadotConfig as DefaultConfig, }; +use subxt_signer::sr25519::Keypair; #[derive(Debug, clap::Args)] #[clap(name = "remove", about = "Remove a contract's code")] @@ -54,27 +58,40 @@ impl RemoveCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { - let extrinsic_opts = ExtrinsicOptsBuilder::default() + let token_metadata = + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + + let signer: Keypair = create_signer(&self.extrinsic_cli_opts.suri)?; + let extrinsic_opts = ExtrinsicOptsBuilder::new(signer) .file(self.extrinsic_cli_opts.file.clone()) .manifest_path(self.extrinsic_cli_opts.manifest_path.clone()) .url(self.extrinsic_cli_opts.url.clone()) - .suri(self.extrinsic_cli_opts.suri.clone()) - .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) + .storage_deposit_limit( + self.extrinsic_cli_opts + .storage_deposit_limit + .clone() + .map(|bv| bv.denominate_balance(&token_metadata)) + .transpose()?, + ) .done(); - let remove_exec: RemoveExec = - RemoveCommandBuilder::default() + let remove_exec: RemoveExec = + RemoveCommandBuilder::new(extrinsic_opts) .code_hash(self.code_hash) - .extrinsic_opts(extrinsic_opts) .done() .await?; let remove_result = remove_exec.remove_code().await?; - let display_events = remove_result.display_events; + let display_events = + DisplayEvents::from_events::( + &remove_result.events, + Some(remove_exec.transcoder()), + &remove_exec.client().metadata(), + )?; let output_events = if self.output_json() { display_events.to_json()? } else { display_events.display_events::( self.extrinsic_cli_opts.verbosity().unwrap(), - remove_exec.token_metadata(), + &token_metadata, )? }; if let Some(code_removed) = remove_result.code_removed { diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index cba404312..9fdd40762 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -18,13 +18,16 @@ use crate::ErrorVariant; use std::fmt::Debug; use super::{ + create_signer, display_dry_run_result_warning, CLIExtrinsicOpts, }; use anyhow::Result; use contract_build::name_value_println; use contract_extrinsics::{ + DisplayEvents, ExtrinsicOptsBuilder, + TokenMetadata, UploadCommandBuilder, UploadExec, }; @@ -36,6 +39,7 @@ use subxt::{ Config, PolkadotConfig as DefaultConfig, }; +use subxt_signer::sr25519::Keypair; #[derive(Debug, clap::Args)] #[clap(name = "upload", about = "Upload a contract's code")] @@ -54,20 +58,27 @@ impl UploadCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { - let extrinsic_opts = ExtrinsicOptsBuilder::default() + let token_metadata = + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + + let signer = create_signer(&self.extrinsic_cli_opts.suri)?; + let extrinsic_opts = ExtrinsicOptsBuilder::new(signer) .file(self.extrinsic_cli_opts.file.clone()) .manifest_path(self.extrinsic_cli_opts.manifest_path.clone()) .url(self.extrinsic_cli_opts.url.clone()) - .suri(self.extrinsic_cli_opts.suri.clone()) - .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) + .storage_deposit_limit( + self.extrinsic_cli_opts + .storage_deposit_limit + .clone() + .map(|bv| bv.denominate_balance(&token_metadata)) + .transpose()?, + ) .done(); - let upload_exec: UploadExec = - UploadCommandBuilder::default() - .extrinsic_opts(extrinsic_opts) - .done() - .await?; + let upload_exec: UploadExec = + UploadCommandBuilder::new(extrinsic_opts).done().await?; let code_hash = upload_exec.code().code_hash(); + let metadata = upload_exec.client().metadata(); if !self.extrinsic_cli_opts.execute { match upload_exec.upload_code_rpc().await? { @@ -85,7 +96,6 @@ impl UploadCommand { } } Err(err) => { - let metadata = upload_exec.client().metadata(); let err = ErrorVariant::from_dispatch_error(&err, &metadata)?; if self.output_json() { return Err(err) @@ -96,13 +106,16 @@ impl UploadCommand { } } else { let upload_result = upload_exec.upload_code().await?; - let display_events = upload_result.display_events; + let display_events = DisplayEvents::from_events::< + DefaultConfig, + DefaultEnvironment, + >(&upload_result.events, None, &metadata)?; let output_events = if self.output_json() { display_events.to_json()? } else { display_events.display_events::( self.extrinsic_cli_opts.verbosity()?, - upload_exec.token_metadata(), + &token_metadata, )? }; if let Some(code_stored) = upload_result.code_stored { diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index 76703ba9b..6203d23d9 100644 --- a/crates/extrinsics/Cargo.toml +++ b/crates/extrinsics/Cargo.toml @@ -38,8 +38,8 @@ sp-weights = "27.0.0" pallet-contracts-uapi = "5.0.0" scale-info = "2.10.0" subxt = "0.34.0" -subxt-signer = { version = "0.34.0", features = ["subxt", "sr25519"] } hex = "0.4.3" +derivative = "2.2.0" ink_metadata = "5.0.0-rc.1" ink_env = "5.0.0-rc.1" @@ -50,6 +50,7 @@ regex = "1.10.3" predicates = "3.1.0" tempfile = "3.10.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +subxt-signer = { version = "0.34.0", features = ["subxt", "sr25519"] } [features] integration-tests = [] diff --git a/crates/extrinsics/src/balance.rs b/crates/extrinsics/src/balance.rs index 96e9892f6..45fd64047 100644 --- a/crates/extrinsics/src/balance.rs +++ b/crates/extrinsics/src/balance.rs @@ -26,7 +26,10 @@ use rust_decimal::{ }; use serde_json::json; use subxt::{ - backend::legacy::LegacyRpcMethods, + backend::{ + legacy::LegacyRpcMethods, + rpc::RpcClient, + }, Config, }; @@ -35,10 +38,13 @@ use anyhow::{ Context, Result, }; +use url::Url; + +use crate::url_to_string; /// Represents different formats of a balance #[derive(Debug, Clone, PartialEq, Eq)] -pub enum BalanceVariant { +pub enum BalanceVariant { /// Default format: no symbol, no token_decimals Default(Balance), /// Denominated format: symbol and token_decimals are present @@ -73,8 +79,10 @@ pub enum UnitPrefix { impl TokenMetadata { /// Query [TokenMetadata] through the node's RPC - pub async fn query(client: &LegacyRpcMethods) -> Result { - let sys_props = client.system_properties().await?; + pub async fn query(url: &Url) -> Result { + let rpc_cli = RpcClient::from_url(url_to_string(url)).await?; + let rpc = LegacyRpcMethods::::new(rpc_cli.clone()); + let sys_props = rpc.system_properties().await?; let default_decimals = json!(12); let default_units = json!("UNIT"); @@ -98,7 +106,7 @@ impl TokenMetadata { impl FromStr for BalanceVariant where - Balance: FromStr + Clone, + Balance: FromStr, { type Err = anyhow::Error; diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index c038bd236..12a5fbd13 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -15,17 +15,11 @@ // along with cargo-contract. If not, see . use super::{ - account_id, - events::DisplayEvents, pallet_contracts_primitives::ContractExecResult, - state, state_call, submit_extrinsic, - BalanceVariant, ContractMessageTranscoder, ErrorVariant, - Missing, - TokenMetadata, }; use crate::{ check_env_types, @@ -40,223 +34,140 @@ use anyhow::{ use ink_env::Environment; use scale::Encode; use sp_weights::Weight; -use subxt_signer::sr25519::Keypair; -use core::marker::PhantomData; -use std::str::FromStr; use subxt::{ backend::{ legacy::LegacyRpcMethods, rpc::RpcClient, }, + blocks::ExtrinsicEvents, ext::{ scale_decode::IntoVisitor, scale_encode::EncodeAsType, }, + tx, Config, OnlineClient, }; -pub struct CallOpts { - contract: Option, +/// A builder for the call command. +pub struct CallCommandBuilder { + contract: C::AccountId, message: String, args: Vec, - extrinsic_opts: ExtrinsicOpts, + extrinsic_opts: ExtrinsicOpts, gas_limit: Option, proof_size: Option, - value: BalanceVariant, -} - -/// A builder for the call command. -pub struct CallCommandBuilder { - opts: CallOpts, - marker: PhantomData (Message, ExtrinsicOptions)>, -} - -impl Default - for CallCommandBuilder< - C, - E, - Missing, - Missing, - > -where - E::Balance: FromStr + From, -{ - fn default() -> Self { - Self::new() - } + value: E::Balance, } -impl CallCommandBuilder, X> +impl CallCommandBuilder where - E::Balance: FromStr + From, + E::Balance: Default, + Signer: tx::Signer + Clone, { /// Returns a clean builder for [`CallExec`]. pub fn new( - ) -> CallCommandBuilder, Missing> - { - CallCommandBuilder { - opts: CallOpts { - contract: None, - message: String::new(), - args: Vec::new(), - extrinsic_opts: ExtrinsicOpts::default(), - gas_limit: None, - proof_size: None, - value: "0".parse().unwrap(), - }, - marker: PhantomData, - } - } - - /// Sets the name of the contract message to call. - pub fn message>( - self, - message: T, - ) -> CallCommandBuilder { - CallCommandBuilder { - opts: CallOpts { - message: message.into(), - ..self.opts - }, - marker: PhantomData, - } - } -} - -impl - CallCommandBuilder> -{ - /// Sets the extrinsic operation. - pub fn extrinsic_opts( - self, - extrinsic_opts: ExtrinsicOpts, - ) -> CallCommandBuilder { + contract: C::AccountId, + message: &str, + extrinsic_opts: ExtrinsicOpts, + ) -> CallCommandBuilder { CallCommandBuilder { - opts: CallOpts { - extrinsic_opts, - ..self.opts - }, - marker: PhantomData, + contract, + message: message.to_string(), + args: Vec::new(), + extrinsic_opts, + gas_limit: None, + proof_size: None, + value: Default::default(), } } -} - -impl CallCommandBuilder { - /// Sets the the address of the the contract to call. - pub fn contract(self, contract: C::AccountId) -> Self { - let mut this = self; - this.opts.contract = Some(contract); - this - } /// Sets the arguments of the contract message to call. pub fn args(self, args: Vec) -> Self { let mut this = self; - this.opts.args = args.into_iter().map(|arg| arg.to_string()).collect(); + this.args = args.into_iter().map(|arg| arg.to_string()).collect(); this } /// Sets the maximum amount of gas to be used for this command. pub fn gas_limit(self, gas_limit: Option) -> Self { let mut this = self; - this.opts.gas_limit = gas_limit; + this.gas_limit = gas_limit; this } /// Sets the maximum proof size for this call. pub fn proof_size(self, proof_size: Option) -> Self { let mut this = self; - this.opts.proof_size = proof_size; + this.proof_size = proof_size; this } /// Sets the value to be transferred as part of the call. - pub fn value(self, value: BalanceVariant) -> Self { + pub fn value(self, value: E::Balance) -> Self { let mut this = self; - this.opts.value = value; + this.value = value; this } -} -impl - CallCommandBuilder -where - C::AccountId: IntoVisitor, - E::Balance: From, -{ /// Preprocesses contract artifacts and options for subsequent contract calls. /// /// This function prepares the necessary data for making a contract call based on the /// provided contract artifacts, message, arguments, and options. It ensures that the - /// required contract code and message data are available, sets up the client, signer, + /// required contract code and message data are available, sets up the client, /// and other relevant parameters, preparing for the contract call operation. /// /// Returns the `CallExec` containing the preprocessed data for the contract call, /// or an error in case of failure. - pub async fn done(self) -> Result> { - let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; + pub async fn done(self) -> Result> { + let artifacts = self.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; - let call_data = transcoder.encode(&self.opts.message, &self.opts.args)?; + let call_data = transcoder.encode(&self.message, &self.args)?; tracing::debug!("Message data: {:?}", hex::encode(&call_data)); - let signer = self.opts.extrinsic_opts.signer()?; - - let url = self.opts.extrinsic_opts.url(); + let url = self.extrinsic_opts.url(); let rpc = RpcClient::from_url(&url).await?; let client = OnlineClient::from_rpc_client(rpc.clone()).await?; let rpc = LegacyRpcMethods::new(rpc); - check_env_types(&client, &transcoder, self.opts.extrinsic_opts.verbosity())?; - - let token_metadata = TokenMetadata::query(&rpc).await?; - let contract = self - .opts - .contract - .ok_or(anyhow!("Contract address not set"))?; + check_env_types(&client, &transcoder, self.extrinsic_opts.verbosity())?; Ok(CallExec { - contract, - message: self.opts.message.clone(), - args: self.opts.args.clone(), - opts: self.opts.extrinsic_opts.clone(), - gas_limit: self.opts.gas_limit, - proof_size: self.opts.proof_size, - value: self.opts.value.clone(), + contract: self.contract, + message: self.message.clone(), + args: self.args.clone(), + opts: self.extrinsic_opts, + gas_limit: self.gas_limit, + proof_size: self.proof_size, + value: self.value, rpc, client, transcoder, call_data, - signer, - token_metadata, }) } } -pub struct CallExec { +pub struct CallExec { contract: C::AccountId, message: String, args: Vec, - opts: ExtrinsicOpts, + opts: ExtrinsicOpts, gas_limit: Option, proof_size: Option, - value: BalanceVariant, + value: E::Balance, rpc: LegacyRpcMethods, client: OnlineClient, transcoder: ContractMessageTranscoder, call_data: Vec, - signer: Keypair, - token_metadata: TokenMetadata, } -impl CallExec +impl CallExec where - C::Signature: From, >::OtherParams: Default, - C::Address: From, - C::AccountId: From + EncodeAsType + IntoVisitor, - E::Balance: From, + C::AccountId: EncodeAsType + IntoVisitor, + Signer: tx::Signer + Clone, { /// Simulates a contract call without modifying the blockchain. /// @@ -268,13 +179,11 @@ where /// Returns the dry run simulation result of type [`ContractExecResult`], which /// includes information about the simulated call, or an error in case of failure. pub async fn call_dry_run(&self) -> Result> { - let storage_deposit_limit = self - .opts - .storage_deposit_limit_balance(&self.token_metadata)?; + let storage_deposit_limit = self.opts.storage_deposit_limit(); let call_request = CallRequest { - origin: account_id::(&self.signer), + origin: self.opts.signer().account_id(), dest: self.contract.clone(), - value: self.value.denominate_balance(&self.token_metadata)?, + value: self.value, gas_limit: None, storage_deposit_limit, input_data: self.call_data.clone(), @@ -293,7 +202,7 @@ where pub async fn call( &self, gas_limit: Option, - ) -> Result { + ) -> Result, ErrorVariant> { if !self .transcoder() .metadata() @@ -317,13 +226,11 @@ where None => self.estimate_gas().await?, }; tracing::debug!("calling contract {:?}", self.contract); - let storage_deposit_limit = self - .opts - .storage_deposit_limit_balance(&self.token_metadata)?; + let storage_deposit_limit = self.opts.storage_deposit_limit(); let call = Call::new( self.contract.clone().into(), - self.value.denominate_balance(&self.token_metadata)?, + self.value, gas_limit, storage_deposit_limit, self.call_data.clone(), @@ -331,15 +238,9 @@ where .build(); let result = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; - - let display_events = DisplayEvents::from_events::( - &result, - Some(&self.transcoder), - &self.client.metadata(), - )?; + submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?; - Ok(display_events) + Ok(result) } /// Estimates the gas required for a contract call without modifying the blockchain. @@ -397,7 +298,7 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } @@ -412,7 +313,7 @@ where } /// Returns the value to be transferred as part of the call. - pub fn value(&self) -> &BalanceVariant { + pub fn value(&self) -> &E::Balance { &self.value } @@ -430,16 +331,6 @@ where pub fn call_data(&self) -> &Vec { &self.call_data } - - /// Returns the signer. - pub fn signer(&self) -> &Keypair { - &self.signer - } - - /// Returns the token metadata. - pub fn token_metadata(&self) -> &TokenMetadata { - &self.token_metadata - } } /// A struct that encodes RPC parameters required for a call to a smart contract. diff --git a/crates/extrinsics/src/events.rs b/crates/extrinsics/src/events.rs index ce6e381b0..c14500712 100644 --- a/crates/extrinsics/src/events.rs +++ b/crates/extrinsics/src/events.rs @@ -182,6 +182,10 @@ pub struct Event { pub fields: Vec, } +/// Events produced from invoking a contract extrinsic. +#[derive(serde::Serialize)] +pub struct Events(Vec); + /// Displays events produced from invoking a contract extrinsic. #[derive(serde::Serialize)] pub struct DisplayEvents(Vec); diff --git a/crates/extrinsics/src/extrinsic_opts.rs b/crates/extrinsics/src/extrinsic_opts.rs index dcd1ced49..a1edab25c 100644 --- a/crates/extrinsics/src/extrinsic_opts.rs +++ b/crates/extrinsics/src/extrinsic_opts.rs @@ -14,99 +14,63 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use core::marker::PhantomData; - +use anyhow::Result; use contract_build::Verbosity; +use derivative::Derivative; use ink_env::Environment; -use subxt_signer::{ - sr25519::Keypair, - SecretUri, +use subxt::{ + tx, + Config, }; use url::Url; -use anyhow::{ - Ok, - Result, -}; - use crate::{ url_to_string, - BalanceVariant, ContractArtifacts, - TokenMetadata, }; use std::{ + marker::PhantomData, option::Option, path::PathBuf, }; /// Arguments required for creating and sending an extrinsic to a substrate node. -#[derive(Clone, Debug)] -pub struct ExtrinsicOpts { +#[derive(Derivative)] +#[derivative(Clone(bound = "E::Balance: Clone"))] +pub struct ExtrinsicOpts { file: Option, manifest_path: Option, url: url::Url, - suri: String, - storage_deposit_limit: Option>, + signer: Signer, + storage_deposit_limit: Option, verbosity: Verbosity, -} - -/// Type state for the extrinsics' commands to tell that some mandatory state has not yet -/// been set yet or to fail upon setting the same state multiple times. -pub struct Missing(PhantomData S>); - -pub mod state { - //! Type states that tell what state of the commands has not - //! yet been set properly for a valid construction. - - /// Type state for the Secret key URI. - pub struct Suri; - /// Type state for extrinsic options. - pub struct ExtrinsicOptions; - /// Type state for the name of the contract message to call. - pub struct Message; + _marker: PhantomData, } /// A builder for extrinsic options. -pub struct ExtrinsicOptsBuilder { - opts: ExtrinsicOpts, - marker: PhantomData Suri>, +pub struct ExtrinsicOptsBuilder { + opts: ExtrinsicOpts, } -impl ExtrinsicOptsBuilder> +impl ExtrinsicOptsBuilder where - E::Balance: From, + Signer: tx::Signer + Clone, { - /// Returns a clean builder for `ExtrinsicOpts`. - pub fn new() -> ExtrinsicOptsBuilder> { - ExtrinsicOptsBuilder { - opts: ExtrinsicOpts::default(), - marker: PhantomData, - } - } - - /// Sets the secret key URI for the account deploying the contract. - pub fn suri>(self, suri: T) -> ExtrinsicOptsBuilder { + /// Returns a clean builder for [`ExtrinsicOpts`]. + pub fn new(signer: Signer) -> ExtrinsicOptsBuilder { ExtrinsicOptsBuilder { opts: ExtrinsicOpts { - suri: suri.into(), - ..self.opts + file: None, + manifest_path: None, + url: url::Url::parse("ws://localhost:9944").unwrap(), + signer, + storage_deposit_limit: None, + verbosity: Verbosity::Default, + _marker: PhantomData, }, - marker: PhantomData, } } -} - -impl Default for ExtrinsicOptsBuilder> -where - E::Balance: From, -{ - fn default() -> Self { - Self::new() - } -} -impl ExtrinsicOptsBuilder { /// Sets the path to the contract build artifact file. pub fn file>(self, file: Option) -> Self { let mut this = self; @@ -132,7 +96,7 @@ impl ExtrinsicOptsBuilder { /// storage. pub fn storage_deposit_limit( self, - storage_deposit_limit: Option>, + storage_deposit_limit: Option, ) -> Self { let mut this = self; this.opts.storage_deposit_limit = storage_deposit_limit; @@ -145,38 +109,16 @@ impl ExtrinsicOptsBuilder { this.opts.verbosity = verbosity; this } -} -impl ExtrinsicOptsBuilder -where - E::Balance: From, -{ - /// Finishes construction of the extrinsic options. - pub fn done(self) -> ExtrinsicOpts { + pub fn done(self) -> ExtrinsicOpts { self.opts } } -#[allow(clippy::new_ret_no_self)] -impl ExtrinsicOpts +impl ExtrinsicOpts where - E::Balance: From, + Signer: tx::Signer + Clone, { - /// Returns a clean builder for [`ExtrinsicOpts`]. - pub fn new() -> ExtrinsicOptsBuilder> { - ExtrinsicOptsBuilder { - opts: Self { - file: None, - manifest_path: None, - url: url::Url::parse("ws://localhost:9944").unwrap(), - suri: String::new(), - storage_deposit_limit: None, - verbosity: Verbosity::Default, - }, - marker: PhantomData, - } - } - /// Load contract artifacts. pub fn contract_artifacts(&self) -> Result { ContractArtifacts::from_manifest_or_file( @@ -185,13 +127,6 @@ where ) } - /// Returns the signer for contract extrinsics. - pub fn signer(&self) -> Result { - let uri = ::from_str(&self.suri)?; - let keypair = Keypair::from_uri(&uri)?; - Ok(keypair) - } - /// Return the file path of the contract artifact. pub fn file(&self) -> Option<&PathBuf> { self.file.as_ref() @@ -207,26 +142,14 @@ where url_to_string(&self.url) } - /// Return the secret URI of the signer. - pub fn suri(&self) -> &str { - &self.suri + /// Return the signer. + pub fn signer(&self) -> &Signer { + &self.signer } /// Return the storage deposit limit. - pub fn storage_deposit_limit(&self) -> Option<&BalanceVariant> { - self.storage_deposit_limit.as_ref() - } - - /// Get the storage deposit limit converted to balance for passing to extrinsics. - pub fn storage_deposit_limit_balance( - &self, - token_metadata: &TokenMetadata, - ) -> Result> { - Ok(self - .storage_deposit_limit - .as_ref() - .map(|bv| bv.denominate_balance(token_metadata)) - .transpose()?) + pub fn storage_deposit_limit(&self) -> Option { + self.storage_deposit_limit } /// Verbosity for message reporting. @@ -234,12 +157,3 @@ where &self.verbosity } } - -impl Default for ExtrinsicOpts -where - E::Balance: From, -{ - fn default() -> Self { - ExtrinsicOpts::new().suri("Alice".to_string()).done() - } -} diff --git a/crates/extrinsics/src/instantiate.rs b/crates/extrinsics/src/instantiate.rs index 89fc4c571..3bb62b405 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -15,7 +15,6 @@ // along with cargo-contract. If not, see . use super::{ - account_id, events::{ CodeStored, ContractInstantiated, @@ -24,14 +23,10 @@ use super::{ ContractInstantiateResult, StorageDeposit, }, - state, state_call, submit_extrinsic, - BalanceVariant, ContractMessageTranscoder, ErrorVariant, - Missing, - TokenMetadata, }; use crate::{ check_env_types, @@ -49,19 +44,14 @@ use anyhow::{ use contract_transcode::Value; use ink_env::Environment; use serde::Serialize; -use subxt_signer::sr25519::Keypair; -use core::marker::PhantomData; use scale::{ Decode, Encode, }; use sp_core::Bytes; use sp_weights::Weight; -use std::{ - fmt::Display, - str::FromStr, -}; +use std::fmt::Display; use subxt::{ backend::{ legacy::LegacyRpcMethods, @@ -73,121 +63,85 @@ use subxt::{ scale_decode::IntoVisitor, scale_encode::EncodeAsType, }, + tx, Config, OnlineClient, }; -struct InstantiateOpts { +/// A builder for the instantiate command. +pub struct InstantiateCommandBuilder { constructor: String, args: Vec, - extrinsic_opts: ExtrinsicOpts, - value: BalanceVariant, + extrinsic_opts: ExtrinsicOpts, + value: E::Balance, gas_limit: Option, proof_size: Option, salt: Option, } -/// A builder for the instantiate command. -pub struct InstantiateCommandBuilder { - opts: InstantiateOpts, - _marker: PhantomData (C, ExtrinsicOptions)>, -} - -impl - InstantiateCommandBuilder> +impl InstantiateCommandBuilder where - E::Balance: From + FromStr, + E::Balance: Default, + Signer: tx::Signer + Clone, + C::Hash: From<[u8; 32]>, { /// Returns a clean builder for [`InstantiateExec`]. - pub fn new() -> InstantiateCommandBuilder> { + pub fn new( + extrinsic_opts: ExtrinsicOpts, + ) -> InstantiateCommandBuilder { InstantiateCommandBuilder { - opts: InstantiateOpts { - constructor: String::from("new"), - args: Vec::new(), - extrinsic_opts: ExtrinsicOpts::default(), - value: "0".parse().unwrap(), - gas_limit: None, - proof_size: None, - salt: None, - }, - _marker: PhantomData, - } - } - - /// Sets the extrinsic operation. - pub fn extrinsic_opts( - self, - extrinsic_opts: ExtrinsicOpts, - ) -> InstantiateCommandBuilder { - InstantiateCommandBuilder { - opts: InstantiateOpts { - extrinsic_opts, - ..self.opts - }, - _marker: PhantomData, + constructor: String::from("new"), + args: Vec::new(), + extrinsic_opts, + value: Default::default(), + gas_limit: None, + proof_size: None, + salt: None, } } -} -impl Default - for InstantiateCommandBuilder> -where - E::Balance: From + FromStr, -{ - fn default() -> Self { - Self::new() - } -} - -impl InstantiateCommandBuilder { /// Sets the name of the contract constructor to call. pub fn constructor>(self, constructor: T) -> Self { let mut this = self; - this.opts.constructor = constructor.into(); + this.constructor = constructor.into(); this } /// Sets the constructor arguments. pub fn args(self, args: Vec) -> Self { let mut this = self; - this.opts.args = args.into_iter().map(|arg| arg.to_string()).collect(); + this.args = args.into_iter().map(|arg| arg.to_string()).collect(); this } /// Sets the initial balance to transfer to the instantiated contract. - pub fn value(self, value: BalanceVariant) -> Self { + pub fn value(self, value: E::Balance) -> Self { let mut this = self; - this.opts.value = value; + this.value = value; this } /// Sets the maximum amount of gas to be used for this command. pub fn gas_limit(self, gas_limit: Option) -> Self { let mut this = self; - this.opts.gas_limit = gas_limit; + this.gas_limit = gas_limit; this } /// Sets the maximum proof size for this instantiation. pub fn proof_size(self, proof_size: Option) -> Self { let mut this = self; - this.opts.proof_size = proof_size; + this.proof_size = proof_size; this } /// Sets the salt used in the address derivation of the new contract. pub fn salt(self, salt: Option) -> Self { let mut this = self; - this.opts.salt = salt; + this.salt = salt; this } -} -impl InstantiateCommandBuilder -where - C::Hash: From<[u8; 32]>, - E::Balance: From, -{ /// Preprocesses contract artifacts and options for instantiation. /// /// This function prepares the required data for instantiating a contract based on the @@ -197,37 +151,31 @@ where /// /// Returns the [`InstantiateExec`] containing the preprocessed data for the /// instantiation, or an error in case of failure. - pub async fn done(self) -> Result> { - let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; + pub async fn done(self) -> Result> { + let artifacts = self.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; - let data = transcoder.encode(&self.opts.constructor, &self.opts.args)?; - let signer = self.opts.extrinsic_opts.signer()?; - let url = self.opts.extrinsic_opts.url(); + let data = transcoder.encode(&self.constructor, &self.args)?; + let url = self.extrinsic_opts.url(); let code = if let Some(code) = artifacts.code { Code::Upload(code.0) } else { let code_hash = artifacts.code_hash()?; Code::Existing(code_hash.into()) }; - let salt = self.opts.salt.clone().map(|s| s.0).unwrap_or_default(); + let salt = self.salt.clone().map(|s| s.0).unwrap_or_default(); let rpc_cli = RpcClient::from_url(&url).await?; let client = OnlineClient::from_rpc_client(rpc_cli.clone()).await?; - check_env_types(&client, &transcoder, self.opts.extrinsic_opts.verbosity())?; + check_env_types(&client, &transcoder, self.extrinsic_opts.verbosity())?; let rpc = LegacyRpcMethods::new(rpc_cli); - let token_metadata = TokenMetadata::query(&rpc).await?; - let args = InstantiateArgs { - constructor: self.opts.constructor.clone(), - raw_args: self.opts.args.clone(), - value: self.opts.value.denominate_balance(&token_metadata)?, - gas_limit: self.opts.gas_limit, - proof_size: self.opts.proof_size, - storage_deposit_limit: self - .opts - .extrinsic_opts - .storage_deposit_limit_balance(&token_metadata)?, + constructor: self.constructor.clone(), + raw_args: self.args.clone(), + value: self.value, + gas_limit: self.gas_limit, + proof_size: self.proof_size, + storage_deposit_limit: self.extrinsic_opts.storage_deposit_limit(), code, data, salt, @@ -235,13 +183,11 @@ where Ok(InstantiateExec { args, - opts: self.opts.extrinsic_opts.clone(), + opts: self.extrinsic_opts, url, rpc, client, - signer, transcoder, - token_metadata, }) } } @@ -304,26 +250,23 @@ impl InstantiateArgs { } } -pub struct InstantiateExec { - opts: ExtrinsicOpts, +pub struct InstantiateExec { + opts: ExtrinsicOpts, args: InstantiateArgs, url: String, rpc: LegacyRpcMethods, client: OnlineClient, - signer: Keypair, transcoder: ContractMessageTranscoder, - token_metadata: TokenMetadata, } -impl InstantiateExec +impl InstantiateExec where C::AccountId: Decode, - C::Signature: From, >::OtherParams: Default, - C::Address: From, C::Hash: IntoVisitor + EncodeAsType, - C::AccountId: From + IntoVisitor + Display, + C::AccountId: IntoVisitor + Display, E::Balance: Serialize, + Signer: tx::Signer + Clone, { /// Decodes the result of a simulated contract instantiation. /// @@ -377,7 +320,7 @@ where ) -> Result> { let storage_deposit_limit = self.args.storage_deposit_limit; let call_request = InstantiateRequest:: { - origin: account_id::(&self.signer), + origin: self.opts.signer().account_id(), value: self.args.value, gas_limit: None, storage_deposit_limit, @@ -403,24 +346,23 @@ where ) .build(); - let result = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; + let events = + submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?; // The CodeStored event is only raised if the contract has not already been // uploaded. - let code_hash = result + let code_hash = events .find_first::>()? .map(|code_stored| code_stored.code_hash); - let instantiated = result + let instantiated = events .find_last::>()? .ok_or_else(|| anyhow!("Failed to find Instantiated event"))?; Ok(InstantiateExecResult { - result, + events, code_hash, contract_address: instantiated.contract, - token_metadata: self.token_metadata.clone(), }) } @@ -439,18 +381,17 @@ where ) .build(); - let result = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; + let events = + submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?; - let instantiated = result + let instantiated = events .find_first::>()? .ok_or_else(|| anyhow!("Failed to find Instantiated event"))?; Ok(InstantiateExecResult { - result, + events, code_hash: None, contract_address: instantiated.contract, - token_metadata: self.token_metadata.clone(), }) } @@ -521,7 +462,7 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } @@ -540,22 +481,17 @@ where &self.client } - /// Returns the signer. - pub fn signer(&self) -> &Keypair { - &self.signer - } - /// Returns the contract message transcoder. pub fn transcoder(&self) -> &ContractMessageTranscoder { &self.transcoder } } +/// A struct representing the result of an instantiate command execution. pub struct InstantiateExecResult { - pub result: ExtrinsicEvents, + pub events: ExtrinsicEvents, pub code_hash: Option, pub contract_address: C::AccountId, - pub token_metadata: TokenMetadata, } /// Result of the contract call diff --git a/crates/extrinsics/src/integration_tests.rs b/crates/extrinsics/src/integration_tests.rs index f89310a23..22aa2bfcb 100644 --- a/crates/extrinsics/src/integration_tests.rs +++ b/crates/extrinsics/src/integration_tests.rs @@ -17,6 +17,7 @@ use crate::{ CallCommandBuilder, CallExec, + DisplayEvents, ExtrinsicOptsBuilder, InstantiateCommandBuilder, InstantiateExecResult, @@ -41,6 +42,10 @@ use subxt::{ OnlineClient, PolkadotConfig as DefaultConfig, }; +use subxt_signer::{ + sr25519::Keypair, + SecretUri, +}; const CONTRACTS_NODE: &str = "substrate-contracts-node"; @@ -467,13 +472,13 @@ async fn api_build_upload_instantiate_call() { let contract_file = project_path.join("target/ink/flipper.contract"); // upload the contract - let opts = ExtrinsicOptsBuilder::default() + let uri = ::from_str("//Alice").unwrap(); + let signer = Keypair::from_uri(&uri).unwrap(); + let opts = ExtrinsicOptsBuilder::new(signer) .file(Some(contract_file)) - .suri("//Alice") .done(); - let upload: UploadExec = - UploadCommandBuilder::default() - .extrinsic_opts(opts.clone()) + let upload: UploadExec = + UploadCommandBuilder::new(opts.clone()) .done() .await .unwrap(); @@ -482,8 +487,7 @@ async fn api_build_upload_instantiate_call() { upload_result.unwrap(); // instantiate the contract - let instantiate = InstantiateCommandBuilder::default() - .extrinsic_opts(opts.clone()) + let instantiate = InstantiateCommandBuilder::new(opts.clone()) .constructor("new") .args(["true"].to_vec()) .done() @@ -498,10 +502,12 @@ async fn api_build_upload_instantiate_call() { // call the contract // the value should be true - let call: CallExec = CallCommandBuilder::default() - .extrinsic_opts(opts.clone()) - .message("get") - .contract(instantiate_result.contract_address.clone()) + let call: CallExec = + CallCommandBuilder::new( + instantiate_result.contract_address.clone(), + "get", + opts.clone(), + ) .done() .await .unwrap(); @@ -525,25 +531,36 @@ async fn api_build_upload_instantiate_call() { // call the contract // flip the value - let call: CallExec = CallCommandBuilder::default() - .extrinsic_opts(opts.clone()) - .message("flip") - .contract(instantiate_result.contract_address.clone()) + let call: CallExec = + CallCommandBuilder::new( + instantiate_result.contract_address.clone(), + "flip", + opts.clone(), + ) .done() .await .unwrap(); let call_result = call.call(None).await; assert!(call_result.is_ok(), "call failed"); let call_result = call_result.unwrap(); - let output = call_result.to_json().unwrap(); + let output = DisplayEvents::from_events::( + &call_result, + None, + &call.client().metadata(), + ) + .unwrap() + .to_json() + .unwrap(); assert!(output.contains("ExtrinsicSuccess"), "{:#?}", output); // call the contract // make sure the value has been flipped - let call: CallExec = CallCommandBuilder::default() - .extrinsic_opts(opts.clone()) - .message("get") - .contract(instantiate_result.contract_address.clone()) + let call: CallExec = + CallCommandBuilder::new( + instantiate_result.contract_address.clone(), + "get", + opts.clone(), + ) .done() .await .unwrap(); @@ -595,13 +612,13 @@ async fn api_build_upload_remove() { let contract_file = project_path.join("target/ink/incrementer.contract"); // upload the contract - let opts = ExtrinsicOptsBuilder::default() + let uri = ::from_str("//Alice").unwrap(); + let signer = Keypair::from_uri(&uri).unwrap(); + let opts = ExtrinsicOptsBuilder::new(signer) .file(Some(contract_file)) - .suri("//Alice") .done(); - let upload: UploadExec = - UploadCommandBuilder::default() - .extrinsic_opts(opts.clone()) + let upload: UploadExec = + UploadCommandBuilder::new(opts.clone()) .done() .await .unwrap(); @@ -613,9 +630,8 @@ async fn api_build_upload_remove() { assert_eq!(64, code_hash.len(), "{code_hash:?}"); // remove the contract - let remove: RemoveExec = - RemoveCommandBuilder::default() - .extrinsic_opts(opts.clone()) + let remove: RemoveExec = + RemoveCommandBuilder::new(opts.clone()) .code_hash(Some(code_hash_h256)) .done() .await diff --git a/crates/extrinsics/src/lib.rs b/crates/extrinsics/src/lib.rs index b4c527844..84ab3b0b6 100644 --- a/crates/extrinsics/src/lib.rs +++ b/crates/extrinsics/src/lib.rs @@ -57,7 +57,6 @@ use subxt::{ Config, OnlineClient, }; -use subxt_signer::sr25519::Keypair; pub use balance::{ BalanceVariant, @@ -88,11 +87,7 @@ pub use error::{ GenericError, }; pub use events::DisplayEvents; -pub use extrinsic_opts::{ - state, - ExtrinsicOptsBuilder, - Missing, -}; +pub use extrinsic_opts::ExtrinsicOptsBuilder; pub use instantiate::{ Code, InstantiateArgs, @@ -129,17 +124,6 @@ impl WasmCode { } } -/// Get the account id from the Keypair -pub fn account_id(keypair: &Keypair) -> T::AccountId -where - T: Config, - T::AccountId: From, - T::Address: From, - T::Signature: From, -{ - subxt::tx::Signer::::account_id(keypair) -} - /// Wait for the transaction to be included successfully into a block. /// /// # Errors @@ -162,10 +146,7 @@ where C: Config, Call: tx::TxPayload, Signer: tx::Signer, - C::Signature: From, >::OtherParams: Default, - C::Address: From, - C::AccountId: From, { let account_id = Signer::account_id(signer); let account_nonce = get_account_nonce(client, rpc, &account_id).await?; diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index cbb90537a..ac4848d02 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -15,16 +15,10 @@ // along with cargo-contract. If not, see . use super::{ - events::{ - CodeRemoved, - DisplayEvents, - }, - state, + events::CodeRemoved, submit_extrinsic, ContractMessageTranscoder, ErrorVariant, - Missing, - TokenMetadata, }; use crate::{ extrinsic_calls::RemoveCode, @@ -32,88 +26,55 @@ use crate::{ }; use anyhow::Result; -use core::marker::PhantomData; use ink_env::Environment; use subxt::{ backend::{ legacy::LegacyRpcMethods, rpc::RpcClient, }, + blocks::ExtrinsicEvents, config, ext::{ scale_decode::IntoVisitor, scale_encode::EncodeAsType, }, + tx, Config, OnlineClient, }; -use subxt_signer::sr25519::Keypair; - -pub struct RemoveOpts { - code_hash: Option, - extrinsic_opts: ExtrinsicOpts, -} /// A builder for the remove command. -pub struct RemoveCommandBuilder { - opts: RemoveOpts, - marker: PhantomData ExtrinsicOptions>, +pub struct RemoveCommandBuilder { + code_hash: Option, + extrinsic_opts: ExtrinsicOpts, } -impl - RemoveCommandBuilder> +impl RemoveCommandBuilder where - E::Balance: From, + Signer: tx::Signer + Clone, { /// Returns a clean builder for [`RemoveExec`]. - pub fn new() -> RemoveCommandBuilder> { + pub fn new( + extrinsic_opts: ExtrinsicOpts, + ) -> RemoveCommandBuilder { RemoveCommandBuilder { - opts: RemoveOpts { - code_hash: None, - extrinsic_opts: ExtrinsicOpts::default(), - }, - marker: PhantomData, + code_hash: None, + extrinsic_opts, } } - /// Sets the extrinsic operation. - pub fn extrinsic_opts( - self, - extrinsic_opts: ExtrinsicOpts, - ) -> RemoveCommandBuilder { - RemoveCommandBuilder { - opts: RemoveOpts { - extrinsic_opts, - ..self.opts - }, - marker: PhantomData, - } - } -} - -impl Default - for RemoveCommandBuilder> -where - E::Balance: From, -{ - fn default() -> Self { - Self::new() - } -} - -impl RemoveCommandBuilder { /// Sets the hash of the smart contract code already uploaded to the chain. pub fn code_hash(self, code_hash: Option) -> Self { let mut this = self; - this.opts.code_hash = code_hash; + this.code_hash = code_hash; this } } -impl RemoveCommandBuilder +impl RemoveCommandBuilder where C::Hash: From<[u8; 32]>, - E::Balance: From, + Signer: tx::Signer + Clone, { /// Preprocesses contract artifacts and options for subsequent removal of contract /// code. @@ -125,16 +86,15 @@ where /// /// Returns the `RemoveExec` containing the preprocessed data for the contract code /// removal, or an error in case of failure. - pub async fn done(self) -> Result> { - let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; + pub async fn done(self) -> Result> { + let artifacts = self.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; - let signer = self.opts.extrinsic_opts.signer()?; let artifacts_path = artifacts.artifact_path().to_path_buf(); - let final_code_hash = match (self.opts.code_hash.as_ref(), artifacts.code.as_ref()) { + let final_code_hash = match (self.code_hash.as_ref(), artifacts.code.as_ref()) { (Some(code_h), _) => Ok(*code_h), - (None, Some(_)) => artifacts.code_hash().map(Into::into), + (None, Some(_)) => artifacts.code_hash().map(|h| h.into() ), (None, None) => Err(anyhow::anyhow!( "No code_hash was provided or contract code was not found from artifact \ file {}. Please provide a code hash with --code-hash argument or specify the \ @@ -143,42 +103,35 @@ where )), }?; - let url = self.opts.extrinsic_opts.url(); + let url = self.extrinsic_opts.url(); let rpc_cli = RpcClient::from_url(&url).await?; let client = OnlineClient::::from_rpc_client(rpc_cli.clone()).await?; let rpc = LegacyRpcMethods::::new(rpc_cli); - let token_metadata = TokenMetadata::query(&rpc).await?; - Ok(RemoveExec { final_code_hash, - opts: self.opts.extrinsic_opts.clone(), + opts: self.extrinsic_opts, rpc, client, transcoder, - signer, - token_metadata, }) } } -pub struct RemoveExec { +pub struct RemoveExec { final_code_hash: C::Hash, - opts: ExtrinsicOpts, + opts: ExtrinsicOpts, rpc: LegacyRpcMethods, client: OnlineClient, transcoder: ContractMessageTranscoder, - signer: Keypair, - token_metadata: TokenMetadata, } -impl RemoveExec +impl RemoveExec where C::Hash: IntoVisitor + EncodeAsType, - C::AccountId: IntoVisitor + From, - C::Address: From, - C::Signature: From, + C::AccountId: IntoVisitor, >::OtherParams: Default, + Signer: tx::Signer + Clone, { /// Removes a contract code from the blockchain. /// @@ -189,9 +142,7 @@ where /// /// Returns the `RemoveResult` containing the events generated from the contract /// code removal, or an error in case of failure. - pub async fn remove_code( - &self, - ) -> Result, ErrorVariant> + pub async fn remove_code(&self) -> Result, ErrorVariant> where E::Balance: IntoVisitor + Into, { @@ -199,19 +150,14 @@ where let call = RemoveCode::new(code_hash).build(); - let result = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; - let display_events = DisplayEvents::from_events::( - &result, - Some(&self.transcoder), - &self.client.metadata(), - )?; + let events = + submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?; let code_removed = - result.find_first::>()?; + events.find_first::>()?; Ok(RemoveResult { code_removed, - display_events, + events, }) } @@ -221,7 +167,7 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } @@ -234,19 +180,10 @@ where pub fn transcoder(&self) -> &ContractMessageTranscoder { &self.transcoder } - - /// Returns the signer. - pub fn signer(&self) -> &Keypair { - &self.signer - } - - /// Returns the token metadata. - pub fn token_metadata(&self) -> &TokenMetadata { - &self.token_metadata - } } -pub struct RemoveResult { - pub code_removed: Option>, - pub display_events: DisplayEvents, +/// A struct representing the result of an remove command execution. +pub struct RemoveResult { + pub code_removed: Option>, + pub events: ExtrinsicEvents, } diff --git a/crates/extrinsics/src/upload.rs b/crates/extrinsics/src/upload.rs index 21fd762d4..2de54a3f9 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -15,18 +15,11 @@ // along with cargo-contract. If not, see . use super::{ - account_id, - events::{ - CodeStored, - DisplayEvents, - }, + events::CodeStored, pallet_contracts_primitives::CodeUploadResult, - state, state_call, submit_extrinsic, ErrorVariant, - Missing, - TokenMetadata, WasmCode, }; use crate::{ @@ -36,7 +29,6 @@ use crate::{ }; use anyhow::Result; use contract_transcode::ContractMessageTranscoder; -use core::marker::PhantomData; use ink_env::Environment; use scale::Encode; use subxt::{ @@ -44,65 +36,33 @@ use subxt::{ legacy::LegacyRpcMethods, rpc::RpcClient, }, + blocks::ExtrinsicEvents, config, ext::{ scale_decode::IntoVisitor, scale_encode::EncodeAsType, }, + tx, Config, OnlineClient, }; -use subxt_signer::sr25519::Keypair; - -struct UploadOpts { - extrinsic_opts: ExtrinsicOpts, -} /// A builder for the upload command. -pub struct UploadCommandBuilder { - opts: UploadOpts, - marker: PhantomData ExtrinsicOptions>, +pub struct UploadCommandBuilder { + extrinsic_opts: ExtrinsicOpts, } -impl UploadCommandBuilder> +impl UploadCommandBuilder where - E::Balance: From, + Signer: tx::Signer + Clone, { /// Returns a clean builder for [`UploadExec`]. - pub fn new() -> UploadCommandBuilder> { - UploadCommandBuilder { - opts: UploadOpts { - extrinsic_opts: ExtrinsicOpts::default(), - }, - marker: PhantomData, - } - } - - /// Sets the extrinsic operation. - pub fn extrinsic_opts( - self, - extrinsic_opts: ExtrinsicOpts, - ) -> UploadCommandBuilder { - UploadCommandBuilder { - opts: UploadOpts { extrinsic_opts }, - marker: PhantomData, - } - } -} - -impl Default for UploadCommandBuilder> -where - E::Balance: From, -{ - fn default() -> Self { - Self::new() + pub fn new( + extrinsic_opts: ExtrinsicOpts, + ) -> UploadCommandBuilder { + UploadCommandBuilder { extrinsic_opts } } -} -impl UploadCommandBuilder -where - E::Balance: From, -{ /// Preprocesses contract artifacts and options for subsequent upload. /// /// This function prepares the necessary data for uploading a contract @@ -112,10 +72,9 @@ where /// /// Returns the `UploadExec` containing the preprocessed data for the upload or /// execution. - pub async fn done(self) -> Result> { - let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; + pub async fn done(self) -> Result> { + let artifacts = self.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; - let signer = self.opts.extrinsic_opts.signer()?; let artifacts_path = artifacts.artifact_path().to_path_buf(); let code = artifacts.code.ok_or_else(|| { @@ -125,44 +84,36 @@ where ) })?; - let url = self.opts.extrinsic_opts.url(); + let url = self.extrinsic_opts.url(); let rpc_cli = RpcClient::from_url(&url).await?; let client = OnlineClient::from_rpc_client(rpc_cli.clone()).await?; - check_env_types(&client, &transcoder, self.opts.extrinsic_opts.verbosity())?; + check_env_types(&client, &transcoder, self.extrinsic_opts.verbosity())?; let rpc = LegacyRpcMethods::new(rpc_cli); - let token_metadata = TokenMetadata::query(&rpc).await?; - Ok(UploadExec { - opts: self.opts.extrinsic_opts.clone(), + opts: self.extrinsic_opts, rpc, client, code, - signer, - token_metadata, transcoder, }) } } -pub struct UploadExec { - opts: ExtrinsicOpts, +pub struct UploadExec { + opts: ExtrinsicOpts, rpc: LegacyRpcMethods, client: OnlineClient, code: WasmCode, - signer: Keypair, - token_metadata: TokenMetadata, transcoder: ContractMessageTranscoder, } -impl UploadExec +impl UploadExec where C::Hash: IntoVisitor, - C::AccountId: IntoVisitor + From, - C::Address: From, - C::Signature: From, + C::AccountId: IntoVisitor, >::OtherParams: Default, - E::Balance: From, + Signer: tx::Signer + Clone, { /// Uploads contract code to a specified URL using a JSON-RPC call. /// @@ -171,11 +122,9 @@ where /// then sends the request using the provided URL. This operation does not modify /// the state of the blockchain. pub async fn upload_code_rpc(&self) -> Result> { - let storage_deposit_limit = self - .opts - .storage_deposit_limit_balance(&self.token_metadata)?; + let storage_deposit_limit = self.opts.storage_deposit_limit(); let call_request = CodeUploadRequest { - origin: account_id::(&self.signer), + origin: self.opts.signer().account_id(), code: self.code.0.clone(), storage_deposit_limit, determinism: Determinism::Enforced, @@ -189,10 +138,8 @@ where /// blockchain, utilizing the provided options. /// The function handles the necessary interactions with the blockchain's runtime /// API to ensure the successful upload of the code. - pub async fn upload_code(&self) -> Result, ErrorVariant> { - let storage_deposit_limit = self - .opts - .storage_deposit_limit_balance(&self.token_metadata)?; + pub async fn upload_code(&self) -> Result, ErrorVariant> { + let storage_deposit_limit = self.opts.storage_deposit_limit(); let call = UploadCode::new( self.code.clone(), @@ -201,20 +148,18 @@ where ) .build(); - let result = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; - let display_events = - DisplayEvents::from_events::(&result, None, &self.client.metadata())?; + let events = + submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?; - let code_stored = result.find_first::>()?; - Ok(UploadResult:: { + let code_stored = events.find_first::>()?; + Ok(UploadResult { code_stored, - display_events, + events, }) } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } @@ -228,16 +173,6 @@ where &self.code } - /// Returns the signer. - pub fn signer(&self) -> &Keypair { - &self.signer - } - - /// Returns the token metadata. - pub fn token_metadata(&self) -> &TokenMetadata { - &self.token_metadata - } - /// Returns the contract message transcoder. pub fn transcoder(&self) -> &ContractMessageTranscoder { &self.transcoder @@ -253,9 +188,10 @@ struct CodeUploadRequest { determinism: Determinism, } -pub struct UploadResult { - pub code_stored: Option>, - pub display_events: DisplayEvents, +/// A struct representing the result of an upload command execution. +pub struct UploadResult { + pub code_stored: Option>, + pub events: ExtrinsicEvents, } /// Copied from `pallet-contracts` to additionally implement `scale_encode::EncodeAsType`.