diff --git a/src/cmd/extrinsics/call.rs b/src/cmd/extrinsics/call.rs index 77609ffb4..ea275a481 100644 --- a/src/cmd/extrinsics/call.rs +++ b/src/cmd/extrinsics/call.rs @@ -17,14 +17,15 @@ use super::{ display_contract_exec_result, display_events, + error_details, load_metadata, parse_balance, prompt_confirm_tx, + state_call, submit_extrinsic, Balance, Client, ContractMessageTranscoder, - ContractsRpcError, DefaultConfig, ExtrinsicOpts, PairSigner, @@ -38,30 +39,16 @@ use anyhow::{ anyhow, Result, }; -use jsonrpsee::{ - core::client::ClientT, - rpc_params, - ws_client::WsClientBuilder, -}; -use pallet_contracts_primitives::{ - ContractResult, - ExecReturnValue, -}; -use serde::Serialize; -use sp_core::Bytes; -use std::{ - fmt::Debug, - result, -}; + +use pallet_contracts_primitives::ContractExecResult; +use scale::Encode; + +use std::fmt::Debug; use subxt::{ - rpc::NumberOrHex, Config, OnlineClient, }; -type ContractExecResult = - ContractResult, Balance>; - #[derive(Debug, clap::Args)] #[clap(name = "call", about = "Call a contract")] pub struct CallCommand { @@ -124,7 +111,7 @@ impl CallCommand { display_contract_exec_result::<_, DEFAULT_KEY_COL_WIDTH>(&result) } Err(ref err) => { - let err = err.error_details(&client.metadata())?; + let err = error_details(err, &client.metadata())?; name_value_println!("Result", err, MAX_KEY_COL_WIDTH); display_contract_exec_result::<_, MAX_KEY_COL_WIDTH>(&result) } @@ -137,28 +124,21 @@ impl CallCommand { async fn call_dry_run( &self, - data: Vec, + input_data: Vec, signer: &PairSigner, - ) -> Result { + ) -> Result> { let url = self.extrinsic_opts.url_to_string(); - let cli = WsClientBuilder::default().build(&url).await?; - let gas_limit = self.gas_limit.as_ref().unwrap_or(&5_000_000_000_000); - let storage_deposit_limit = self - .extrinsic_opts - .storage_deposit_limit - .as_ref() - .map(|limit| NumberOrHex::Hex((*limit).into())); - let call_request = RpcCallRequest { + let gas_limit = *self.gas_limit.as_ref().unwrap_or(&5_000_000_000_000); + let storage_deposit_limit = self.extrinsic_opts.storage_deposit_limit; + let call_request = CallRequest { origin: signer.account_id().clone(), dest: self.contract.clone(), - value: NumberOrHex::Hex(self.value.into()), - gas_limit: NumberOrHex::Number(*gas_limit), + value: self.value, + gas_limit, storage_deposit_limit, - input_data: Bytes(data), + input_data, }; - let params = rpc_params![call_request]; - let result = cli.request("contracts_call", params).await?; - Ok(result) + state_call(&url, "ContractsApi_call", call_request).await } async fn call( @@ -230,7 +210,7 @@ impl CallCommand { Ok(gas_limit) } Err(ref err) => { - let err = err.error_details(&client.metadata())?; + let err = error_details(err, &client.metadata())?; name_value_println!("Result", err, MAX_KEY_COL_WIDTH); display_contract_exec_result::<_, MAX_KEY_COL_WIDTH>(&call_result)?; Err(anyhow!("Pre-submission dry-run failed. Use --skip-dry-run to skip this step.")) @@ -241,14 +221,13 @@ impl CallCommand { /// A struct that encodes RPC parameters required for a call to a smart contract. /// -/// Copied from `pallet-contracts-rpc`. -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct RpcCallRequest { +/// Copied from `pallet-contracts-rpc-runtime-api`. +#[derive(Encode)] +pub struct CallRequest { origin: ::AccountId, dest: ::AccountId, - value: NumberOrHex, - gas_limit: NumberOrHex, - storage_deposit_limit: Option, - input_data: Bytes, + value: Balance, + gas_limit: u64, + storage_deposit_limit: Option, + input_data: Vec, } diff --git a/src/cmd/extrinsics/instantiate.rs b/src/cmd/extrinsics/instantiate.rs index b07ccaa9e..8e7d23a34 100644 --- a/src/cmd/extrinsics/instantiate.rs +++ b/src/cmd/extrinsics/instantiate.rs @@ -17,16 +17,17 @@ use super::{ display_contract_exec_result, display_events, + error_details, parse_balance, prompt_confirm_tx, runtime_api::api, + state_call, submit_extrinsic, Balance, Client, CodeHash, ContractAccount, ContractMessageTranscoder, - ContractsRpcError, DefaultConfig, ExtrinsicOpts, PairSigner, @@ -43,16 +44,10 @@ use anyhow::{ Context, Result, }; -use jsonrpsee::{ - core::client::ClientT, - rpc_params, - ws_client::WsClientBuilder, -}; -use pallet_contracts_primitives::{ - ContractResult, - InstantiateReturnValue, -}; -use serde::Serialize; + +use pallet_contracts_primitives::ContractInstantiateResult; + +use scale::Encode; use sp_core::{ crypto::Ss58Codec, Bytes, @@ -63,19 +58,12 @@ use std::{ Path, PathBuf, }, - result, }; use subxt::{ - rpc::NumberOrHex, Config, OnlineClient, }; -type ContractInstantiateResult = ContractResult< - result::Result, ContractsRpcError>, - Balance, ->; - #[derive(Debug, clap::Args)] pub struct InstantiateCommand { /// Path to Wasm contract code, defaults to `./target/ink/.wasm`. @@ -145,7 +133,7 @@ impl InstantiateCommand { tracing::debug!("Contract code path: {}", wasm_path.display()); let code = fs::read(&wasm_path) .context(format!("Failed to read from {}", wasm_path.display()))?; - Ok(Code::Upload(code.into())) + Ok(Code::Upload(code)) } let code = match (self.wasm_path.as_ref(), self.code_hash.as_ref()) { @@ -162,7 +150,7 @@ impl InstantiateCommand { } (None, Some(code_hash)) => Ok(Code::Existing(*code_hash)), }?; - let salt = self.salt.clone().unwrap_or_else(|| Bytes(Vec::new())); + let salt = self.salt.clone().map(|s| s.0).unwrap_or_default(); let args = InstantiateArgs { constructor: self.constructor.clone(), @@ -199,7 +187,7 @@ struct InstantiateArgs { gas_limit: Option, storage_deposit_limit: Option, data: Vec, - salt: Bytes, + salt: Vec, } pub struct Exec<'a> { @@ -243,7 +231,7 @@ impl<'a> Exec<'a> { } Err(ref err) => { let metadata = self.client.metadata(); - let err = err.error_details(&metadata)?; + let err = error_details(err, &metadata)?; name_value_println!("Result", err, MAX_KEY_COL_WIDTH); display_contract_exec_result::<_, MAX_KEY_COL_WIDTH>(&result) } @@ -269,7 +257,7 @@ impl<'a> Exec<'a> { async fn instantiate_with_code( &self, - code: Bytes, + code: Vec, ) -> Result<(Option, ContractAccount)> { let gas_limit = self .pre_submit_dry_run_gas_estimate(Code::Upload(code.clone())) @@ -285,7 +273,7 @@ impl<'a> Exec<'a> { self.args.storage_deposit_limit, code.to_vec(), self.args.data.clone(), - self.args.salt.0.clone(), + self.args.salt.clone(), ); let result = submit_extrinsic(&self.client, &call, &self.signer).await?; @@ -331,7 +319,7 @@ impl<'a> Exec<'a> { self.args.storage_deposit_limit, code_hash, self.args.data.clone(), - self.args.salt.0.clone(), + self.args.salt.clone(), ); let result = submit_extrinsic(&self.client, &call, &self.signer).await?; @@ -356,30 +344,23 @@ impl<'a> Exec<'a> { name_value_println!("Gas limit", gas_limit.to_string(), DEFAULT_KEY_COL_WIDTH); } - async fn instantiate_dry_run(&self, code: Code) -> Result { - let cli = WsClientBuilder::default().build(&self.url).await?; - let gas_limit = self.args.gas_limit.as_ref().unwrap_or(&5_000_000_000_000); - let storage_deposit_limit = self - .args - .storage_deposit_limit - .as_ref() - .map(|limit| NumberOrHex::Hex((*limit).into())); + async fn instantiate_dry_run( + &self, + code: Code, + ) -> Result::AccountId, Balance>> + { + let gas_limit = *self.args.gas_limit.as_ref().unwrap_or(&5_000_000_000_000); + let storage_deposit_limit = self.args.storage_deposit_limit; let call_request = InstantiateRequest { origin: self.signer.account_id().clone(), - value: NumberOrHex::Hex(self.args.value.into()), - gas_limit: NumberOrHex::Number(*gas_limit), + value: self.args.value, + gas_limit, storage_deposit_limit, code, - data: self.args.data.clone().into(), + data: self.args.data.clone(), salt: self.args.salt.clone(), }; - let params = rpc_params![call_request]; - let result: ContractInstantiateResult = cli - .request("contracts_instantiate", params) - .await - .context("contracts_instantiate RPC error")?; - - Ok(result) + state_call(&self.url, "ContractsApi_instantiate", &call_request).await } /// Dry run the instantiation before tx submission. Returns the gas required estimate. @@ -406,7 +387,7 @@ impl<'a> Exec<'a> { Ok(gas_limit) } Err(ref err) => { - let err = err.error_details(&self.client.metadata())?; + let err = error_details(err, &self.client.metadata())?; name_value_println!("Result", err, MAX_KEY_COL_WIDTH); display_contract_exec_result::<_, MAX_KEY_COL_WIDTH>( &instantiate_result, @@ -418,24 +399,22 @@ impl<'a> Exec<'a> { } /// A struct that encodes RPC parameters required to instantiate a new smart contract. -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Encode)] struct InstantiateRequest { origin: ::AccountId, - value: NumberOrHex, - gas_limit: NumberOrHex, - storage_deposit_limit: Option, + value: Balance, + gas_limit: u64, + storage_deposit_limit: Option, code: Code, - data: Bytes, - salt: Bytes, + data: Vec, + salt: Vec, } /// Reference to an existing code hash or a new Wasm module. -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Encode)] enum Code { /// A Wasm module as raw bytes. - Upload(Bytes), + Upload(Vec), /// The code hash of an on-chain Wasm blob. Existing(::Hash), } diff --git a/src/cmd/extrinsics/mod.rs b/src/cmd/extrinsics/mod.rs index 2c0b9eb21..b90d5ef6a 100644 --- a/src/cmd/extrinsics/mod.rs +++ b/src/cmd/extrinsics/mod.rs @@ -30,6 +30,11 @@ use anyhow::{ Result, }; use colored::Colorize; +use jsonrpsee::{ + core::client::ClientT, + rpc_params, + ws_client::WsClientBuilder, +}; use std::{ fs::File, io::{ @@ -49,12 +54,18 @@ use crate::{ DEFAULT_KEY_COL_WIDTH, }; use pallet_contracts_primitives::ContractResult; +use scale::{ + Decode, + Encode, +}; use serde_json::Value; use sp_core::{ crypto::Pair, sr25519, + Bytes, }; use subxt::{ + ext::sp_runtime::DispatchError, tx, Config, OnlineClient, @@ -242,36 +253,25 @@ where .map_err(Into::into) } -#[derive(serde::Deserialize)] -pub struct ContractsRpcError(Value); - -impl ContractsRpcError { - pub fn error_details(&self, metadata: &subxt::Metadata) -> Result { - let try_parse_module_error = || { - let obj = self.0.as_object()?; - let module = obj.get("Module")?; - let pallet_index = module.get("index").and_then(|i| i.as_u64())?; - let error_field = module.get("error")?; - let error_index = match error_field { - Value::Array(arr) => arr.get(0).and_then(|v| v.as_u64()), - // the legacy ModuleError has a single `u8` for the error index - Value::Number(n) => n.as_u64(), - _ => None, - }?; - Some((pallet_index as u8, error_index as u8)) - }; +async fn state_call(url: &str, func: &str, args: A) -> Result { + let cli = WsClientBuilder::default().build(&url).await?; + let params = rpc_params![func, Bytes(args.encode())]; + let bytes: Bytes = cli.request("state_call", params).await?; + Ok(R::decode(&mut bytes.as_ref())?) +} - if let Some((pallet_index, error_index)) = try_parse_module_error() { - let details = metadata.error(pallet_index, error_index)?; +fn error_details(error: &DispatchError, metadata: &subxt::Metadata) -> Result { + match error { + DispatchError::Module(err) => { + let details = metadata.error(err.index, err.error)?; Ok(format!( "ModuleError: {}::{}: {:?}", details.pallet(), details.error(), details.docs() )) - } else { - Ok(format!("DispatchError: {:?}", self.0)) } + err => Ok(format!("DispatchError: {:?}", err)), } } diff --git a/src/cmd/extrinsics/upload.rs b/src/cmd/extrinsics/upload.rs index 313645297..d31daa5d0 100644 --- a/src/cmd/extrinsics/upload.rs +++ b/src/cmd/extrinsics/upload.rs @@ -16,13 +16,14 @@ use super::{ display_events, + error_details, runtime_api::api, + state_call, submit_extrinsic, Balance, Client, CodeHash, ContractMessageTranscoder, - ContractsRpcError, DefaultConfig, ExtrinsicOpts, PairSigner, @@ -33,28 +34,19 @@ use anyhow::{ Result, }; use colored::Colorize; -use jsonrpsee::{ - core::client::ClientT, - rpc_params, - ws_client::WsClientBuilder, -}; -use serde::Serialize; -use sp_core::Bytes; + +use scale::Encode; + +use pallet_contracts_primitives::CodeUploadResult; use std::{ fmt::Debug, path::PathBuf, - result, }; use subxt::{ - rpc::NumberOrHex, Config, OnlineClient, }; -type CodeUploadResult = result::Result; -type CodeUploadReturnValue = - pallet_contracts_primitives::CodeUploadReturnValue; - #[derive(Debug, clap::Args)] #[clap(name = "upload", about = "Upload a contract's code")] pub struct UploadCommand { @@ -97,7 +89,7 @@ impl UploadCommand { } Err(err) => { let metadata = client.metadata(); - let err = err.error_details(&metadata)?; + let err = error_details(&err, &metadata)?; name_value_println!("Result", err); } } @@ -127,27 +119,15 @@ impl UploadCommand { &self, code: Vec, signer: &PairSigner, - ) -> Result { + ) -> Result> { let url = self.extrinsic_opts.url_to_string(); - let cli = WsClientBuilder::default().build(&url).await?; - let storage_deposit_limit = self - .extrinsic_opts - .storage_deposit_limit - .as_ref() - .map(|limit| NumberOrHex::Hex((*limit).into())); + let storage_deposit_limit = self.extrinsic_opts.storage_deposit_limit; let call_request = CodeUploadRequest { origin: signer.account_id().clone(), - code: Bytes(code), + code, storage_deposit_limit, }; - let params = rpc_params!(call_request); - - let result: CodeUploadResult = cli - .request("contracts_upload_code", params) - .await - .context("contracts_upload_code RPC error")?; - - Ok(result) + state_call(&url, "ContractsApi_upload_code", call_request).await } async fn upload_code( @@ -177,10 +157,9 @@ impl UploadCommand { } /// A struct that encodes RPC parameters required for a call to upload a new code. -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Encode)] pub struct CodeUploadRequest { origin: ::AccountId, - code: Bytes, - storage_deposit_limit: Option, + code: Vec, + storage_deposit_limit: Option, }