From 3a2359e4978f5155ec7f814191c0464f3622e61e Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Mon, 22 Jan 2024 14:38:39 +0100 Subject: [PATCH 01/39] Add generics for info and storage commands --- Cargo.lock | 2 + crates/cargo-contract/Cargo.toml | 1 + crates/cargo-contract/src/cmd/info.rs | 39 ++++++---- crates/cargo-contract/src/cmd/mod.rs | 7 +- crates/cargo-contract/src/cmd/storage.rs | 7 +- crates/extrinsics/Cargo.toml | 1 + crates/extrinsics/src/contract_info.rs | 94 +++++++++++++---------- crates/extrinsics/src/contract_storage.rs | 31 +++++--- 8 files changed, 116 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee66eff58..6b1480208 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -790,6 +790,7 @@ dependencies = [ "crossterm", "current_platform", "hex", + "ink_env", "ink_metadata", "jsonschema", "pallet-contracts-primitives", @@ -1072,6 +1073,7 @@ dependencies = [ "futures", "hex", "ink", + "ink_env", "ink_metadata", "itertools 0.12.0", "pallet-contracts-primitives", diff --git a/crates/cargo-contract/Cargo.toml b/crates/cargo-contract/Cargo.toml index 9586ccd7c..911140e06 100644 --- a/crates/cargo-contract/Cargo.toml +++ b/crates/cargo-contract/Cargo.toml @@ -38,6 +38,7 @@ semver = "1.0" jsonschema = "0.17" schemars = "0.8" ink_metadata = "5.0.0-rc" +ink_env = "5.0.0-rc" crossterm = "0.27.0" # dependencies for extrinsics (deploying and calling a contract) diff --git a/crates/cargo-contract/src/cmd/info.rs b/crates/cargo-contract/src/cmd/info.rs index 4bf72a047..e9e19f49b 100644 --- a/crates/cargo-contract/src/cmd/info.rs +++ b/crates/cargo-contract/src/cmd/info.rs @@ -26,12 +26,14 @@ use contract_extrinsics::{ fetch_contract_info, fetch_wasm_code, url_to_string, - Balance, - CodeHash, ContractInfo, ErrorVariant, TrieId, }; +use ink_env::{ + DefaultEnvironment, + Environment, +}; use std::{ fmt::Debug, io::Write, @@ -103,7 +105,10 @@ impl InfoCommand { .as_ref() .expect("Contract argument was not provided"); - let info_to_json = fetch_contract_info(contract, &rpc, &client).await?; + let info_to_json = fetch_contract_info::( + contract, &rpc, &client, + ) + .await?; let wasm_code = fetch_wasm_code(&client, &rpc, info_to_json.code_hash()).await?; @@ -122,15 +127,19 @@ impl InfoCommand { } else if self.output_json { println!( "{}", - serde_json::to_string_pretty(&ExtendedContractInfo::new( - info_to_json, - &wasm_code + serde_json::to_string_pretty(&ExtendedContractInfo::< + ::Hash, + ::Balance, + >::new( + info_to_json, &wasm_code ))? ) } else { - basic_display_format_extended_contract_info(&ExtendedContractInfo::new( - info_to_json, - &wasm_code, + basic_display_format_extended_contract_info(&ExtendedContractInfo::< + ::Hash, + ::Balance, + >::new( + info_to_json, &wasm_code )) } Ok(()) @@ -139,17 +148,21 @@ impl InfoCommand { } #[derive(serde::Serialize)] -pub struct ExtendedContractInfo { +pub struct ExtendedContractInfo { pub trie_id: TrieId, - pub code_hash: CodeHash, + pub code_hash: Hash, pub storage_items: u32, pub storage_items_deposit: Balance, pub storage_total_deposit: Balance, pub source_language: String, } -impl ExtendedContractInfo { - pub fn new(contract_info: ContractInfo, code: &[u8]) -> Self { +impl ExtendedContractInfo +where + Hash: serde::Serialize + Copy, + Balance: serde::Serialize + Copy, +{ + pub fn new(contract_info: ContractInfo, code: &[u8]) -> Self { let language = match determine_language(code).ok() { Some(lang) => lang.to_string(), None => "Unknown".to_string(), diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index 7795dc9f7..4dc811f7f 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -69,6 +69,7 @@ use contract_extrinsics::{ Balance, BalanceVariant, }; +use core::fmt; use pallet_contracts_primitives::ContractResult; use std::io::{ self, @@ -224,7 +225,11 @@ pub fn print_gas_required_success(gas: Weight) { } /// Display contract information in a formatted way -pub fn basic_display_format_extended_contract_info(info: &ExtendedContractInfo) { +pub fn basic_display_format_extended_contract_info( + info: &ExtendedContractInfo, +) where + Hash: fmt::Debug, +{ name_value_println!("TrieId", info.trie_id, MAX_KEY_COL_WIDTH); name_value_println!( "Code Hash", diff --git a/crates/cargo-contract/src/cmd/storage.rs b/crates/cargo-contract/src/cmd/storage.rs index 85520e50f..ce6834b73 100644 --- a/crates/cargo-contract/src/cmd/storage.rs +++ b/crates/cargo-contract/src/cmd/storage.rs @@ -28,6 +28,7 @@ use contract_extrinsics::{ ErrorVariant, }; use crossterm::terminal; +use ink_env::DefaultEnvironment; use std::{ cmp, path::PathBuf, @@ -65,8 +66,10 @@ pub struct StorageCommand { impl StorageCommand { pub async fn run(&self) -> Result<(), ErrorVariant> { - let rpc = ContractStorageRpc::::new(&self.url).await?; - let storage_layout = ContractStorage::::new(rpc); + let rpc = ContractStorageRpc::::new(&self.url) + .await?; + let storage_layout = + ContractStorage::::new(rpc); if self.raw { let storage_data = storage_layout diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index 0f1b2a738..09008ee2b 100644 --- a/crates/extrinsics/Cargo.toml +++ b/crates/extrinsics/Cargo.toml @@ -41,6 +41,7 @@ subxt = "0.33.0" subxt-signer = { version = "0.33.0", features = ["subxt", "sr25519"] } hex = "0.4.3" ink_metadata = "5.0.0-rc" +ink_env = "5.0.0-rc" [dev-dependencies] ink = "5.0.0-rc" diff --git a/crates/extrinsics/src/contract_info.rs b/crates/extrinsics/src/contract_info.rs index 73f4f91d0..f5c66228c 100644 --- a/crates/extrinsics/src/contract_info.rs +++ b/crates/extrinsics/src/contract_info.rs @@ -14,13 +14,7 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use super::{ - get_best_block, - Balance, - Client, - CodeHash, - DefaultConfig, -}; +use super::get_best_block; use anyhow::{ anyhow, Result, @@ -31,6 +25,7 @@ use std::fmt::{ Formatter, }; +use ink_env::Environment; use scale::Decode; use std::option::Option; use subxt::{ @@ -52,13 +47,14 @@ use subxt::{ }; /// Return the account data for an account ID. -async fn get_account_balance( +async fn get_account_balance( account: &C::AccountId, rpc: &LegacyRpcMethods, client: &OnlineClient, -) -> Result +) -> Result> where C::AccountId: AsRef<[u8]>, + E::Balance: IntoVisitor, { let storage_query = subxt::dynamic::storage("System", "Account", vec![Value::from_bytes(account)]); @@ -71,19 +67,21 @@ where .await? .ok_or_else(|| anyhow::anyhow!("Failed to fetch account data"))?; - let data = account.as_type::()?.data; + let data = account.as_type::>()?.data; Ok(data) } /// Fetch the contract info from the storage using the provided client. -pub async fn fetch_contract_info( +pub async fn fetch_contract_info( contract: &C::AccountId, rpc: &LegacyRpcMethods, client: &OnlineClient, -) -> Result +) -> Result> where C::AccountId: AsRef<[u8]> + Display + IntoVisitor, + C::Hash: IntoVisitor, DecodeError: From<<::Visitor as Visitor>::Error>, + E::Balance: IntoVisitor, { let best_block = get_best_block(rpc).await?; @@ -105,26 +103,28 @@ where })?; let contract_info_raw = - ContractInfoRaw::::new(contract.clone(), contract_info_value)?; + ContractInfoRaw::::new(contract.clone(), contract_info_value)?; let deposit_account = contract_info_raw.get_deposit_account(); - let deposit_account_data = get_account_balance(deposit_account, rpc, client).await?; + let deposit_account_data = + get_account_balance::(deposit_account, rpc, client).await?; Ok(contract_info_raw.into_contract_info(deposit_account_data)) } /// Struct representing contract info, supporting deposit on either the main or secondary /// account. -struct ContractInfoRaw { +struct ContractInfoRaw { deposit_account: C::AccountId, - contract_info: ContractInfoOf, + contract_info: ContractInfoOf, deposit_on_main_account: bool, } -impl ContractInfoRaw +impl ContractInfoRaw where - C: Config, C::AccountId: IntoVisitor, + C::Hash: IntoVisitor, DecodeError: From<<::Visitor as Visitor>::Error>, + E::Balance: IntoVisitor, { /// Create a new instance of `ContractInfoRaw` based on the provided contract and /// contract info value. Determines whether it's a main or secondary account deposit. @@ -132,7 +132,8 @@ where contract_account: C::AccountId, contract_info_value: DecodedValueThunk, ) -> Result { - let contract_info = contract_info_value.as_type::()?; + let contract_info = + contract_info_value.as_type::>()?; // Pallet-contracts [>=10, <15] store the contract's deposit as a free balance // in a secondary account (deposit account). Other versions store it as // reserved balance on the main contract's account. If the @@ -161,7 +162,10 @@ where } /// Convert `ContractInfoRaw` to `ContractInfo` - pub fn into_contract_info(self, deposit: AccountData) -> ContractInfo { + pub fn into_contract_info( + self, + deposit: AccountData, + ) -> ContractInfo { let total_deposit = if self.deposit_on_main_account { deposit.reserved } else { @@ -185,15 +189,19 @@ where } #[derive(Debug, PartialEq, serde::Serialize)] -pub struct ContractInfo { +pub struct ContractInfo { trie_id: TrieId, - code_hash: CodeHash, + code_hash: Hash, storage_items: u32, storage_items_deposit: Balance, storage_total_deposit: Balance, } -impl ContractInfo { +impl ContractInfo +where + Hash: serde::Serialize, + Balance: serde::Serialize + Copy, +{ /// Convert and return contract info in JSON format. pub fn to_json(&self) -> Result { Ok(serde_json::to_string_pretty(self)?) @@ -205,7 +213,7 @@ impl ContractInfo { } /// Return the code_hash of the contract. - pub fn code_hash(&self) -> &CodeHash { + pub fn code_hash(&self) -> &Hash { &self.code_hash } @@ -255,11 +263,14 @@ impl Display for TrieId { } /// Fetch the contract wasm code from the storage using the provided client and code hash. -pub async fn fetch_wasm_code( - client: &Client, - rpc: &LegacyRpcMethods, - hash: &CodeHash, -) -> Result> { +pub async fn fetch_wasm_code( + client: &OnlineClient, + rpc: &LegacyRpcMethods, + hash: &C::Hash, +) -> Result> +where + C::Hash: AsRef<[u8]> + Display + IntoVisitor, +{ let best_block = get_best_block(rpc).await?; let pristine_code_address = @@ -292,9 +303,9 @@ fn parse_contract_account_address( } /// Fetch all contract addresses from the storage using the provided client. -pub async fn fetch_all_contracts( - client: &Client, - rpc: &LegacyRpcMethods, +pub async fn fetch_all_contracts( + client: &OnlineClient, + rpc: &LegacyRpcMethods, ) -> Result> { let best_block = get_best_block(rpc).await?; let root_key = subxt::dynamic::storage( @@ -322,14 +333,14 @@ pub async fn fetch_all_contracts( /// A struct used in the storage reads to access account info. #[derive(DecodeAsType, Debug)] #[decode_as_type(crate_path = "subxt::ext::scale_decode")] -struct AccountInfo { - data: AccountData, +struct AccountInfo { + data: AccountData, } /// A struct used in the storage reads to access account data. #[derive(Clone, Debug, DecodeAsType)] #[decode_as_type(crate_path = "subxt::ext::scale_decode")] -struct AccountData { +struct AccountData { free: Balance, reserved: Balance, } @@ -342,9 +353,9 @@ struct BoundedVec(pub ::std::vec::Vec); /// A struct used in the storage reads to access contract info. #[derive(Debug, DecodeAsType)] #[decode_as_type(crate_path = "subxt::ext::scale_decode")] -struct ContractInfoOf { +struct ContractInfoOf { trie_id: BoundedVec, - code_hash: CodeHash, + code_hash: Hash, storage_items: u32, storage_item_deposit: Balance, } @@ -364,9 +375,12 @@ mod tests { IntoPortable, Path, }; - use subxt::metadata::{ - types::Metadata, - DecodeWithMetadata, + use subxt::{ + metadata::{ + types::Metadata, + DecodeWithMetadata, + }, + PolkadotConfig as DefaultConfig, }; // Find the type index in the metadata. diff --git a/crates/extrinsics/src/contract_storage.rs b/crates/extrinsics/src/contract_storage.rs index ca9f4ee53..8873681ca 100644 --- a/crates/extrinsics/src/contract_storage.rs +++ b/crates/extrinsics/src/contract_storage.rs @@ -22,6 +22,7 @@ use contract_transcode::{ ContractMessageTranscoder, Value, }; +use ink_env::Environment; use ink_metadata::layout::{ Layout, StructLayout, @@ -50,6 +51,7 @@ use std::{ Display, Formatter, }, + marker::PhantomData, }; use subxt::{ backend::{ @@ -75,21 +77,26 @@ use super::{ fetch_contract_info, url_to_string, ContractInfo, - DefaultConfig, TrieId, }; -pub struct ContractStorage { - rpc: ContractStorageRpc, +pub struct ContractStorage { + rpc: ContractStorageRpc, + _phantom: PhantomData (C, E)>, } -impl ContractStorage +impl ContractStorage where C::AccountId: AsRef<[u8]> + Display + IntoVisitor, + C::Hash: IntoVisitor, DecodeError: From<<::Visitor as Visitor>::Error>, + E::Balance: IntoVisitor + Serialize, { - pub fn new(rpc: ContractStorageRpc) -> Self { - Self { rpc } + pub fn new(rpc: ContractStorageRpc) -> Self { + Self { + rpc, + _phantom: Default::default(), + } } /// Load the raw key/value storage for a given contract. @@ -587,16 +594,19 @@ impl ContractStorageLayout { } /// Methods for querying contracts over RPC. -pub struct ContractStorageRpc { +pub struct ContractStorageRpc { rpc_client: RpcClient, rpc_methods: LegacyRpcMethods, client: OnlineClient, + _phantom: PhantomData (C, E)>, } -impl ContractStorageRpc +impl ContractStorageRpc where C::AccountId: AsRef<[u8]> + Display + IntoVisitor, + C::Hash: IntoVisitor, DecodeError: From<<::Visitor as Visitor>::Error>, + E::Balance: IntoVisitor, { /// Create a new instance of the ContractsRpc. pub async fn new(url: &url::Url) -> Result { @@ -608,6 +618,7 @@ where rpc_client, rpc_methods, client, + _phantom: Default::default(), }) } @@ -615,8 +626,8 @@ where pub async fn fetch_contract_info( &self, contract: &C::AccountId, - ) -> Result { - fetch_contract_info(contract, &self.rpc_methods, &self.client).await + ) -> Result> { + fetch_contract_info::(contract, &self.rpc_methods, &self.client).await } /// Fetch the contract storage at the given key. From 2a16a0c9adf283792d55f25e2585dbcc7457b74d Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Mon, 22 Jan 2024 14:51:33 +0100 Subject: [PATCH 02/39] Fix unit tests --- crates/extrinsics/src/contract_info.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/extrinsics/src/contract_info.rs b/crates/extrinsics/src/contract_info.rs index f5c66228c..ad0af6ba6 100644 --- a/crates/extrinsics/src/contract_info.rs +++ b/crates/extrinsics/src/contract_info.rs @@ -370,6 +370,7 @@ struct DepositAccount { #[cfg(test)] mod tests { use super::*; + use ink_env::DefaultEnvironment; use scale::Encode; use scale_info::{ IntoPortable, @@ -452,8 +453,11 @@ mod tests { let contract = AccountId32([0u8; 32]); let contract_info_raw = - ContractInfoRaw::::new(contract, contract_info_thunk) - .expect("the conatract info raw must be created"); + ContractInfoRaw::::new( + contract, + contract_info_thunk, + ) + .expect("the conatract info raw must be created"); let account_data = AccountData { free: 1, reserved: 10, @@ -518,8 +522,11 @@ mod tests { let contract = AccountId32([0u8; 32]); let contract_info_raw = - ContractInfoRaw::::new(contract, contract_info_thunk) - .expect("the conatract info raw must be created"); + ContractInfoRaw::::new( + contract, + contract_info_thunk, + ) + .expect("the conatract info raw must be created"); let account_data = AccountData { free: 1, reserved: 10, From e48a522f8e40768ba27810df31f35d4d2fc69474 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Mon, 22 Jan 2024 16:05:25 +0100 Subject: [PATCH 03/39] Add Account as generic --- crates/extrinsics/src/contract_info.rs | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/extrinsics/src/contract_info.rs b/crates/extrinsics/src/contract_info.rs index ad0af6ba6..84f325b96 100644 --- a/crates/extrinsics/src/contract_info.rs +++ b/crates/extrinsics/src/contract_info.rs @@ -41,7 +41,6 @@ use subxt::{ scale_value::Value, }, storage::dynamic, - utils::AccountId32, Config, OnlineClient, }; @@ -289,16 +288,19 @@ where /// Parse a contract account address from a storage key. Returns error if a key is /// malformated. -fn parse_contract_account_address( +fn parse_contract_account_address( storage_contract_account_key: &[u8], storage_contract_root_key_len: usize, -) -> Result { +) -> Result +where + C::AccountId: Decode, +{ // storage_contract_account_key is a concatenation of contract_info_of root key and // Twox64Concat(AccountId) let mut account = storage_contract_account_key .get(storage_contract_root_key_len + 8..) .ok_or(anyhow!("Unexpected storage key size"))?; - AccountId32::decode(&mut account) + Decode::decode(&mut account) .map_err(|err| anyhow!("AccountId deserialization error: {}", err)) } @@ -306,14 +308,14 @@ fn parse_contract_account_address( pub async fn fetch_all_contracts( client: &OnlineClient, rpc: &LegacyRpcMethods, -) -> Result> { +) -> Result> +where + C::AccountId: Decode, +{ let best_block = get_best_block(rpc).await?; - let root_key = subxt::dynamic::storage( - "Contracts", - "ContractInfoOf", - vec![Value::from_bytes(AccountId32([0u8; 32]))], - ) - .to_root_bytes(); + let root_key = + subxt::dynamic::storage("Contracts", "ContractInfoOf", Vec::<()>::new()) + .to_root_bytes(); let mut keys = client .storage() .at(best_block) @@ -323,7 +325,7 @@ pub async fn fetch_all_contracts( let mut contract_accounts = Vec::new(); while let Some(result) = keys.next().await { let key = result?; - let contract_account = parse_contract_account_address(&key, root_key.len())?; + let contract_account = parse_contract_account_address::(&key, root_key.len())?; contract_accounts.push(contract_account); } @@ -381,6 +383,7 @@ mod tests { types::Metadata, DecodeWithMetadata, }, + utils::AccountId32, PolkadotConfig as DefaultConfig, }; From 4bc30daa5a6fc18e6027724ae1fa9e5dd5fe8473 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 23 Jan 2024 16:24:46 +0100 Subject: [PATCH 04/39] Adde generics to upload and remove commands --- crates/cargo-contract/src/cmd/remove.rs | 18 +++--- crates/extrinsics/src/balance.rs | 12 ++-- crates/extrinsics/src/call.rs | 2 +- crates/extrinsics/src/events.rs | 67 +++++++++++++--------- crates/extrinsics/src/instantiate.rs | 6 +- crates/extrinsics/src/lib.rs | 7 ++- crates/extrinsics/src/remove.rs | 74 +++++++++++++++---------- crates/extrinsics/src/upload.rs | 36 +++++++----- crates/transcode/src/lib.rs | 19 +++++-- 9 files changed, 150 insertions(+), 91 deletions(-) diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index 6acdd373c..5896ed168 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -25,7 +25,10 @@ use contract_extrinsics::{ DefaultConfig, ExtrinsicOptsBuilder, RemoveCommandBuilder, + RemoveExec, + RemoveResult, }; +use ink_env::DefaultEnvironment; use subxt::Config; #[derive(Debug, clap::Args)] @@ -55,12 +58,13 @@ impl RemoveCommand { .suri(self.extrinsic_cli_opts.suri.clone()) .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) .done(); - let remove_exec = RemoveCommandBuilder::default() - .code_hash(self.code_hash) - .extrinsic_opts(extrinsic_opts) - .done() - .await?; - let remove_result = remove_exec.remove_code().await?; + let remove_exec: RemoveExec = + RemoveCommandBuilder::default() + .code_hash(self.code_hash) + .extrinsic_opts(extrinsic_opts) + .done() + .await?; + let remove_result: RemoveResult<_, _> = remove_exec.remove_code().await?; let display_events = remove_result.display_events; let output_events = if self.output_json() { display_events.to_json()? @@ -71,7 +75,7 @@ impl RemoveCommand { )? }; if let Some(code_removed) = remove_result.code_removed { - let remove_result = code_removed.code_hash; + let remove_result: ::Hash = code_removed.code_hash; if self.output_json() { // Create a JSON object with the events and the removed code hash. diff --git a/crates/extrinsics/src/balance.rs b/crates/extrinsics/src/balance.rs index 025238378..4965f7a96 100644 --- a/crates/extrinsics/src/balance.rs +++ b/crates/extrinsics/src/balance.rs @@ -25,13 +25,13 @@ use rust_decimal::{ Decimal, }; use serde_json::json; -use subxt::backend::legacy::LegacyRpcMethods; - -use super::{ - Balance, - DefaultConfig, +use subxt::{ + backend::legacy::LegacyRpcMethods, + Config, }; +use super::Balance; + use anyhow::{ anyhow, Context, @@ -75,7 +75,7 @@ pub enum UnitPrefix { impl TokenMetadata { /// Query [TokenMetadata] through the node's RPC - pub async fn query(client: &LegacyRpcMethods) -> Result { + pub async fn query(client: &LegacyRpcMethods) -> Result { let sys_props = client.system_properties().await?; let default_decimals = json!(12); diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index 51381a954..9db0b18c1 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -303,7 +303,7 @@ impl CallExec { let result = submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; - let display_events = DisplayEvents::from_events( + let display_events = DisplayEvents::from_events::( &result, Some(&self.transcoder), &self.client.metadata(), diff --git a/crates/extrinsics/src/events.rs b/crates/extrinsics/src/events.rs index 7f3bafc5a..f6371ab00 100644 --- a/crates/extrinsics/src/events.rs +++ b/crates/extrinsics/src/events.rs @@ -15,10 +15,7 @@ // along with cargo-contract. If not, see . use super::{ - Balance, BalanceVariant, - CodeHash, - DefaultConfig, TokenMetadata, }; use crate::DEFAULT_KEY_COL_WIDTH; @@ -42,7 +39,10 @@ use subxt::{ blocks::ExtrinsicEvents, events::StaticEvent, ext::{ - scale_decode, + scale_decode::{ + self, + IntoVisitor, + }, scale_encode, }, Config, @@ -58,12 +58,15 @@ use subxt::{ )] #[decode_as_type(crate_path = "subxt::ext::scale_decode")] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub struct ContractEmitted { - pub contract: ::AccountId, +pub struct ContractEmitted { + pub contract: AccountId, pub data: Vec, } -impl StaticEvent for ContractEmitted { +impl StaticEvent for ContractEmitted +where + AccountId: IntoVisitor, +{ const PALLET: &'static str = "Contracts"; const EVENT: &'static str = "ContractEmitted"; } @@ -78,14 +81,17 @@ impl StaticEvent for ContractEmitted { )] #[decode_as_type(crate_path = "subxt::ext::scale_decode")] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub struct ContractInstantiated { +pub struct ContractInstantiated { /// Account id of the deployer. - pub deployer: ::AccountId, + pub deployer: AccountId, /// Account id where the contract was instantiated to. - pub contract: ::AccountId, + pub contract: AccountId, } -impl StaticEvent for ContractInstantiated { +impl StaticEvent for ContractInstantiated +where + AccountId: IntoVisitor, +{ const PALLET: &'static str = "Contracts"; const EVENT: &'static str = "Instantiated"; } @@ -100,12 +106,15 @@ impl StaticEvent for ContractInstantiated { )] #[decode_as_type(crate_path = "subxt::ext::scale_decode")] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub struct CodeStored { +pub struct CodeStored { /// Hash under which the contract code was stored. - pub code_hash: CodeHash, + pub code_hash: Hash, } -impl StaticEvent for CodeStored { +impl StaticEvent for CodeStored +where + Hash: IntoVisitor, +{ const PALLET: &'static str = "Contracts"; const EVENT: &'static str = "CodeStored"; } @@ -120,13 +129,18 @@ impl StaticEvent for CodeStored { )] #[decode_as_type(crate_path = "subxt::ext::scale_decode")] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub struct CodeRemoved { - pub code_hash: CodeHash, +pub struct CodeRemoved { + pub code_hash: Hash, pub deposit_released: Balance, - pub remover: ::AccountId, + pub remover: AccountId, } -impl StaticEvent for CodeRemoved { +impl StaticEvent for CodeRemoved +where + Hash: IntoVisitor, + Balance: IntoVisitor, + AccountId: IntoVisitor, +{ const PALLET: &'static str = "Contracts"; const EVENT: &'static str = "CodeRemoved"; } @@ -170,11 +184,14 @@ pub struct DisplayEvents(Vec); impl DisplayEvents { /// Parses events and returns an object which can be serialised - pub fn from_events( - result: &ExtrinsicEvents, + pub fn from_events( + result: &ExtrinsicEvents, transcoder: Option<&ContractMessageTranscoder>, subxt_metadata: &subxt::Metadata, - ) -> Result { + ) -> Result + where + C::AccountId: IntoVisitor, + { let mut events: Vec = vec![]; let events_transcoder = TranscoderBuilder::new(subxt_metadata.types()) @@ -202,13 +219,13 @@ impl DisplayEvents { let event_sig_topic = event.topics().iter().next(); let mut unnamed_field_name = 0; for field_metadata in event_fields { - if ::is_event( + if as StaticEvent>::is_event( event.pallet_name(), event.variant_name(), ) && field_metadata.name == Some("data".to_string()) { tracing::debug!("event data: {:?}", hex::encode(&event_data)); - let field = contract_event_data_field( + let field = contract_event_data_field::( transcoder, field_metadata, event_sig_topic, @@ -300,10 +317,10 @@ impl DisplayEvents { /// Construct the contract event data field, attempting to decode the event using the /// [`ContractMessageTranscoder`] if available. -fn contract_event_data_field( +fn contract_event_data_field( transcoder: Option<&ContractMessageTranscoder>, field_metadata: &scale_info::Field, - event_sig_topic: Option<&sp_core::H256>, + event_sig_topic: Option<&C::Hash>, event_data: &mut &[u8], ) -> Result { let event_value = if let Some(transcoder) = transcoder { diff --git a/crates/extrinsics/src/instantiate.rs b/crates/extrinsics/src/instantiate.rs index 424e965bf..ca00becd5 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -383,11 +383,11 @@ impl InstantiateExec { // The CodeStored event is only raised if the contract has not already been // uploaded. let code_hash = result - .find_first::()? + .find_first::::Hash>>()? .map(|code_stored| code_stored.code_hash); let instantiated = result - .find_last::()? + .find_last::::AccountId>>()? .ok_or_else(|| anyhow!("Failed to find Instantiated event"))?; Ok(InstantiateExecResult { @@ -417,7 +417,7 @@ impl InstantiateExec { submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; let instantiated = result - .find_first::()? + .find_first::::AccountId>>()? .ok_or_else(|| anyhow!("Failed to find Instantiated event"))?; Ok(InstantiateExecResult { diff --git a/crates/extrinsics/src/lib.rs b/crates/extrinsics/src/lib.rs index b36f079b5..9891741ba 100644 --- a/crates/extrinsics/src/lib.rs +++ b/crates/extrinsics/src/lib.rs @@ -102,6 +102,7 @@ pub use instantiate::{ pub use remove::{ RemoveCommandBuilder, RemoveExec, + RemoveResult, }; pub use subxt::PolkadotConfig as DefaultConfig; @@ -153,7 +154,9 @@ where T: Config, Call: tx::TxPayload, Signer: tx::Signer, + T::Signature: From, >::OtherParams: Default, + T::AccountId: From, { let account_id = Signer::account_id(signer); let account_nonce = get_account_nonce(client, rpc, &account_id).await?; @@ -220,8 +223,8 @@ where Ok(account_nonce) } -async fn state_call( - rpc: &LegacyRpcMethods, +async fn state_call( + rpc: &LegacyRpcMethods, func: &str, args: A, ) -> Result { diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index 60ca7187f..87a3d1856 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -21,10 +21,7 @@ use super::{ }, state, submit_extrinsic, - Client, - CodeHash, ContractMessageTranscoder, - DefaultConfig, ErrorVariant, Missing, TokenMetadata, @@ -36,30 +33,35 @@ use crate::{ use anyhow::Result; use core::marker::PhantomData; +use ink_env::Environment; use subxt::{ backend::{ legacy::LegacyRpcMethods, rpc::RpcClient, }, + config, + ext::scale_decode::IntoVisitor, Config, OnlineClient, }; use subxt_signer::sr25519::Keypair; -pub struct RemoveOpts { - code_hash: Option, +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 { + opts: RemoveOpts, + marker: PhantomData (E, ExtrinsicOptions)>, } -impl RemoveCommandBuilder> { +impl + RemoveCommandBuilder> +{ /// Returns a clean builder for [`RemoveExec`]. - pub fn new() -> RemoveCommandBuilder> { + pub fn new() -> RemoveCommandBuilder> { RemoveCommandBuilder { opts: RemoveOpts { code_hash: None, @@ -73,7 +75,7 @@ impl RemoveCommandBuilder> { pub fn extrinsic_opts( self, extrinsic_opts: ExtrinsicOpts, - ) -> RemoveCommandBuilder { + ) -> RemoveCommandBuilder { RemoveCommandBuilder { opts: RemoveOpts { extrinsic_opts, @@ -84,22 +86,27 @@ impl RemoveCommandBuilder> { } } -impl Default for RemoveCommandBuilder> { +impl Default + for RemoveCommandBuilder> +{ fn default() -> Self { Self::new() } } -impl RemoveCommandBuilder { +impl RemoveCommandBuilder { /// Sets the hash of the smart contract code already uploaded to the chain. - pub fn code_hash(self, code_hash: Option<::Hash>) -> Self { + pub fn code_hash(self, code_hash: Option) -> Self { let mut this = self; this.opts.code_hash = code_hash; this } } -impl RemoveCommandBuilder { +impl RemoveCommandBuilder +where + [u8; 32]: From, +{ /// Preprocesses contract artifacts and options for subsequent removal of contract /// code. /// @@ -110,7 +117,7 @@ impl RemoveCommandBuilder { /// /// 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 { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; let signer = self.opts.extrinsic_opts.signer()?; @@ -118,7 +125,7 @@ impl RemoveCommandBuilder { let artifacts_path = artifacts.artifact_path().to_path_buf(); let final_code_hash = match (self.opts.code_hash.as_ref(), artifacts.code.as_ref()) { - (Some(code_h), _) => Ok(code_h.0), + (Some(code_h), _) => Ok((*code_h).into()), (None, Some(_)) => artifacts.code_hash(), (None, None) => Err(anyhow::anyhow!( "No code_hash was provided or contract code was not found from artifact \ @@ -130,8 +137,8 @@ impl RemoveCommandBuilder { let url = self.opts.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 client = OnlineClient::::from_rpc_client(rpc_cli.clone()).await?; + let rpc = LegacyRpcMethods::::new(rpc_cli); let token_metadata = TokenMetadata::query(&rpc).await?; @@ -143,21 +150,31 @@ impl RemoveCommandBuilder { transcoder, signer, token_metadata, + _marker: Default::default(), }) } } -pub struct RemoveExec { +pub struct RemoveExec { final_code_hash: [u8; 32], opts: ExtrinsicOpts, - rpc: LegacyRpcMethods, - client: Client, + rpc: LegacyRpcMethods, + client: OnlineClient, transcoder: ContractMessageTranscoder, signer: Keypair, token_metadata: TokenMetadata, + _marker: PhantomData E>, } -impl RemoveExec { +impl RemoveExec +where + C::Hash: IntoVisitor, + E::Balance: IntoVisitor, + C::AccountId: IntoVisitor + From, + C::Address: From, + C::Signature: From, + >::OtherParams: Default, +{ /// Removes a contract code from the blockchain. /// /// This function removes a contract code with the specified code hash from the @@ -167,7 +184,7 @@ impl RemoveExec { /// /// 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 { + pub async fn remove_code(&self) -> Result, ErrorVariant> { let code_hash = sp_core::H256(self.final_code_hash); let call = RemoveCode::new(code_hash).build(); @@ -180,7 +197,8 @@ impl RemoveExec { &self.client.metadata(), )?; - let code_removed = result.find_first::()?; + let code_removed = + result.find_first::>()?; Ok(RemoveResult { code_removed, display_events, @@ -198,7 +216,7 @@ impl RemoveExec { } /// Returns the client. - pub fn client(&self) -> &Client { + pub fn client(&self) -> &OnlineClient { &self.client } @@ -218,7 +236,7 @@ impl RemoveExec { } } -pub struct RemoveResult { - pub code_removed: Option, +pub struct RemoveResult { + pub code_removed: Option>, pub display_events: DisplayEvents, } diff --git a/crates/extrinsics/src/upload.rs b/crates/extrinsics/src/upload.rs index 6f94e08bb..dff49c02e 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -24,7 +24,6 @@ use super::{ state_call, submit_extrinsic, Balance, - Client, CodeHash, DefaultConfig, ErrorVariant, @@ -47,7 +46,11 @@ use subxt::{ legacy::LegacyRpcMethods, rpc::RpcClient, }, - ext::scale_encode::EncodeAsType, + config, + ext::{ + scale_decode::IntoVisitor, + scale_encode::EncodeAsType, + }, Config, OnlineClient, }; @@ -102,7 +105,7 @@ impl UploadCommandBuilder { /// /// Returns the `UploadExec` containing the preprocessed data for the upload or /// execution. - pub async fn done(self) -> Result { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; let signer = self.opts.extrinsic_opts.signer()?; @@ -135,17 +138,24 @@ impl UploadCommandBuilder { } } -pub struct UploadExec { +pub struct UploadExec { opts: ExtrinsicOpts, - rpc: LegacyRpcMethods, - client: Client, + 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, + >::OtherParams: Default, +{ /// Uploads contract code to a specified URL using a JSON-RPC call. /// /// This function performs a JSON-RPC call to upload contract code to the given URL. @@ -171,7 +181,7 @@ impl UploadExec { /// 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 { + pub async fn upload_code(&self) -> Result, ErrorVariant> { let storage_deposit_limit = self .opts .storage_deposit_limit_balance(&self.token_metadata)?; @@ -188,8 +198,8 @@ impl UploadExec { let display_events = DisplayEvents::from_events(&result, None, &self.client.metadata())?; - let code_stored = result.find_first::()?; - Ok(UploadResult { + let code_stored = result.find_first::>()?; + Ok(UploadResult:: { code_stored, display_events, }) @@ -201,7 +211,7 @@ impl UploadExec { } /// Returns the client. - pub fn client(&self) -> &Client { + pub fn client(&self) -> &OnlineClient { &self.client } @@ -235,8 +245,8 @@ struct CodeUploadRequest { determinism: Determinism, } -pub struct UploadResult { - pub code_stored: Option, +pub struct UploadResult { + pub code_stored: Option>, pub display_events: DisplayEvents, } diff --git a/crates/transcode/src/lib.rs b/crates/transcode/src/lib.rs index cec4b2a9d..38aa7c3fc 100644 --- a/crates/transcode/src/lib.rs +++ b/crates/transcode/src/lib.rs @@ -290,11 +290,14 @@ impl ContractMessageTranscoder { .find(|msg| msg.label() == &name.to_string()) } - pub fn decode_contract_event( + pub fn decode_contract_event( &self, - event_sig_topic: &primitive_types::H256, + event_sig_topic: &Hash, data: &mut &[u8], - ) -> Result { + ) -> Result + where + Hash: AsRef<[u8]>, + { // data is an encoded `Vec` so is prepended with its length `Compact`, // which we ignore because the structure of the event data is known for // decoding. @@ -306,7 +309,7 @@ impl ContractMessageTranscoder { .iter() .find(|event| { if let Some(sig_topic) = event.signature_topic() { - sig_topic.as_bytes() == event_sig_topic.as_bytes() + sig_topic.as_bytes() == event_sig_topic.as_ref() } else { false } @@ -498,6 +501,10 @@ impl CompositeTypeFields { #[cfg(test)] mod tests { use super::*; + use ink_env::{ + DefaultEnvironment, + Environment, + }; use primitive_types::H256; use scale::Encode; use scon::Value; @@ -776,7 +783,7 @@ mod tests { let metadata = generate_metadata(); let transcoder = ContractMessageTranscoder::new(metadata); - let signature_topic: H256 = + let signature_topic: ::Hash = ::SIGNATURE_TOPIC .unwrap() .into(); @@ -799,7 +806,7 @@ mod tests { 52u8, 40, 235, 225, 70, 245, 184, 36, 21, 218, 130, 114, 75, 207, 117, 240, 83, 118, 135, 56, 220, 172, 95, 131, 171, 125, 130, 167, 10, 15, 242, 222, ]; - let signature_topic: H256 = + let signature_topic: ::Hash = ::SIGNATURE_TOPIC .unwrap() .into(); From 085a2ff782f3ff3d87b072b20f45b5ad0b68bf3d Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 23 Jan 2024 17:25:37 +0100 Subject: [PATCH 05/39] Fix tests --- crates/cargo-contract/src/cmd/upload.rs | 13 ++++++++++--- crates/extrinsics/src/extrinsic_calls.rs | 8 ++++---- crates/extrinsics/src/remove.rs | 18 +++++++++++------- crates/extrinsics/src/upload.rs | 24 +++++++++++------------- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index d6f075597..afcee2fe6 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -27,6 +27,12 @@ use contract_extrinsics::{ Balance, ExtrinsicOptsBuilder, UploadCommandBuilder, + UploadExec, + UploadResult, +}; +use subxt::{ + Config, + PolkadotConfig as DefaultConfig, }; #[derive(Debug, clap::Args)] @@ -53,7 +59,7 @@ impl UploadCommand { .suri(self.extrinsic_cli_opts.suri.clone()) .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) .done(); - let upload_exec = UploadCommandBuilder::default() + let upload_exec: UploadExec = UploadCommandBuilder::default() .extrinsic_opts(extrinsic_opts) .done() .await?; @@ -86,7 +92,8 @@ impl UploadCommand { } } } else { - let upload_result = upload_exec.upload_code().await?; + let upload_result: UploadResult = + upload_exec.upload_code().await?; let display_events = upload_result.display_events; let output_events = if self.output_json() { display_events.to_json()? @@ -97,7 +104,7 @@ impl UploadCommand { )? }; if let Some(code_stored) = upload_result.code_stored { - let code_hash = code_stored.code_hash; + let code_hash: ::Hash = code_stored.code_hash; if self.output_json() { // Create a JSON object with the events and the code hash. let json_object = serde_json::json!({ diff --git a/crates/extrinsics/src/extrinsic_calls.rs b/crates/extrinsics/src/extrinsic_calls.rs index a3950b61a..9379fbabf 100644 --- a/crates/extrinsics/src/extrinsic_calls.rs +++ b/crates/extrinsics/src/extrinsic_calls.rs @@ -66,12 +66,12 @@ impl core::fmt::Display for Weight { /// A raw call to `pallet-contracts`'s `remove_code`. #[derive(EncodeAsType)] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub(crate) struct RemoveCode { - code_hash: CodeHash, +pub(crate) struct RemoveCode { + code_hash: Hash, } -impl RemoveCode { - pub fn new(code_hash: CodeHash) -> Self { +impl RemoveCode { + pub fn new(code_hash: Hash) -> Self { Self { code_hash } } diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index 87a3d1856..28d310b56 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -40,7 +40,10 @@ use subxt::{ rpc::RpcClient, }, config, - ext::scale_decode::IntoVisitor, + ext::{ + scale_decode::IntoVisitor, + scale_encode::EncodeAsType, + }, Config, OnlineClient, }; @@ -105,7 +108,7 @@ impl RemoveCommandBuilder { impl RemoveCommandBuilder where - [u8; 32]: From, + C::Hash: From<[u8; 32]>, { /// Preprocesses contract artifacts and options for subsequent removal of contract /// code. @@ -125,8 +128,8 @@ where let artifacts_path = artifacts.artifact_path().to_path_buf(); let final_code_hash = match (self.opts.code_hash.as_ref(), artifacts.code.as_ref()) { - (Some(code_h), _) => Ok((*code_h).into()), - (None, Some(_)) => artifacts.code_hash(), + (Some(code_h), _) => Ok(*code_h), + (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 \ @@ -156,7 +159,7 @@ where } pub struct RemoveExec { - final_code_hash: [u8; 32], + final_code_hash: C::Hash, opts: ExtrinsicOpts, rpc: LegacyRpcMethods, client: OnlineClient, @@ -174,6 +177,7 @@ where C::Address: From, C::Signature: From, >::OtherParams: Default, + C::Hash: EncodeAsType, { /// Removes a contract code from the blockchain. /// @@ -185,7 +189,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> { - let code_hash = sp_core::H256(self.final_code_hash); + let code_hash = self.final_code_hash; let call = RemoveCode::new(code_hash).build(); @@ -206,7 +210,7 @@ where } /// Returns the final code hash. - pub fn final_code_hash(&self) -> [u8; 32] { + pub fn final_code_hash(&self) -> C::Hash { self.final_code_hash } diff --git a/crates/extrinsics/src/upload.rs b/crates/extrinsics/src/upload.rs index dff49c02e..5696ab9ed 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -24,8 +24,6 @@ use super::{ state_call, submit_extrinsic, Balance, - CodeHash, - DefaultConfig, ErrorVariant, Missing, TokenMetadata, @@ -61,14 +59,14 @@ struct UploadOpts { } /// A builder for the upload command. -pub struct UploadCommandBuilder { +pub struct UploadCommandBuilder { opts: UploadOpts, - marker: PhantomData ExtrinsicOptions>, + marker: PhantomData (C, ExtrinsicOptions)>, } -impl UploadCommandBuilder> { +impl UploadCommandBuilder> { /// Returns a clean builder for [`UploadExec`]. - pub fn new() -> UploadCommandBuilder> { + pub fn new() -> UploadCommandBuilder> { UploadCommandBuilder { opts: UploadOpts { extrinsic_opts: ExtrinsicOpts::default(), @@ -81,7 +79,7 @@ impl UploadCommandBuilder> { pub fn extrinsic_opts( self, extrinsic_opts: ExtrinsicOpts, - ) -> UploadCommandBuilder { + ) -> UploadCommandBuilder { UploadCommandBuilder { opts: UploadOpts { extrinsic_opts }, marker: PhantomData, @@ -89,13 +87,13 @@ impl UploadCommandBuilder> { } } -impl Default for UploadCommandBuilder> { +impl Default for UploadCommandBuilder> { fn default() -> Self { Self::new() } } -impl UploadCommandBuilder { +impl UploadCommandBuilder { /// Preprocesses contract artifacts and options for subsequent upload. /// /// This function prepares the necessary data for uploading a contract @@ -105,7 +103,7 @@ impl UploadCommandBuilder { /// /// Returns the `UploadExec` containing the preprocessed data for the upload or /// execution. - pub async fn done(self) -> Result> { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; let signer = self.opts.extrinsic_opts.signer()?; @@ -162,7 +160,7 @@ where /// It constructs a [`CodeUploadRequest`] with the code and relevant parameters, /// 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> { + pub async fn upload_code_rpc(&self) -> Result> { let storage_deposit_limit = self .opts .storage_deposit_limit_balance(&self.token_metadata)?; @@ -238,8 +236,8 @@ where /// A struct that encodes RPC parameters required for a call to upload a new code. #[derive(Encode)] -struct CodeUploadRequest { - origin: ::AccountId, +struct CodeUploadRequest { + origin: AccountId, code: Vec, storage_deposit_limit: Option, determinism: Determinism, From 252f133179356a76a6e0aa5417ee653888759723 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 23 Jan 2024 23:02:00 +0100 Subject: [PATCH 06/39] PhantomData cleanup --- crates/cargo-contract/src/cmd/storage.rs | 3 +-- crates/extrinsics/src/contract_storage.rs | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/crates/cargo-contract/src/cmd/storage.rs b/crates/cargo-contract/src/cmd/storage.rs index ce6834b73..9fd76dff6 100644 --- a/crates/cargo-contract/src/cmd/storage.rs +++ b/crates/cargo-contract/src/cmd/storage.rs @@ -66,8 +66,7 @@ pub struct StorageCommand { impl StorageCommand { pub async fn run(&self) -> Result<(), ErrorVariant> { - let rpc = ContractStorageRpc::::new(&self.url) - .await?; + let rpc = ContractStorageRpc::::new(&self.url).await?; let storage_layout = ContractStorage::::new(rpc); diff --git a/crates/extrinsics/src/contract_storage.rs b/crates/extrinsics/src/contract_storage.rs index 8873681ca..1c31a2c97 100644 --- a/crates/extrinsics/src/contract_storage.rs +++ b/crates/extrinsics/src/contract_storage.rs @@ -81,8 +81,8 @@ use super::{ }; pub struct ContractStorage { - rpc: ContractStorageRpc, - _phantom: PhantomData (C, E)>, + rpc: ContractStorageRpc, + _phantom: PhantomData E>, } impl ContractStorage @@ -92,7 +92,7 @@ where DecodeError: From<<::Visitor as Visitor>::Error>, E::Balance: IntoVisitor + Serialize, { - pub fn new(rpc: ContractStorageRpc) -> Self { + pub fn new(rpc: ContractStorageRpc) -> Self { Self { rpc, _phantom: Default::default(), @@ -104,7 +104,7 @@ where &self, contract_account: &C::AccountId, ) -> Result { - let contract_info = self.rpc.fetch_contract_info(contract_account).await?; + let contract_info = self.rpc.fetch_contract_info::(contract_account).await?; let trie_id = contract_info.trie_id(); let mut storage_keys = Vec::new(); @@ -594,19 +594,17 @@ impl ContractStorageLayout { } /// Methods for querying contracts over RPC. -pub struct ContractStorageRpc { +pub struct ContractStorageRpc { rpc_client: RpcClient, rpc_methods: LegacyRpcMethods, client: OnlineClient, - _phantom: PhantomData (C, E)>, } -impl ContractStorageRpc +impl ContractStorageRpc where C::AccountId: AsRef<[u8]> + Display + IntoVisitor, C::Hash: IntoVisitor, DecodeError: From<<::Visitor as Visitor>::Error>, - E::Balance: IntoVisitor, { /// Create a new instance of the ContractsRpc. pub async fn new(url: &url::Url) -> Result { @@ -618,15 +616,17 @@ where rpc_client, rpc_methods, client, - _phantom: Default::default(), }) } /// Fetch the contract info to access the trie id for querying storage. - pub async fn fetch_contract_info( + pub async fn fetch_contract_info( &self, contract: &C::AccountId, - ) -> Result> { + ) -> Result> + where + E::Balance: IntoVisitor, + { fetch_contract_info::(contract, &self.rpc_methods, &self.client).await } From 715b555806f2faa3cb8f644d93bbbc17bc7d0171 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Wed, 24 Jan 2024 10:55:39 +0100 Subject: [PATCH 07/39] Fix tests --- crates/cargo-contract/src/cmd/remove.rs | 14 ++++---- crates/extrinsics/src/integration_tests.rs | 11 +++--- crates/extrinsics/src/remove.rs | 39 ++++++++++------------ crates/extrinsics/src/upload.rs | 16 ++++----- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index 5896ed168..789fcd366 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -58,13 +58,13 @@ impl RemoveCommand { .suri(self.extrinsic_cli_opts.suri.clone()) .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) .done(); - let remove_exec: RemoveExec = - RemoveCommandBuilder::default() - .code_hash(self.code_hash) - .extrinsic_opts(extrinsic_opts) - .done() - .await?; - let remove_result: RemoveResult<_, _> = remove_exec.remove_code().await?; + let remove_exec: RemoveExec = RemoveCommandBuilder::default() + .code_hash(self.code_hash) + .extrinsic_opts(extrinsic_opts) + .done() + .await?; + let remove_result: RemoveResult<_, DefaultEnvironment> = + remove_exec.remove_code().await?; let display_events = remove_result.display_events; let output_events = if self.output_json() { display_events.to_json()? diff --git a/crates/extrinsics/src/integration_tests.rs b/crates/extrinsics/src/integration_tests.rs index 5700e3433..0a3ea3178 100644 --- a/crates/extrinsics/src/integration_tests.rs +++ b/crates/extrinsics/src/integration_tests.rs @@ -19,10 +19,13 @@ use crate::{ ExtrinsicOptsBuilder, InstantiateCommandBuilder, RemoveCommandBuilder, + RemoveExec, UploadCommandBuilder, + UploadExec, }; use anyhow::Result; use contract_build::code_hash; +use ink_env::DefaultEnvironment; use predicates::prelude::*; use std::{ ffi::OsStr, @@ -466,7 +469,7 @@ async fn api_build_upload_instantiate_call() { .file(Some(contract_file)) .suri("//Alice") .done(); - let upload = UploadCommandBuilder::default() + let upload: UploadExec = UploadCommandBuilder::default() .extrinsic_opts(opts.clone()) .done() .await @@ -592,7 +595,7 @@ async fn api_build_upload_remove() { .file(Some(contract_file)) .suri("//Alice") .done(); - let upload = UploadCommandBuilder::default() + let upload: UploadExec = UploadCommandBuilder::default() .extrinsic_opts(opts.clone()) .done() .await @@ -605,13 +608,13 @@ async fn api_build_upload_remove() { assert_eq!(64, code_hash.len(), "{code_hash:?}"); // remove the contract - let remove = RemoveCommandBuilder::default() + let remove: RemoveExec = RemoveCommandBuilder::default() .extrinsic_opts(opts.clone()) .code_hash(Some(code_hash_h256)) .done() .await .unwrap(); - let remove_result = remove.remove_code().await; + let remove_result = remove.remove_code::().await; assert!(remove_result.is_ok(), "remove code failed"); remove_result.unwrap(); diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index 28d310b56..5dfcbd193 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -55,16 +55,14 @@ pub struct RemoveOpts { } /// A builder for the remove command. -pub struct RemoveCommandBuilder { +pub struct RemoveCommandBuilder { opts: RemoveOpts, - marker: PhantomData (E, ExtrinsicOptions)>, + marker: PhantomData ExtrinsicOptions>, } -impl - RemoveCommandBuilder> -{ +impl RemoveCommandBuilder> { /// Returns a clean builder for [`RemoveExec`]. - pub fn new() -> RemoveCommandBuilder> { + pub fn new() -> RemoveCommandBuilder> { RemoveCommandBuilder { opts: RemoveOpts { code_hash: None, @@ -78,7 +76,7 @@ impl pub fn extrinsic_opts( self, extrinsic_opts: ExtrinsicOpts, - ) -> RemoveCommandBuilder { + ) -> RemoveCommandBuilder { RemoveCommandBuilder { opts: RemoveOpts { extrinsic_opts, @@ -89,15 +87,13 @@ impl } } -impl Default - for RemoveCommandBuilder> -{ +impl Default for RemoveCommandBuilder> { fn default() -> Self { Self::new() } } -impl RemoveCommandBuilder { +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; @@ -106,7 +102,7 @@ impl RemoveCommandBuilder { } } -impl RemoveCommandBuilder +impl RemoveCommandBuilder where C::Hash: From<[u8; 32]>, { @@ -120,7 +116,7 @@ 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> { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; let signer = self.opts.extrinsic_opts.signer()?; @@ -153,12 +149,11 @@ where transcoder, signer, token_metadata, - _marker: Default::default(), }) } } -pub struct RemoveExec { +pub struct RemoveExec { final_code_hash: C::Hash, opts: ExtrinsicOpts, rpc: LegacyRpcMethods, @@ -166,18 +161,15 @@ pub struct RemoveExec { transcoder: ContractMessageTranscoder, signer: Keypair, token_metadata: TokenMetadata, - _marker: PhantomData E>, } -impl RemoveExec +impl RemoveExec where - C::Hash: IntoVisitor, - E::Balance: IntoVisitor, + C::Hash: IntoVisitor + EncodeAsType, C::AccountId: IntoVisitor + From, C::Address: From, C::Signature: From, >::OtherParams: Default, - C::Hash: EncodeAsType, { /// Removes a contract code from the blockchain. /// @@ -188,7 +180,12 @@ 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, + { let code_hash = self.final_code_hash; let call = RemoveCode::new(code_hash).build(); diff --git a/crates/extrinsics/src/upload.rs b/crates/extrinsics/src/upload.rs index 5696ab9ed..9afedcb99 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -59,14 +59,14 @@ struct UploadOpts { } /// A builder for the upload command. -pub struct UploadCommandBuilder { +pub struct UploadCommandBuilder { opts: UploadOpts, - marker: PhantomData (C, ExtrinsicOptions)>, + marker: PhantomData ExtrinsicOptions>, } -impl UploadCommandBuilder> { +impl UploadCommandBuilder> { /// Returns a clean builder for [`UploadExec`]. - pub fn new() -> UploadCommandBuilder> { + pub fn new() -> UploadCommandBuilder> { UploadCommandBuilder { opts: UploadOpts { extrinsic_opts: ExtrinsicOpts::default(), @@ -79,7 +79,7 @@ impl UploadCommandBuilder> { pub fn extrinsic_opts( self, extrinsic_opts: ExtrinsicOpts, - ) -> UploadCommandBuilder { + ) -> UploadCommandBuilder { UploadCommandBuilder { opts: UploadOpts { extrinsic_opts }, marker: PhantomData, @@ -87,13 +87,13 @@ impl UploadCommandBuilder> { } } -impl Default for UploadCommandBuilder> { +impl Default for UploadCommandBuilder> { fn default() -> Self { Self::new() } } -impl UploadCommandBuilder { +impl UploadCommandBuilder { /// Preprocesses contract artifacts and options for subsequent upload. /// /// This function prepares the necessary data for uploading a contract @@ -103,7 +103,7 @@ impl UploadCommandBuilder { /// /// Returns the `UploadExec` containing the preprocessed data for the upload or /// execution. - pub async fn done(self) -> Result> { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; let signer = self.opts.extrinsic_opts.signer()?; From ba00e698c1d0879dd2b379dc5b8735844a701ece Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Wed, 24 Jan 2024 11:13:33 +0100 Subject: [PATCH 08/39] Add generics for Upload and Remove commands --- crates/cargo-contract/src/cmd/upload.rs | 3 ++- crates/extrinsics/src/upload.rs | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index afcee2fe6..2574e083e 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -30,6 +30,7 @@ use contract_extrinsics::{ UploadExec, UploadResult, }; +use ink_env::DefaultEnvironment; use subxt::{ Config, PolkadotConfig as DefaultConfig, @@ -67,7 +68,7 @@ impl UploadCommand { let code_hash = upload_exec.code().code_hash(); if !self.extrinsic_cli_opts.execute { - match upload_exec.upload_code_rpc().await? { + match upload_exec.upload_code_rpc::().await? { Ok(result) => { let upload_result = UploadDryRunResult { result: String::from("Success!"), diff --git a/crates/extrinsics/src/upload.rs b/crates/extrinsics/src/upload.rs index 9afedcb99..35068352f 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -23,7 +23,6 @@ use super::{ state, state_call, submit_extrinsic, - Balance, ErrorVariant, Missing, TokenMetadata, @@ -37,6 +36,7 @@ use crate::{ use anyhow::Result; use contract_transcode::ContractMessageTranscoder; use core::marker::PhantomData; +use ink_env::Environment; use pallet_contracts_primitives::CodeUploadResult; use scale::Encode; use subxt::{ @@ -160,7 +160,9 @@ where /// It constructs a [`CodeUploadRequest`] with the code and relevant parameters, /// 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> { + pub async fn upload_code_rpc( + &self, + ) -> Result> { let storage_deposit_limit = self .opts .storage_deposit_limit_balance(&self.token_metadata)?; @@ -236,7 +238,7 @@ where /// A struct that encodes RPC parameters required for a call to upload a new code. #[derive(Encode)] -struct CodeUploadRequest { +struct CodeUploadRequest { origin: AccountId, code: Vec, storage_deposit_limit: Option, From 63e3ecdb14bf98a6d3e0f907a303ce75b3f0cc66 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Wed, 24 Jan 2024 13:04:09 +0100 Subject: [PATCH 09/39] Add generics to instantiate command --- crates/cargo-contract/src/cmd/instantiate.rs | 30 ++--- crates/cargo-contract/src/cmd/remove.rs | 4 +- crates/cargo-contract/src/cmd/upload.rs | 4 +- crates/extrinsics/src/call.rs | 2 +- crates/extrinsics/src/events.rs | 4 +- crates/extrinsics/src/extrinsic_calls.rs | 17 ++- crates/extrinsics/src/instantiate.rs | 122 +++++++++++-------- crates/extrinsics/src/integration_tests.rs | 4 +- crates/extrinsics/src/lib.rs | 10 +- crates/extrinsics/src/remove.rs | 8 +- crates/extrinsics/src/upload.rs | 10 +- 11 files changed, 121 insertions(+), 94 deletions(-) diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index c7067f9c6..76732a5a3 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -50,6 +50,7 @@ use contract_extrinsics::{ }; use sp_core::Bytes; use std::fmt::Debug; +use subxt::PolkadotConfig as DefaultConfig; #[derive(Debug, clap::Args)] pub struct InstantiateCommand { @@ -102,16 +103,17 @@ impl InstantiateCommand { .suri(self.extrinsic_cli_opts.suri.clone()) .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) .done(); - let instantiate_exec = 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 = + 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?; if !self.extrinsic_cli_opts.execute { let result = instantiate_exec.instantiate_dry_run().await?; @@ -176,7 +178,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 { @@ -232,8 +234,8 @@ 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_result: InstantiateExecResult, + instantiate_exec: &InstantiateExec, + instantiate_exec_result: InstantiateExecResult, output_json: bool, verbosity: Verbosity, ) -> Result<(), ErrorVariant> { @@ -266,7 +268,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/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index 789fcd366..87e9e2f9e 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -26,7 +26,6 @@ use contract_extrinsics::{ ExtrinsicOptsBuilder, RemoveCommandBuilder, RemoveExec, - RemoveResult, }; use ink_env::DefaultEnvironment; use subxt::Config; @@ -63,8 +62,7 @@ impl RemoveCommand { .extrinsic_opts(extrinsic_opts) .done() .await?; - let remove_result: RemoveResult<_, DefaultEnvironment> = - remove_exec.remove_code().await?; + let remove_result = remove_exec.remove_code::().await?; let display_events = remove_result.display_events; let output_events = if self.output_json() { display_events.to_json()? diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index 2574e083e..e62f6ee0c 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -28,7 +28,6 @@ use contract_extrinsics::{ ExtrinsicOptsBuilder, UploadCommandBuilder, UploadExec, - UploadResult, }; use ink_env::DefaultEnvironment; use subxt::{ @@ -93,8 +92,7 @@ impl UploadCommand { } } } else { - let upload_result: UploadResult = - upload_exec.upload_code().await?; + let upload_result = upload_exec.upload_code().await?; let display_events = upload_result.display_events; let output_events = if self.output_json() { display_events.to_json()? diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index 9db0b18c1..ee3a88b47 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -242,7 +242,7 @@ impl CallExec { .opts .storage_deposit_limit_balance(&self.token_metadata)?; let call_request = CallRequest { - origin: account_id(&self.signer), + origin: account_id::(&self.signer), dest: self.contract.clone(), value: self.value.denominate_balance(&self.token_metadata)?, gas_limit: None, diff --git a/crates/extrinsics/src/events.rs b/crates/extrinsics/src/events.rs index f6371ab00..f0320f791 100644 --- a/crates/extrinsics/src/events.rs +++ b/crates/extrinsics/src/events.rs @@ -129,13 +129,13 @@ where )] #[decode_as_type(crate_path = "subxt::ext::scale_decode")] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub struct CodeRemoved { +pub struct CodeRemoved { pub code_hash: Hash, pub deposit_released: Balance, pub remover: AccountId, } -impl StaticEvent for CodeRemoved +impl StaticEvent for CodeRemoved where Hash: IntoVisitor, Balance: IntoVisitor, diff --git a/crates/extrinsics/src/extrinsic_calls.rs b/crates/extrinsics/src/extrinsic_calls.rs index 9379fbabf..f2ba40bc1 100644 --- a/crates/extrinsics/src/extrinsic_calls.rs +++ b/crates/extrinsics/src/extrinsic_calls.rs @@ -16,7 +16,6 @@ use super::{ Balance, - CodeHash, DefaultConfig, }; use crate::{ @@ -146,23 +145,29 @@ impl InstantiateWithCode { /// A raw call to `pallet-contracts`'s `instantiate_with_code_hash`. #[derive(Debug, EncodeAsType)] -#[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub(crate) struct Instantiate { +#[encode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_encode")] +pub(crate) struct Instantiate +where + C::Hash: EncodeAsType, +{ #[codec(compact)] value: Balance, gas_limit: Weight, storage_deposit_limit: Option>, - code_hash: CodeHash, + code_hash: C::Hash, data: Vec, salt: Vec, } -impl Instantiate { +impl Instantiate +where + C::Hash: EncodeAsType, +{ pub fn new( value: Balance, gas_limit: sp_weights::Weight, storage_deposit_limit: Option, - code_hash: CodeHash, + code_hash: C::Hash, data: Vec, salt: Vec, ) -> Self { diff --git a/crates/extrinsics/src/instantiate.rs b/crates/extrinsics/src/instantiate.rs index ca00becd5..a00fd7fe1 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -25,10 +25,7 @@ use super::{ submit_extrinsic, Balance, BalanceVariant, - Client, - CodeHash, ContractMessageTranscoder, - DefaultConfig, ErrorVariant, Missing, StorageDeposit, @@ -53,15 +50,24 @@ use subxt_signer::sr25519::Keypair; use pallet_contracts_primitives::ContractInstantiateResult; use core::marker::PhantomData; -use scale::Encode; +use scale::{ + Decode, + Encode, +}; use sp_core::Bytes; use sp_weights::Weight; +use std::fmt::Display; use subxt::{ backend::{ legacy::LegacyRpcMethods, rpc::RpcClient, }, blocks::ExtrinsicEvents, + config, + ext::{ + scale_decode::IntoVisitor, + scale_encode::EncodeAsType, + }, Config, OnlineClient, }; @@ -77,14 +83,14 @@ struct InstantiateOpts { } /// A builder for the instantiate command. -pub struct InstantiateCommandBuilder { +pub struct InstantiateCommandBuilder { opts: InstantiateOpts, - marker: PhantomData ExtrinsicOptions>, + _marker: PhantomData (C, ExtrinsicOptions)>, } -impl InstantiateCommandBuilder> { +impl InstantiateCommandBuilder> { /// Returns a clean builder for [`InstantiateExec`]. - pub fn new() -> InstantiateCommandBuilder> { + pub fn new() -> InstantiateCommandBuilder> { InstantiateCommandBuilder { opts: InstantiateOpts { constructor: String::from("new"), @@ -95,7 +101,7 @@ impl InstantiateCommandBuilder> { proof_size: None, salt: None, }, - marker: PhantomData, + _marker: PhantomData, } } @@ -103,24 +109,26 @@ impl InstantiateCommandBuilder> { pub fn extrinsic_opts( self, extrinsic_opts: ExtrinsicOpts, - ) -> InstantiateCommandBuilder { + ) -> InstantiateCommandBuilder { InstantiateCommandBuilder { opts: InstantiateOpts { extrinsic_opts, ..self.opts }, - marker: PhantomData, + _marker: PhantomData, } } } -impl Default for InstantiateCommandBuilder> { +impl Default + for InstantiateCommandBuilder> +{ fn default() -> Self { Self::new() } } -impl InstantiateCommandBuilder { +impl InstantiateCommandBuilder { /// Sets the name of the contract constructor to call. pub fn constructor>(self, constructor: T) -> Self { let mut this = self; @@ -164,7 +172,10 @@ impl InstantiateCommandBuilder { } } -impl InstantiateCommandBuilder { +impl InstantiateCommandBuilder +where + C::Hash: From<[u8; 32]>, +{ /// Preprocesses contract artifacts and options for instantiation. /// /// This function prepares the required data for instantiating a contract based on the @@ -174,7 +185,7 @@ impl InstantiateCommandBuilder { /// /// Returns the [`InstantiateExec`] containing the preprocessed data for the /// instantiation, or an error in case of failure. - pub async fn done(self) -> Result { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; let data = transcoder.encode(&self.opts.constructor, &self.opts.args)?; @@ -223,19 +234,19 @@ impl InstantiateCommandBuilder { } } -pub struct InstantiateArgs { +pub struct InstantiateArgs { constructor: String, raw_args: Vec, value: Balance, gas_limit: Option, proof_size: Option, storage_deposit_limit: Option, - code: Code, + code: Code, data: Vec, salt: Vec, } -impl InstantiateArgs { +impl InstantiateArgs { /// Returns the constructor name. pub fn constructor(&self) -> &str { &self.constructor @@ -266,7 +277,7 @@ impl InstantiateArgs { self.storage_deposit_limit.map(Into::into) } - pub fn code(&self) -> &Code { + pub fn code(&self) -> &Code { &self.code } @@ -281,18 +292,26 @@ impl InstantiateArgs { } } -pub struct InstantiateExec { +pub struct InstantiateExec { opts: ExtrinsicOpts, - args: InstantiateArgs, + args: InstantiateArgs, url: String, - rpc: LegacyRpcMethods, - client: Client, + 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, +{ /// Decodes the result of a simulated contract instantiation. /// /// This function decodes the result of a simulated contract instantiation dry run. @@ -303,11 +322,7 @@ impl InstantiateExec { /// Returns the decoded dry run result, or an error in case of failure. pub async fn decode_instantiate_dry_run( &self, - result: &ContractInstantiateResult< - ::AccountId, - Balance, - (), - >, + result: &ContractInstantiateResult, ) -> Result { tracing::debug!("instantiate data {:?}", self.args.data); match result.result { @@ -346,12 +361,10 @@ impl InstantiateExec { /// Returns the dry run simulation result, or an error in case of failure. pub async fn instantiate_dry_run( &self, - ) -> Result< - ContractInstantiateResult<::AccountId, Balance, ()>, - > { + ) -> Result> { let storage_deposit_limit = self.args.storage_deposit_limit; - let call_request = InstantiateRequest { - origin: account_id(&self.signer), + let call_request = InstantiateRequest:: { + origin: account_id::(&self.signer), value: self.args.value, gas_limit: None, storage_deposit_limit, @@ -366,7 +379,7 @@ impl InstantiateExec { &self, code: Vec, gas_limit: Weight, - ) -> Result { + ) -> Result, ErrorVariant> { let call = InstantiateWithCode::new( self.args.value, gas_limit, @@ -383,11 +396,11 @@ impl InstantiateExec { // The CodeStored event is only raised if the contract has not already been // uploaded. let code_hash = result - .find_first::::Hash>>()? + .find_first::>()? .map(|code_stored| code_stored.code_hash); let instantiated = result - .find_last::::AccountId>>()? + .find_last::>()? .ok_or_else(|| anyhow!("Failed to find Instantiated event"))?; Ok(InstantiateExecResult { @@ -400,10 +413,10 @@ impl InstantiateExec { async fn instantiate_with_code_hash( &self, - code_hash: CodeHash, + code_hash: C::Hash, gas_limit: Weight, - ) -> Result { - let call = Instantiate::new( + ) -> Result, ErrorVariant> { + let call = Instantiate::::new( self.args.value, gas_limit, self.args.storage_deposit_limit, @@ -417,7 +430,7 @@ impl InstantiateExec { submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; let instantiated = result - .find_first::::AccountId>>()? + .find_first::>()? .ok_or_else(|| anyhow!("Failed to find Instantiated event"))?; Ok(InstantiateExecResult { @@ -441,7 +454,7 @@ impl InstantiateExec { pub async fn instantiate( &self, gas_limit: Option, - ) -> Result { + ) -> Result, ErrorVariant> { // use user specified values where provided, otherwise estimate let gas_limit = match gas_limit { Some(gas_limit) => gas_limit, @@ -500,7 +513,7 @@ impl InstantiateExec { } /// Returns the instantiate arguments. - pub fn args(&self) -> &InstantiateArgs { + pub fn args(&self) -> &InstantiateArgs { &self.args } @@ -510,7 +523,7 @@ impl InstantiateExec { } /// Returns the client. - pub fn client(&self) -> &Client { + pub fn client(&self) -> &OnlineClient { &self.client } @@ -525,10 +538,10 @@ impl InstantiateExec { } } -pub struct InstantiateExecResult { - pub result: ExtrinsicEvents, - pub code_hash: Option, - pub contract_address: subxt::utils::AccountId32, +pub struct InstantiateExecResult { + pub result: ExtrinsicEvents, + pub code_hash: Option, + pub contract_address: C::AccountId, pub token_metadata: TokenMetadata, } @@ -556,21 +569,24 @@ impl InstantiateDryRunResult { /// A struct that encodes RPC parameters required to instantiate a new smart contract. #[derive(Encode)] -struct InstantiateRequest { - origin: ::AccountId, +struct InstantiateRequest { + origin: C::AccountId, value: Balance, gas_limit: Option, storage_deposit_limit: Option, - code: Code, + code: Code, data: Vec, salt: Vec, } /// Reference to an existing code hash or a new Wasm module. #[derive(Clone, Encode)] -pub enum Code { +pub enum Code +where + Hash: Clone, +{ /// A Wasm module as raw bytes. Upload(Vec), /// The code hash of an on-chain Wasm blob. - Existing(::Hash), + Existing(Hash), } diff --git a/crates/extrinsics/src/integration_tests.rs b/crates/extrinsics/src/integration_tests.rs index 0a3ea3178..548427e4d 100644 --- a/crates/extrinsics/src/integration_tests.rs +++ b/crates/extrinsics/src/integration_tests.rs @@ -18,6 +18,7 @@ use crate::{ CallCommandBuilder, ExtrinsicOptsBuilder, InstantiateCommandBuilder, + InstantiateExecResult, RemoveCommandBuilder, RemoveExec, UploadCommandBuilder, @@ -488,7 +489,8 @@ async fn api_build_upload_instantiate_call() { .unwrap(); let instantiate_result = instantiate.instantiate(None).await; assert!(instantiate_result.is_ok(), "instantiate code failed"); - let instantiate_result = instantiate_result.unwrap(); + let instantiate_result: InstantiateExecResult = + instantiate_result.unwrap(); let contract_account = instantiate_result.contract_address.to_string(); assert_eq!(48, contract_account.len(), "{contract_account:?}"); diff --git a/crates/extrinsics/src/lib.rs b/crates/extrinsics/src/lib.rs index 9891741ba..9bd930ffb 100644 --- a/crates/extrinsics/src/lib.rs +++ b/crates/extrinsics/src/lib.rs @@ -128,8 +128,14 @@ impl WasmCode { } /// Get the account id from the Keypair -pub fn account_id(keypair: &Keypair) -> AccountId32 { - subxt::tx::Signer::::account_id(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. diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index 5dfcbd193..002dfb047 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -182,7 +182,7 @@ where /// code removal, or an error in case of failure. pub async fn remove_code( &self, - ) -> Result, ErrorVariant> + ) -> Result, ErrorVariant> where E::Balance: IntoVisitor, { @@ -199,7 +199,7 @@ where )?; let code_removed = - result.find_first::>()?; + result.find_first::>()?; Ok(RemoveResult { code_removed, display_events, @@ -237,7 +237,7 @@ where } } -pub struct RemoveResult { - pub code_removed: Option>, +pub struct RemoveResult { + pub code_removed: Option>, pub display_events: DisplayEvents, } diff --git a/crates/extrinsics/src/upload.rs b/crates/extrinsics/src/upload.rs index 35068352f..4c2039e64 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -167,7 +167,7 @@ where .opts .storage_deposit_limit_balance(&self.token_metadata)?; let call_request = CodeUploadRequest { - origin: account_id(&self.signer), + origin: account_id::(&self.signer), code: self.code.0.clone(), storage_deposit_limit, determinism: Determinism::Enforced, @@ -181,7 +181,7 @@ 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> { + pub async fn upload_code(&self) -> Result, ErrorVariant> { let storage_deposit_limit = self .opts .storage_deposit_limit_balance(&self.token_metadata)?; @@ -199,7 +199,7 @@ where DisplayEvents::from_events(&result, None, &self.client.metadata())?; let code_stored = result.find_first::>()?; - Ok(UploadResult:: { + Ok(UploadResult:: { code_stored, display_events, }) @@ -245,8 +245,8 @@ struct CodeUploadRequest { determinism: Determinism, } -pub struct UploadResult { - pub code_stored: Option>, +pub struct UploadResult { + pub code_stored: Option>, pub display_events: DisplayEvents, } From a50185e16f86320b559bbafc0095a61fe551d5f2 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 2 Feb 2024 16:43:49 +0100 Subject: [PATCH 10/39] Add generic to extrinsics crate --- crates/cargo-contract/src/cmd/call.rs | 12 +- crates/cargo-contract/src/cmd/instantiate.rs | 25 ++-- crates/cargo-contract/src/cmd/mod.rs | 18 +-- crates/cargo-contract/src/cmd/remove.rs | 15 +-- crates/cargo-contract/src/cmd/upload.rs | 21 ++-- crates/extrinsics/Cargo.toml | 1 + crates/extrinsics/src/balance.rs | 27 ++-- crates/extrinsics/src/call.rs | 123 ++++++++++++------- crates/extrinsics/src/events.rs | 23 +++- crates/extrinsics/src/extrinsic_calls.rs | 33 +++-- crates/extrinsics/src/extrinsic_opts.rs | 51 +++++--- crates/extrinsics/src/instantiate.rs | 92 ++++++++------ crates/extrinsics/src/lib.rs | 13 +- crates/extrinsics/src/remove.rs | 48 +++++--- crates/extrinsics/src/upload.rs | 46 ++++--- 15 files changed, 331 insertions(+), 217 deletions(-) diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index 89fc3645b..45338d96d 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -17,6 +17,10 @@ use crate::ErrorVariant; use contract_build::util::DEFAULT_KEY_COL_WIDTH; +use ink_env::{ + DefaultEnvironment, + Environment, +}; use std::fmt::Debug; use super::{ @@ -73,7 +77,7 @@ pub struct CallCommand { proof_size: Option, /// The value to be transferred as part of the call. #[clap(name = "value", long, default_value = "0")] - value: BalanceVariant, + value: BalanceVariant<::Balance>, /// Export the call output in JSON format. #[clap(long, conflicts_with = "verbose")] output_json: bool, @@ -176,7 +180,7 @@ impl CallCommand { let output = if self.output_json() { display_events.to_json()? } else { - display_events.display_events( + display_events.display_events::( self.extrinsic_cli_opts.verbosity().unwrap(), call_exec.token_metadata(), )? @@ -189,7 +193,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 { @@ -245,7 +249,7 @@ pub struct CallDryRunResult { pub gas_consumed: Weight, pub gas_required: Weight, /// Storage deposit after the operation - pub storage_deposit: StorageDeposit, + pub storage_deposit: StorageDeposit<::Balance>, } impl CallDryRunResult { diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index 76732a5a3..82ad40c90 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -48,6 +48,10 @@ use contract_extrinsics::{ InstantiateDryRunResult, InstantiateExecResult, }; +use ink_env::{ + DefaultEnvironment, + Environment, +}; use sp_core::Bytes; use std::fmt::Debug; use subxt::PolkadotConfig as DefaultConfig; @@ -64,7 +68,7 @@ pub struct InstantiateCommand { extrinsic_cli_opts: CLIExtrinsicOpts, /// Transfers an initial balance to the instantiated contract #[clap(name = "value", long, default_value = "0")] - value: BalanceVariant, + value: BalanceVariant<::Balance>, /// Maximum amount of gas to be used for this command. /// If not specified will perform a dry-run to estimate the gas consumed for the /// instantiation. @@ -103,7 +107,7 @@ impl InstantiateCommand { .suri(self.extrinsic_cli_opts.suri.clone()) .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) .done(); - let instantiate_exec: InstantiateExec = + let instantiate_exec: InstantiateExec = InstantiateCommandBuilder::default() .constructor(self.constructor.clone()) .args(self.args.clone()) @@ -178,7 +182,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 { @@ -234,12 +238,12 @@ 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, output_json: bool, verbosity: Verbosity, ) -> Result<(), ErrorVariant> { - let events = DisplayEvents::from_events( + let events = DisplayEvents::from_events::( &instantiate_exec_result.result, Some(instantiate_exec.transcoder()), &instantiate_exec.client().metadata(), @@ -257,7 +261,10 @@ pub async fn display_result( } else { println!( "{}", - events.display_events(verbosity, &instantiate_exec_result.token_metadata)? + events.display_events::( + verbosity, + &instantiate_exec_result.token_metadata + )? ); if let Some(code_hash) = instantiate_exec_result.code_hash { name_value_println!("Code hash", format!("{code_hash:?}")); @@ -268,7 +275,7 @@ pub async fn display_result( } pub fn print_default_instantiate_preview( - instantiate_exec: &InstantiateExec, + instantiate_exec: &InstantiateExec, gas_limit: Weight, ) { name_value_println!( @@ -303,7 +310,9 @@ impl InstantiateResult { } } -pub fn print_instantiate_dry_run_result(result: &InstantiateDryRunResult) { +pub fn print_instantiate_dry_run_result( + result: &InstantiateDryRunResult<::Balance>, +) { name_value_println!( "Result", format!("{}", result.result), diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index 4dc811f7f..fd03b7e61 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -64,12 +64,13 @@ use contract_build::{ VerbosityFlags, DEFAULT_KEY_COL_WIDTH, }; +use contract_extrinsics::BalanceVariant; pub(crate) use contract_extrinsics::ErrorVariant; -use contract_extrinsics::{ - Balance, - BalanceVariant, -}; use core::fmt; +use ink_env::{ + DefaultEnvironment, + Environment, +}; use pallet_contracts_primitives::ContractResult; use std::io::{ self, @@ -113,7 +114,8 @@ pub struct CLIExtrinsicOpts { /// The maximum amount of balance that can be charged from the caller to pay for the /// storage. consumed. #[clap(long)] - storage_deposit_limit: Option, + storage_deposit_limit: + Option::Balance>>, /// Before submitting a transaction, do not dry-run it via RPC first. #[clap(long)] skip_dry_run: bool, @@ -134,7 +136,7 @@ pub const MAX_KEY_COL_WIDTH: usize = STORAGE_DEPOSIT_KEY.len() + 1; /// Print to stdout the fields of the result of a `instantiate` or `call` dry-run via RPC. pub fn display_contract_exec_result( - result: &ContractResult, + result: &ContractResult::Balance, ()>, ) -> Result<()> { let mut debug_message_lines = std::str::from_utf8(&result.debug_message) .context("Error decoding UTF8 debug message bytes")? @@ -159,7 +161,7 @@ pub fn display_contract_exec_result( } pub fn display_contract_exec_result_debug( - result: &ContractResult, + result: &ContractResult::Balance, ()>, ) -> Result<()> { let mut debug_message_lines = std::str::from_utf8(&result.debug_message) .context("Error decoding UTF8 debug message bytes")? @@ -226,7 +228,7 @@ pub fn print_gas_required_success(gas: Weight) { /// Display contract information in a formatted way pub fn basic_display_format_extended_contract_info( - info: &ExtendedContractInfo, + info: &ExtendedContractInfo::Balance>, ) where Hash: fmt::Debug, { diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index 87e9e2f9e..1a190a4a3 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -57,17 +57,18 @@ impl RemoveCommand { .suri(self.extrinsic_cli_opts.suri.clone()) .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) .done(); - let remove_exec: RemoveExec = RemoveCommandBuilder::default() - .code_hash(self.code_hash) - .extrinsic_opts(extrinsic_opts) - .done() - .await?; - let remove_result = remove_exec.remove_code::().await?; + let remove_exec: RemoveExec = + RemoveCommandBuilder::default() + .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 output_events = if self.output_json() { display_events.to_json()? } else { - display_events.display_events( + display_events.display_events::( self.extrinsic_cli_opts.verbosity().unwrap(), remove_exec.token_metadata(), )? diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index e62f6ee0c..cba404312 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -24,12 +24,14 @@ use super::{ use anyhow::Result; use contract_build::name_value_println; use contract_extrinsics::{ - Balance, ExtrinsicOptsBuilder, UploadCommandBuilder, UploadExec, }; -use ink_env::DefaultEnvironment; +use ink_env::{ + DefaultEnvironment, + Environment, +}; use subxt::{ Config, PolkadotConfig as DefaultConfig, @@ -59,15 +61,16 @@ impl UploadCommand { .suri(self.extrinsic_cli_opts.suri.clone()) .storage_deposit_limit(self.extrinsic_cli_opts.storage_deposit_limit.clone()) .done(); - let upload_exec: UploadExec = UploadCommandBuilder::default() - .extrinsic_opts(extrinsic_opts) - .done() - .await?; + let upload_exec: UploadExec = + UploadCommandBuilder::default() + .extrinsic_opts(extrinsic_opts) + .done() + .await?; let code_hash = upload_exec.code().code_hash(); if !self.extrinsic_cli_opts.execute { - match upload_exec.upload_code_rpc::().await? { + match upload_exec.upload_code_rpc().await? { Ok(result) => { let upload_result = UploadDryRunResult { result: String::from("Success!"), @@ -97,7 +100,7 @@ impl UploadCommand { let output_events = if self.output_json() { display_events.to_json()? } else { - display_events.display_events( + display_events.display_events::( self.extrinsic_cli_opts.verbosity()?, upload_exec.token_metadata(), )? @@ -131,7 +134,7 @@ impl UploadCommand { pub struct UploadDryRunResult { pub result: String, pub code_hash: String, - pub deposit: Balance, + pub deposit: ::Balance, } impl UploadDryRunResult { diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index 09008ee2b..4bcc29877 100644 --- a/crates/extrinsics/Cargo.toml +++ b/crates/extrinsics/Cargo.toml @@ -52,5 +52,6 @@ tempfile = "3.9.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } [features] +default = ["integration-tests"] integration-tests = [] test-ci-only = [] diff --git a/crates/extrinsics/src/balance.rs b/crates/extrinsics/src/balance.rs index 4965f7a96..cf488a513 100644 --- a/crates/extrinsics/src/balance.rs +++ b/crates/extrinsics/src/balance.rs @@ -30,8 +30,6 @@ use subxt::{ Config, }; -use super::Balance; - use anyhow::{ anyhow, Context, @@ -40,7 +38,7 @@ use anyhow::{ /// 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 @@ -98,7 +96,10 @@ impl TokenMetadata { } } -impl FromStr for BalanceVariant { +impl FromStr for BalanceVariant +where + Balance: FromStr + Clone, +{ type Err = anyhow::Error; /// Attempts to parse the balance either in plain or denominated formats @@ -157,7 +158,10 @@ impl FromStr for DenominatedBalance { } } -impl BalanceVariant { +impl BalanceVariant +where + Balance: From + Clone, +{ /// Converts BalanceVariant into Balance. /// /// It is a reverse process of `from>()` @@ -197,7 +201,7 @@ impl BalanceVariant { /// ``` pub fn denominate_balance(&self, token_metadata: &TokenMetadata) -> Result { match self { - BalanceVariant::Default(balance) => Ok(*balance), + BalanceVariant::Default(balance) => Ok(balance.clone()), BalanceVariant::Denominated(den_balance) => { let zeros: usize = (token_metadata.token_decimals as isize + match den_balance.unit { @@ -219,12 +223,12 @@ impl BalanceVariant { "Given precision of a Balance value is higher than allowed" )) } - let balance: Balance = den_balance + let balance: u128 = den_balance .value .checked_mul(multiple) .context("error while converting balance to raw format. Overflow during multiplication!")? .try_into()?; - Ok(balance) + Ok(balance.into()) } } } @@ -345,12 +349,15 @@ impl BalanceVariant { Ok(BalanceVariant::Denominated(den_balance)) } else { - Ok(BalanceVariant::Default(n)) + Ok(BalanceVariant::Default(n.into())) } } } -impl Display for BalanceVariant { +impl Display for BalanceVariant +where + Balance: Display + Clone, +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { BalanceVariant::Default(balance) => f.write_str(&balance.to_string()), diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index ee3a88b47..4385b0fb7 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -20,12 +20,8 @@ use super::{ state, state_call, submit_extrinsic, - AccountId32, - Balance, BalanceVariant, - Client, ContractMessageTranscoder, - DefaultConfig, ErrorVariant, Missing, TokenMetadata, @@ -40,53 +36,69 @@ use anyhow::{ anyhow, Result, }; +use ink_env::Environment; use pallet_contracts_primitives::ContractExecResult; 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, }, + ext::{ + scale_decode::IntoVisitor, + scale_encode::EncodeAsType, + }, Config, OnlineClient, }; -pub struct CallOpts { - contract: ::AccountId, +pub struct CallOpts { + contract: Option, message: String, args: Vec, - extrinsic_opts: ExtrinsicOpts, + extrinsic_opts: ExtrinsicOpts, gas_limit: Option, proof_size: Option, - value: BalanceVariant, + value: BalanceVariant, } /// A builder for the call command. -pub struct CallCommandBuilder { - opts: CallOpts, +pub struct CallCommandBuilder { + opts: CallOpts, marker: PhantomData (Message, ExtrinsicOptions)>, } -impl Default - for CallCommandBuilder, Missing> +impl Default + for CallCommandBuilder< + C, + E, + Missing, + Missing, + > +where + E::Balance: FromStr + From, { fn default() -> Self { Self::new() } } -impl CallCommandBuilder, E> { +impl CallCommandBuilder, T> +where + E::Balance: FromStr + From, +{ /// Returns a clean builder for [`CallExec`]. pub fn new( - ) -> CallCommandBuilder, Missing> + ) -> CallCommandBuilder, Missing> { CallCommandBuilder { opts: CallOpts { - contract: AccountId32([0; 32]), + contract: None, message: String::new(), args: Vec::new(), extrinsic_opts: ExtrinsicOpts::default(), @@ -99,10 +111,10 @@ impl CallCommandBuilder, E> { } /// Sets the name of the contract message to call. - pub fn message>( + pub fn message>( self, - message: T, - ) -> CallCommandBuilder { + message: M, + ) -> CallCommandBuilder { CallCommandBuilder { opts: CallOpts { message: message.into(), @@ -113,12 +125,14 @@ impl CallCommandBuilder, E> { } } -impl CallCommandBuilder> { +impl + CallCommandBuilder> +{ /// Sets the extrinsic operation. pub fn extrinsic_opts( self, - extrinsic_opts: ExtrinsicOpts, - ) -> CallCommandBuilder { + extrinsic_opts: ExtrinsicOpts, + ) -> CallCommandBuilder { CallCommandBuilder { opts: CallOpts { extrinsic_opts, @@ -129,11 +143,11 @@ impl CallCommandBuilder> { } } -impl CallCommandBuilder { +impl CallCommandBuilder { /// Sets the the address of the the contract to call. - pub fn contract(self, contract: ::AccountId) -> Self { + pub fn contract(self, contract: C::AccountId) -> Self { let mut this = self; - this.opts.contract = contract; + this.opts.contract = Some(contract); this } @@ -159,14 +173,19 @@ impl CallCommandBuilder { } /// Sets the value to be transferred as part of the call. - pub fn value(self, value: BalanceVariant) -> Self { + pub fn value(self, value: BalanceVariant) -> Self { let mut this = self; this.opts.value = value; this } } -impl CallCommandBuilder { +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 @@ -176,7 +195,7 @@ impl CallCommandBuilder { /// /// Returns the `CallExec` containing the preprocessed data for the contract call, /// or an error in case of failure. - pub async fn done(self) -> Result { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; @@ -192,9 +211,13 @@ impl CallCommandBuilder { check_env_types(&client, &transcoder)?; let token_metadata = TokenMetadata::query(&rpc).await?; + let contract = self + .opts + .contract + .ok_or(anyhow!("Contract address not set"))?; Ok(CallExec { - contract: self.opts.contract.clone(), + contract, message: self.opts.message.clone(), args: self.opts.args.clone(), opts: self.opts.extrinsic_opts.clone(), @@ -211,23 +234,33 @@ impl CallCommandBuilder { } } -pub struct CallExec { - contract: ::AccountId, +pub struct CallExec +where + E::Balance: From, +{ + contract: C::AccountId, message: String, args: Vec, - opts: ExtrinsicOpts, + opts: ExtrinsicOpts, gas_limit: Option, proof_size: Option, - value: BalanceVariant, - rpc: LegacyRpcMethods, - client: Client, + value: BalanceVariant, + 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, +{ /// Simulates a contract call without modifying the blockchain. /// /// This function performs a dry run simulation of a contract call, capturing @@ -237,12 +270,12 @@ impl CallExec { /// /// 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> { + pub async fn call_dry_run(&self) -> Result> { let storage_deposit_limit = self .opts .storage_deposit_limit_balance(&self.token_metadata)?; let call_request = CallRequest { - origin: account_id::(&self.signer), + origin: account_id::(&self.signer), dest: self.contract.clone(), value: self.value.denominate_balance(&self.token_metadata)?, gas_limit: None, @@ -303,7 +336,7 @@ impl CallExec { let result = submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; - let display_events = DisplayEvents::from_events::( + let display_events = DisplayEvents::from_events::( &result, Some(&self.transcoder), &self.client.metadata(), @@ -352,7 +385,7 @@ impl CallExec { } /// Returns the address of the the contract to call. - pub fn contract(&self) -> &::AccountId { + pub fn contract(&self) -> &C::AccountId { &self.contract } @@ -367,7 +400,7 @@ impl CallExec { } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } @@ -382,12 +415,12 @@ impl CallExec { } /// Returns the value to be transferred as part of the call. - pub fn value(&self) -> &BalanceVariant { + pub fn value(&self) -> &BalanceVariant { &self.value } /// Returns the client. - pub fn client(&self) -> &Client { + pub fn client(&self) -> &OnlineClient { &self.client } @@ -416,9 +449,9 @@ impl CallExec { /// /// Copied from `pallet-contracts-rpc-runtime-api`. #[derive(Encode)] -struct CallRequest { - origin: ::AccountId, - dest: ::AccountId, +struct CallRequest { + origin: AccountId, + dest: AccountId, value: Balance, gas_limit: Option, storage_deposit_limit: Option, diff --git a/crates/extrinsics/src/events.rs b/crates/extrinsics/src/events.rs index f0320f791..7beede2df 100644 --- a/crates/extrinsics/src/events.rs +++ b/crates/extrinsics/src/events.rs @@ -29,9 +29,13 @@ use contract_transcode::{ }; use anyhow::Result; +use ink_env::Environment; use scale_info::form::PortableForm; use std::{ - fmt::Write, + fmt::{ + Display, + Write, + }, str::FromStr, }; use subxt::{ @@ -184,13 +188,14 @@ pub struct DisplayEvents(Vec); impl DisplayEvents { /// Parses events and returns an object which can be serialised - pub fn from_events( + pub fn from_events( result: &ExtrinsicEvents, transcoder: Option<&ContractMessageTranscoder>, subxt_metadata: &subxt::Metadata, ) -> Result where C::AccountId: IntoVisitor, + // E::Balance: /*From +*/, { let mut events: Vec = vec![]; @@ -263,11 +268,14 @@ impl DisplayEvents { } /// Displays events in a human readable format - pub fn display_events( + pub fn display_events( &self, verbosity: Verbosity, token_metadata: &TokenMetadata, - ) -> Result { + ) -> Result + where + E::Balance: Display + From, + { let event_field_indent: usize = DEFAULT_KEY_COL_WIDTH - 3; let mut out = format!( "{:>width$}\n", @@ -291,8 +299,11 @@ impl DisplayEvents { || field.type_name == Some("BalanceOf".to_string()) { if let Value::UInt(balance) = field.value { - value = BalanceVariant::from(balance, Some(token_metadata))? - .to_string(); + value = BalanceVariant::::from( + balance, + Some(token_metadata), + )? + .to_string(); } } let _ = writeln!( diff --git a/crates/extrinsics/src/extrinsic_calls.rs b/crates/extrinsics/src/extrinsic_calls.rs index f2ba40bc1..d1450e859 100644 --- a/crates/extrinsics/src/extrinsic_calls.rs +++ b/crates/extrinsics/src/extrinsic_calls.rs @@ -14,14 +14,11 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use super::{ - Balance, - DefaultConfig, -}; use crate::{ upload::Determinism, WasmCode, }; +use ink_env::Environment; use subxt::{ ext::{ codec::Compact, @@ -82,13 +79,13 @@ impl RemoveCode { /// A raw call to `pallet-contracts`'s `upload_code`. #[derive(Debug, EncodeAsType)] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub(crate) struct UploadCode { +pub(crate) struct UploadCode { code: Vec, storage_deposit_limit: Option>, determinism: Determinism, } -impl UploadCode { +impl UploadCode { pub fn new( code: WasmCode, storage_deposit_limit: Option, @@ -109,7 +106,7 @@ impl UploadCode { /// A raw call to `pallet-contracts`'s `instantiate_with_code`. #[derive(Debug, EncodeAsType)] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub(crate) struct InstantiateWithCode { +pub(crate) struct InstantiateWithCode { #[codec(compact)] value: Balance, gas_limit: Weight, @@ -119,7 +116,7 @@ pub(crate) struct InstantiateWithCode { salt: Vec, } -impl InstantiateWithCode { +impl InstantiateWithCode { pub fn new( value: Balance, gas_limit: sp_weights::Weight, @@ -146,27 +143,27 @@ impl InstantiateWithCode { /// A raw call to `pallet-contracts`'s `instantiate_with_code_hash`. #[derive(Debug, EncodeAsType)] #[encode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_encode")] -pub(crate) struct Instantiate +pub(crate) struct Instantiate where C::Hash: EncodeAsType, { #[codec(compact)] - value: Balance, + value: E::Balance, gas_limit: Weight, - storage_deposit_limit: Option>, + storage_deposit_limit: Option>, code_hash: C::Hash, data: Vec, salt: Vec, } -impl Instantiate +impl Instantiate where C::Hash: EncodeAsType, { pub fn new( - value: Balance, + value: E::Balance, gas_limit: sp_weights::Weight, - storage_deposit_limit: Option, + storage_deposit_limit: Option, code_hash: C::Hash, data: Vec, salt: Vec, @@ -189,8 +186,8 @@ where /// A raw call to `pallet-contracts`'s `call`. #[derive(EncodeAsType)] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub(crate) struct Call { - dest: MultiAddress<::AccountId, ()>, +pub(crate) struct Call { + dest: MultiAddress, #[codec(compact)] value: Balance, gas_limit: Weight, @@ -198,9 +195,9 @@ pub(crate) struct Call { data: Vec, } -impl Call { +impl Call { pub fn new( - dest: MultiAddress<::AccountId, ()>, + dest: MultiAddress, value: Balance, gas_limit: sp_weights::Weight, storage_deposit_limit: Option, diff --git a/crates/extrinsics/src/extrinsic_opts.rs b/crates/extrinsics/src/extrinsic_opts.rs index 188286105..bd570dd47 100644 --- a/crates/extrinsics/src/extrinsic_opts.rs +++ b/crates/extrinsics/src/extrinsic_opts.rs @@ -16,6 +16,7 @@ use core::marker::PhantomData; +use ink_env::Environment; use subxt_signer::{ sr25519::Keypair, SecretUri, @@ -29,7 +30,6 @@ use anyhow::{ use crate::{ url_to_string, - Balance, BalanceVariant, ContractArtifacts, TokenMetadata, @@ -41,12 +41,12 @@ use std::{ /// Arguments required for creating and sending an extrinsic to a substrate node. #[derive(Clone, Debug)] -pub struct ExtrinsicOpts { +pub struct ExtrinsicOpts { file: Option, manifest_path: Option, url: url::Url, suri: String, - storage_deposit_limit: Option, + storage_deposit_limit: Option>, } /// Type state for the extrinsics' commands to tell that some mandatory state has not yet @@ -66,14 +66,17 @@ pub mod state { } /// A builder for extrinsic options. -pub struct ExtrinsicOptsBuilder { - opts: ExtrinsicOpts, +pub struct ExtrinsicOptsBuilder { + opts: ExtrinsicOpts, marker: PhantomData Suri>, } -impl ExtrinsicOptsBuilder> { +impl ExtrinsicOptsBuilder> +where + E::Balance: From, +{ /// Returns a clean builder for `ExtrinsicOpts`. - pub fn new() -> ExtrinsicOptsBuilder> { + pub fn new() -> ExtrinsicOptsBuilder> { ExtrinsicOptsBuilder { opts: ExtrinsicOpts::default(), marker: PhantomData, @@ -81,7 +84,7 @@ impl ExtrinsicOptsBuilder> { } /// Sets the secret key URI for the account deploying the contract. - pub fn suri>(self, suri: T) -> ExtrinsicOptsBuilder { + pub fn suri>(self, suri: T) -> ExtrinsicOptsBuilder { ExtrinsicOptsBuilder { opts: ExtrinsicOpts { suri: suri.into(), @@ -92,13 +95,16 @@ impl ExtrinsicOptsBuilder> { } } -impl Default for ExtrinsicOptsBuilder> { +impl Default for ExtrinsicOptsBuilder> +where + E::Balance: From, +{ fn default() -> Self { Self::new() } } -impl ExtrinsicOptsBuilder { +impl ExtrinsicOptsBuilder { /// Sets the path to the contract build artifact file. pub fn file>(self, file: Option) -> Self { let mut this = self; @@ -124,7 +130,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; @@ -132,17 +138,23 @@ impl ExtrinsicOptsBuilder { } } -impl ExtrinsicOptsBuilder { +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, +{ /// Returns a clean builder for [`ExtrinsicOpts`]. - pub fn new() -> ExtrinsicOptsBuilder> { + pub fn new() -> ExtrinsicOptsBuilder> { ExtrinsicOptsBuilder { opts: Self { file: None, @@ -191,7 +203,7 @@ impl ExtrinsicOpts { } /// Return the storage deposit limit. - pub fn storage_deposit_limit(&self) -> Option<&BalanceVariant> { + pub fn storage_deposit_limit(&self) -> Option<&BalanceVariant> { self.storage_deposit_limit.as_ref() } @@ -199,7 +211,7 @@ impl ExtrinsicOpts { pub fn storage_deposit_limit_balance( &self, token_metadata: &TokenMetadata, - ) -> Result> { + ) -> Result> { Ok(self .storage_deposit_limit .as_ref() @@ -208,7 +220,10 @@ impl ExtrinsicOpts { } } -impl Default for ExtrinsicOpts { +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 a00fd7fe1..32c0af8bf 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -23,7 +23,6 @@ use super::{ state, state_call, submit_extrinsic, - Balance, BalanceVariant, ContractMessageTranscoder, ErrorVariant, @@ -45,6 +44,8 @@ use anyhow::{ Result, }; use contract_transcode::Value; +use ink_env::Environment; +use serde::Serialize; use subxt_signer::sr25519::Keypair; use pallet_contracts_primitives::ContractInstantiateResult; @@ -56,7 +57,10 @@ use scale::{ }; use sp_core::Bytes; use sp_weights::Weight; -use std::fmt::Display; +use std::{ + fmt::Display, + str::FromStr, +}; use subxt::{ backend::{ legacy::LegacyRpcMethods, @@ -72,25 +76,29 @@ use subxt::{ OnlineClient, }; -struct InstantiateOpts { +struct InstantiateOpts { constructor: String, args: Vec, - extrinsic_opts: ExtrinsicOpts, - value: BalanceVariant, + extrinsic_opts: ExtrinsicOpts, + value: BalanceVariant, gas_limit: Option, proof_size: Option, salt: Option, } /// A builder for the instantiate command. -pub struct InstantiateCommandBuilder { - opts: InstantiateOpts, +pub struct InstantiateCommandBuilder { + opts: InstantiateOpts, _marker: PhantomData (C, ExtrinsicOptions)>, } -impl InstantiateCommandBuilder> { +impl + InstantiateCommandBuilder> +where + E::Balance: From + FromStr, +{ /// Returns a clean builder for [`InstantiateExec`]. - pub fn new() -> InstantiateCommandBuilder> { + pub fn new() -> InstantiateCommandBuilder> { InstantiateCommandBuilder { opts: InstantiateOpts { constructor: String::from("new"), @@ -108,8 +116,8 @@ impl InstantiateCommandBuilder> { /// Sets the extrinsic operation. pub fn extrinsic_opts( self, - extrinsic_opts: ExtrinsicOpts, - ) -> InstantiateCommandBuilder { + extrinsic_opts: ExtrinsicOpts, + ) -> InstantiateCommandBuilder { InstantiateCommandBuilder { opts: InstantiateOpts { extrinsic_opts, @@ -120,15 +128,17 @@ impl InstantiateCommandBuilder> { } } -impl Default - for InstantiateCommandBuilder> +impl Default + for InstantiateCommandBuilder> +where + E::Balance: From + FromStr, { fn default() -> Self { Self::new() } } -impl InstantiateCommandBuilder { +impl InstantiateCommandBuilder { /// Sets the name of the contract constructor to call. pub fn constructor>(self, constructor: T) -> Self { let mut this = self; @@ -144,7 +154,7 @@ impl InstantiateCommandBuilder { } /// Sets the initial balance to transfer to the instantiated contract. - pub fn value(self, value: BalanceVariant) -> Self { + pub fn value(self, value: BalanceVariant) -> Self { let mut this = self; this.opts.value = value; this @@ -172,9 +182,10 @@ impl InstantiateCommandBuilder { } } -impl InstantiateCommandBuilder +impl InstantiateCommandBuilder where C::Hash: From<[u8; 32]>, + E::Balance: From, { /// Preprocesses contract artifacts and options for instantiation. /// @@ -185,7 +196,7 @@ where /// /// Returns the [`InstantiateExec`] containing the preprocessed data for the /// instantiation, or an error in case of failure. - pub async fn done(self) -> Result> { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; let data = transcoder.encode(&self.opts.constructor, &self.opts.args)?; @@ -234,19 +245,19 @@ where } } -pub struct InstantiateArgs { +pub struct InstantiateArgs { constructor: String, raw_args: Vec, - value: Balance, + value: E::Balance, gas_limit: Option, proof_size: Option, - storage_deposit_limit: Option, + storage_deposit_limit: Option, code: Code, data: Vec, salt: Vec, } -impl InstantiateArgs { +impl InstantiateArgs { /// Returns the constructor name. pub fn constructor(&self) -> &str { &self.constructor @@ -258,7 +269,7 @@ impl InstantiateArgs { } /// Returns the value to transfer to the instantiated contract. - pub fn value(&self) -> Balance { + pub fn value(&self) -> E::Balance { self.value } @@ -273,7 +284,7 @@ impl InstantiateArgs { } /// Returns the storage deposit limit for this instantiation. - pub fn storage_deposit_limit_compact(&self) -> Option> { + pub fn storage_deposit_limit_compact(&self) -> Option> { self.storage_deposit_limit.map(Into::into) } @@ -292,9 +303,9 @@ impl InstantiateArgs { } } -pub struct InstantiateExec { - opts: ExtrinsicOpts, - args: InstantiateArgs, +pub struct InstantiateExec { + opts: ExtrinsicOpts, + args: InstantiateArgs, url: String, rpc: LegacyRpcMethods, client: OnlineClient, @@ -303,7 +314,7 @@ pub struct InstantiateExec { token_metadata: TokenMetadata, } -impl InstantiateExec +impl InstantiateExec where C::AccountId: Decode, C::Signature: From, @@ -311,6 +322,7 @@ where C::Address: From, C::Hash: IntoVisitor + EncodeAsType, C::AccountId: From + IntoVisitor + Display, + E::Balance: Serialize, { /// Decodes the result of a simulated contract instantiation. /// @@ -322,8 +334,8 @@ where /// Returns the decoded dry run result, or an error in case of failure. pub async fn decode_instantiate_dry_run( &self, - result: &ContractInstantiateResult, - ) -> Result { + result: &ContractInstantiateResult, + ) -> Result, ErrorVariant> { tracing::debug!("instantiate data {:?}", self.args.data); match result.result { Ok(ref ret_val) => { @@ -361,9 +373,9 @@ where /// Returns the dry run simulation result, or an error in case of failure. pub async fn instantiate_dry_run( &self, - ) -> Result> { + ) -> Result> { let storage_deposit_limit = self.args.storage_deposit_limit; - let call_request = InstantiateRequest:: { + let call_request = InstantiateRequest:: { origin: account_id::(&self.signer), value: self.args.value, gas_limit: None, @@ -416,7 +428,7 @@ where code_hash: C::Hash, gas_limit: Weight, ) -> Result, ErrorVariant> { - let call = Instantiate::::new( + let call = Instantiate::::new( self.args.value, gas_limit, self.args.storage_deposit_limit, @@ -508,12 +520,12 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } /// Returns the instantiate arguments. - pub fn args(&self) -> &InstantiateArgs { + pub fn args(&self) -> &InstantiateArgs { &self.args } @@ -547,7 +559,7 @@ pub struct InstantiateExecResult { /// Result of the contract call #[derive(serde::Serialize)] -pub struct InstantiateDryRunResult { +pub struct InstantiateDryRunResult { /// The decoded result returned from the constructor pub result: Value, /// contract address @@ -557,10 +569,10 @@ pub struct InstantiateDryRunResult { pub gas_consumed: Weight, pub gas_required: Weight, /// Storage deposit after the operation - pub storage_deposit: StorageDeposit, + pub storage_deposit: StorageDeposit, } -impl InstantiateDryRunResult { +impl InstantiateDryRunResult { /// Returns a result in json format pub fn to_json(&self) -> Result { Ok(serde_json::to_string_pretty(self)?) @@ -569,11 +581,11 @@ impl InstantiateDryRunResult { /// A struct that encodes RPC parameters required to instantiate a new smart contract. #[derive(Encode)] -struct InstantiateRequest { +struct InstantiateRequest { origin: C::AccountId, - value: Balance, + value: E::Balance, gas_limit: Option, - storage_deposit_limit: Option, + storage_deposit_limit: Option, code: Code, data: Vec, salt: Vec, diff --git a/crates/extrinsics/src/lib.rs b/crates/extrinsics/src/lib.rs index 9bd930ffb..993b68f8a 100644 --- a/crates/extrinsics/src/lib.rs +++ b/crates/extrinsics/src/lib.rs @@ -51,7 +51,6 @@ use subxt::{ blocks, config, tx, - utils::AccountId32, Config, OnlineClient, }; @@ -113,7 +112,6 @@ pub use upload::{ }; pub type Client = OnlineClient; -pub type Balance = u128; pub type CodeHash = ::Hash; /// The Wasm code of a contract. @@ -162,6 +160,7 @@ where Signer: tx::Signer, T::Signature: From, >::OtherParams: Default, + T::Address: From, T::AccountId: From, { let account_id = Signer::account_id(signer); @@ -287,7 +286,7 @@ pub fn url_to_string(url: &url::Url) -> String { /// Copy of `pallet_contracts_primitives::StorageDeposit` which implements `Serialize`, /// required for json output. #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, serde::Serialize)] -pub enum StorageDeposit { +pub enum StorageDeposit { /// The transaction reduced storage consumption. /// /// This means that the specified amount of balance was transferred from the involved @@ -300,14 +299,16 @@ pub enum StorageDeposit { Charge(Balance), } -impl From<&pallet_contracts_primitives::StorageDeposit> for StorageDeposit { +impl From<&pallet_contracts_primitives::StorageDeposit> + for StorageDeposit +{ fn from(deposit: &pallet_contracts_primitives::StorageDeposit) -> Self { match deposit { pallet_contracts_primitives::StorageDeposit::Refund(balance) => { - Self::Refund(*balance) + Self::Refund(balance.clone()) } pallet_contracts_primitives::StorageDeposit::Charge(balance) => { - Self::Charge(*balance) + Self::Charge(balance.clone()) } } } diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index 002dfb047..f2a5f6580 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -49,20 +49,24 @@ use subxt::{ }; use subxt_signer::sr25519::Keypair; -pub struct RemoveOpts { +pub struct RemoveOpts { code_hash: Option, - extrinsic_opts: ExtrinsicOpts, + extrinsic_opts: ExtrinsicOpts, } /// A builder for the remove command. -pub struct RemoveCommandBuilder { - opts: RemoveOpts, +pub struct RemoveCommandBuilder { + opts: RemoveOpts, marker: PhantomData ExtrinsicOptions>, } -impl RemoveCommandBuilder> { +impl + RemoveCommandBuilder> +where + E::Balance: From, +{ /// Returns a clean builder for [`RemoveExec`]. - pub fn new() -> RemoveCommandBuilder> { + pub fn new() -> RemoveCommandBuilder> { RemoveCommandBuilder { opts: RemoveOpts { code_hash: None, @@ -75,8 +79,8 @@ impl RemoveCommandBuilder> { /// Sets the extrinsic operation. pub fn extrinsic_opts( self, - extrinsic_opts: ExtrinsicOpts, - ) -> RemoveCommandBuilder { + extrinsic_opts: ExtrinsicOpts, + ) -> RemoveCommandBuilder { RemoveCommandBuilder { opts: RemoveOpts { extrinsic_opts, @@ -87,13 +91,17 @@ impl RemoveCommandBuilder> { } } -impl Default for RemoveCommandBuilder> { +impl Default + for RemoveCommandBuilder> +where + E::Balance: From, +{ fn default() -> Self { Self::new() } } -impl RemoveCommandBuilder { +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; @@ -102,9 +110,10 @@ impl RemoveCommandBuilder { } } -impl RemoveCommandBuilder +impl RemoveCommandBuilder where C::Hash: From<[u8; 32]>, + E::Balance: From, { /// Preprocesses contract artifacts and options for subsequent removal of contract /// code. @@ -116,7 +125,7 @@ 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> { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; let signer = self.opts.extrinsic_opts.signer()?; @@ -153,9 +162,9 @@ where } } -pub struct RemoveExec { +pub struct RemoveExec { final_code_hash: C::Hash, - opts: ExtrinsicOpts, + opts: ExtrinsicOpts, rpc: LegacyRpcMethods, client: OnlineClient, transcoder: ContractMessageTranscoder, @@ -163,13 +172,14 @@ pub struct RemoveExec { token_metadata: TokenMetadata, } -impl RemoveExec +impl RemoveExec where C::Hash: IntoVisitor + EncodeAsType, C::AccountId: IntoVisitor + From, C::Address: From, C::Signature: From, >::OtherParams: Default, + // E::Balance: From { /// Removes a contract code from the blockchain. /// @@ -180,11 +190,11 @@ 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( + pub async fn remove_code( &self, ) -> Result, ErrorVariant> where - E::Balance: IntoVisitor, + E::Balance: IntoVisitor + Into, { let code_hash = self.final_code_hash; @@ -192,7 +202,7 @@ where let result = submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; - let display_events = DisplayEvents::from_events( + let display_events = DisplayEvents::from_events::( &result, Some(&self.transcoder), &self.client.metadata(), @@ -212,7 +222,7 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } diff --git a/crates/extrinsics/src/upload.rs b/crates/extrinsics/src/upload.rs index 4c2039e64..68a210edd 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -54,19 +54,22 @@ use subxt::{ }; use subxt_signer::sr25519::Keypair; -struct UploadOpts { - extrinsic_opts: ExtrinsicOpts, +struct UploadOpts { + extrinsic_opts: ExtrinsicOpts, } /// A builder for the upload command. -pub struct UploadCommandBuilder { - opts: UploadOpts, +pub struct UploadCommandBuilder { + opts: UploadOpts, marker: PhantomData ExtrinsicOptions>, } -impl UploadCommandBuilder> { +impl UploadCommandBuilder> +where + E::Balance: From, +{ /// Returns a clean builder for [`UploadExec`]. - pub fn new() -> UploadCommandBuilder> { + pub fn new() -> UploadCommandBuilder> { UploadCommandBuilder { opts: UploadOpts { extrinsic_opts: ExtrinsicOpts::default(), @@ -78,8 +81,8 @@ impl UploadCommandBuilder> { /// Sets the extrinsic operation. pub fn extrinsic_opts( self, - extrinsic_opts: ExtrinsicOpts, - ) -> UploadCommandBuilder { + extrinsic_opts: ExtrinsicOpts, + ) -> UploadCommandBuilder { UploadCommandBuilder { opts: UploadOpts { extrinsic_opts }, marker: PhantomData, @@ -87,13 +90,19 @@ impl UploadCommandBuilder> { } } -impl Default for UploadCommandBuilder> { +impl Default for UploadCommandBuilder> +where + E::Balance: From, +{ fn default() -> Self { Self::new() } } -impl UploadCommandBuilder { +impl UploadCommandBuilder +where + E::Balance: From, +{ /// Preprocesses contract artifacts and options for subsequent upload. /// /// This function prepares the necessary data for uploading a contract @@ -103,7 +112,7 @@ impl UploadCommandBuilder { /// /// Returns the `UploadExec` containing the preprocessed data for the upload or /// execution. - pub async fn done(self) -> Result> { + pub async fn done(self) -> Result> { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; let transcoder = artifacts.contract_transcoder()?; let signer = self.opts.extrinsic_opts.signer()?; @@ -136,8 +145,8 @@ impl UploadCommandBuilder { } } -pub struct UploadExec { - opts: ExtrinsicOpts, +pub struct UploadExec { + opts: ExtrinsicOpts, rpc: LegacyRpcMethods, client: OnlineClient, code: WasmCode, @@ -146,13 +155,14 @@ pub struct UploadExec { transcoder: ContractMessageTranscoder, } -impl UploadExec +impl UploadExec where C::Hash: IntoVisitor, C::AccountId: IntoVisitor + From, C::Address: From, C::Signature: From, >::OtherParams: Default, + E::Balance: From, { /// Uploads contract code to a specified URL using a JSON-RPC call. /// @@ -160,9 +170,7 @@ where /// It constructs a [`CodeUploadRequest`] with the code and relevant parameters, /// 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> { + pub async fn upload_code_rpc(&self) -> Result> { let storage_deposit_limit = self .opts .storage_deposit_limit_balance(&self.token_metadata)?; @@ -196,7 +204,7 @@ where let result = submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; let display_events = - DisplayEvents::from_events(&result, None, &self.client.metadata())?; + DisplayEvents::from_events::(&result, None, &self.client.metadata())?; let code_stored = result.find_first::>()?; Ok(UploadResult:: { @@ -206,7 +214,7 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } From ea37a30d6d0a91577dbd384a23d82298a679b803 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 2 Feb 2024 17:02:34 +0100 Subject: [PATCH 11/39] Add fmt --- crates/cargo-contract/src/cmd/mod.rs | 5 ++++- crates/cargo-contract/src/cmd/storage.rs | 4 +--- crates/extrinsics/src/extrinsic_opts.rs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index d86722353..ba7a9222f 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -66,8 +66,11 @@ use contract_build::{ VerbosityFlags, DEFAULT_KEY_COL_WIDTH, }; -use contract_extrinsics::{pallet_contracts_primitives::ContractResult, BalanceVariant}; pub(crate) use contract_extrinsics::ErrorVariant; +use contract_extrinsics::{ + pallet_contracts_primitives::ContractResult, + BalanceVariant, +}; use core::fmt; use ink_env::{ DefaultEnvironment, diff --git a/crates/cargo-contract/src/cmd/storage.rs b/crates/cargo-contract/src/cmd/storage.rs index 7692601ed..73d198f7a 100644 --- a/crates/cargo-contract/src/cmd/storage.rs +++ b/crates/cargo-contract/src/cmd/storage.rs @@ -29,9 +29,7 @@ use contract_extrinsics::{ ErrorVariant, }; use ink_env::DefaultEnvironment; -use std::{ - path::PathBuf, -}; +use std::path::PathBuf; use subxt::Config; #[derive(Debug, clap::Args)] diff --git a/crates/extrinsics/src/extrinsic_opts.rs b/crates/extrinsics/src/extrinsic_opts.rs index 70b9ebd23..dcd1ced49 100644 --- a/crates/extrinsics/src/extrinsic_opts.rs +++ b/crates/extrinsics/src/extrinsic_opts.rs @@ -16,8 +16,8 @@ use core::marker::PhantomData; -use ink_env::Environment; use contract_build::Verbosity; +use ink_env::Environment; use subxt_signer::{ sr25519::Keypair, SecretUri, From ad7b2ce0c6a3081ba7da621f0107ac3795770835 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 2 Feb 2024 17:33:25 +0100 Subject: [PATCH 12/39] Fix tests --- crates/extrinsics/Cargo.toml | 1 - crates/extrinsics/src/balance.rs | 104 +++++++++++++++------ crates/extrinsics/src/integration_tests.rs | 43 +++++---- 3 files changed, 98 insertions(+), 50 deletions(-) diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index c814fa4dd..8a61845e1 100644 --- a/crates/extrinsics/Cargo.toml +++ b/crates/extrinsics/Cargo.toml @@ -52,6 +52,5 @@ tempfile = "3.9.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } [features] -default = ["integration-tests"] integration-tests = [] test-ci-only = [] diff --git a/crates/extrinsics/src/balance.rs b/crates/extrinsics/src/balance.rs index cf488a513..b3668e51c 100644 --- a/crates/extrinsics/src/balance.rs +++ b/crates/extrinsics/src/balance.rs @@ -383,24 +383,41 @@ impl Display for DenominatedBalance { #[cfg(test)] mod tests { + use ink_env::{ + DefaultEnvironment, + Environment, + }; + use super::*; #[test] fn correct_balances_parses_success() { assert!( - BalanceVariant::from_str("500DOT").is_ok(), + BalanceVariant::<::Balance>::from_str( + "500DOT" + ) + .is_ok(), "<500DOT> was not parsed correctly" ); assert!( - BalanceVariant::from_str("500").is_ok(), + BalanceVariant::<::Balance>::from_str( + "500" + ) + .is_ok(), "<500> was not parsed correctly" ); assert!( - BalanceVariant::from_str("1.0").is_err(), + BalanceVariant::<::Balance>::from_str( + "1.0" + ) + .is_err(), "<1.0> was not parsed correctly. Units must be provided" ); assert!( - BalanceVariant::from_str("1.0DOT").is_ok(), + BalanceVariant::<::Balance>::from_str( + "1.0DOT" + ) + .is_ok(), "<1.0DOt> was not parsed correctly" ); } @@ -408,7 +425,10 @@ mod tests { #[test] fn incorrect_balances() { assert!( - BalanceVariant::from_str("500%").is_err(), + BalanceVariant::<::Balance>::from_str( + "500%" + ) + .is_err(), "expected to fail parsing incorrect balance" ); } @@ -419,7 +439,11 @@ mod tests { token_decimals: 10, symbol: String::from("DOT"), }; - let bv = BalanceVariant::from_str("500MDOT").expect("successful parsing. qed"); + let bv = + BalanceVariant::<::Balance>::from_str( + "500MDOT", + ) + .expect("successful parsing. qed"); assert!( bv.denominate_balance(&tm).is_ok(), "balances could not be denominated correctly" @@ -433,8 +457,13 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 500 * 1_000_000 * 10_000_000_000; - let bv = BalanceVariant::from_str("500MDOT").expect("successful parsing. qed"); + let balance: ::Balance = + 500 * 1_000_000 * 10_000_000_000; + let bv = + BalanceVariant::<::Balance>::from_str( + "500MDOT", + ) + .expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); } @@ -446,7 +475,8 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 5_005_000_000_000_000_000; + let balance: ::Balance = + 5_005_000_000_000_000_000; let bv = BalanceVariant::from_str("500.5MDOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -459,7 +489,7 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 5_005_000; + let balance: ::Balance = 5_005_000; let bv = BalanceVariant::from_str("500.5μDOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -471,7 +501,7 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 1; + let balance: ::Balance = 1; let bv = BalanceVariant::from_str("0.1nDOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -487,7 +517,10 @@ mod tests { symbol: String::from("DOT"), }; let bv = - BalanceVariant::from_str("0.01546nDOT").expect("successful parsing. qed"); + BalanceVariant::<::Balance>::from_str( + "0.01546nDOT", + ) + .expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm); assert!(balance_parsed.is_err()) } @@ -499,7 +532,8 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 5_005_000_000_000_000_000_000; + let balance: ::Balance = + 5_005_000_000_000_000_000_000; let bv = BalanceVariant::from_str("500.5GDOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -512,7 +546,7 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 5_005_000_000_000_000; + let balance: ::Balance = 5_005_000_000_000_000; let bv = BalanceVariant::from_str("500.5kDOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -525,7 +559,7 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 5_005_000_000_000; + let balance: ::Balance = 5_005_000_000_000; let bv = BalanceVariant::from_str("500.5DOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -538,7 +572,7 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 5_005_000_000; + let balance: ::Balance = 5_005_000_000; let bv = BalanceVariant::from_str("500.5mDOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -550,7 +584,7 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 5_005_000; + let balance: ::Balance = 5_005_000; let bv = BalanceVariant::from_str("500.5μDOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -562,7 +596,7 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 5_005; + let balance: ::Balance = 5_005; let bv = BalanceVariant::from_str("500.5nDOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -575,7 +609,7 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 5_235_456_210_000_000; + let balance: ::Balance = 5_235_456_210_000_000; let bv = BalanceVariant::from_str("523.545621kDOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); @@ -589,7 +623,7 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let balance: Balance = 50_015_000_000_000; + let balance: ::Balance = 50_015_000_000_000; let bv = BalanceVariant::from_str("5001.5DOT").expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm).expect("successful parsing. qed"); assert_eq!(balance, balance_parsed); @@ -602,7 +636,11 @@ mod tests { token_decimals: decimals, symbol: String::from("DOT"), }; - let bv = BalanceVariant::from_str("0.4μDOT").expect("successful parsing. qed"); + let bv = + BalanceVariant::<::Balance>::from_str( + "0.4μDOT", + ) + .expect("successful parsing. qed"); let balance_parsed = bv.denominate_balance(&tm); assert!(balance_parsed.is_err()) } @@ -611,7 +649,8 @@ mod tests { fn big_input_to_denominate() { // max value of Decimal:MAX is 79_228_162_514_264_337_593_543_950_335 let s = "79_228_162_514_264_337_593_543_950_336DOT"; - let bv = BalanceVariant::from_str(s); + let bv = + BalanceVariant::<::Balance>::from_str(s); assert!(bv.is_err()) } @@ -619,7 +658,8 @@ mod tests { fn big_input_to_raw() { // max value of Decimal:MAX is 79_228_162_514_264_337_593_543_950_335 let s = "79_228_162_514_264_337_593_543_950_336"; - let bv = BalanceVariant::from_str(s); + let bv = + BalanceVariant::<::Balance>::from_str(s); assert!(bv.is_ok()) } @@ -631,8 +671,10 @@ mod tests { symbol: String::from("DOT"), }; let balance = 532_500_000_000_u128; - let denominated_balance = - BalanceVariant::from(balance, Some(&tm)).expect("successful conversion"); + let denominated_balance = BalanceVariant::< + ::Balance, + >::from(balance, Some(&tm)) + .expect("successful conversion"); let sample = BalanceVariant::Denominated(DenominatedBalance { value: Decimal::new(5325, 1), unit: UnitPrefix::Kilo, @@ -649,8 +691,10 @@ mod tests { symbol: String::from("DOT"), }; let balance = 532_500_000_000_u128; - let denominated_balance = - BalanceVariant::from(balance, Some(&tm)).expect("successful conversion"); + let denominated_balance = BalanceVariant::< + ::Balance, + >::from(balance, Some(&tm)) + .expect("successful conversion"); let sample = BalanceVariant::Denominated(DenominatedBalance { value: Decimal::new(5325, 2), unit: UnitPrefix::One, @@ -671,8 +715,10 @@ mod tests { // 10_000 - Micro // 532_500 - 52.25 Micro let balance = 532_500_u128; - let denominated_balance = - BalanceVariant::from(balance, Some(&tm)).expect("successful conversion"); + let denominated_balance = BalanceVariant::< + ::Balance, + >::from(balance, Some(&tm)) + .expect("successful conversion"); let sample = BalanceVariant::Denominated(DenominatedBalance { value: Decimal::new(5325, 2), unit: UnitPrefix::Micro, diff --git a/crates/extrinsics/src/integration_tests.rs b/crates/extrinsics/src/integration_tests.rs index bcca23161..05830c8e1 100644 --- a/crates/extrinsics/src/integration_tests.rs +++ b/crates/extrinsics/src/integration_tests.rs @@ -470,11 +470,12 @@ async fn api_build_upload_instantiate_call() { .file(Some(contract_file)) .suri("//Alice") .done(); - let upload: UploadExec = UploadCommandBuilder::default() - .extrinsic_opts(opts.clone()) - .done() - .await - .unwrap(); + let upload: UploadExec = + UploadCommandBuilder::default() + .extrinsic_opts(opts.clone()) + .done() + .await + .unwrap(); let upload_result = upload.upload_code().await; assert!(upload_result.is_ok(), "upload code failed"); upload_result.unwrap(); @@ -496,7 +497,7 @@ async fn api_build_upload_instantiate_call() { // call the contract // the value should be true - let call = CallCommandBuilder::default() + let call = CallCommandBuilder::::default() .extrinsic_opts(opts.clone()) .message("get") .contract(instantiate_result.contract_address.clone()) @@ -523,7 +524,7 @@ async fn api_build_upload_instantiate_call() { // call the contract // flip the value - let call = CallCommandBuilder::default() + let call = CallCommandBuilder::::default() .extrinsic_opts(opts.clone()) .message("flip") .contract(instantiate_result.contract_address.clone()) @@ -538,7 +539,7 @@ async fn api_build_upload_instantiate_call() { // call the contract // make sure the value has been flipped - let call = CallCommandBuilder::default() + let call = CallCommandBuilder::::default() .extrinsic_opts(opts.clone()) .message("get") .contract(instantiate_result.contract_address.clone()) @@ -597,11 +598,12 @@ async fn api_build_upload_remove() { .file(Some(contract_file)) .suri("//Alice") .done(); - let upload: UploadExec = UploadCommandBuilder::default() - .extrinsic_opts(opts.clone()) - .done() - .await - .unwrap(); + let upload: UploadExec = + UploadCommandBuilder::default() + .extrinsic_opts(opts.clone()) + .done() + .await + .unwrap(); let upload_result = upload.upload_code().await; assert!(upload_result.is_ok(), "upload code failed"); let upload_result = upload_result.unwrap(); @@ -610,13 +612,14 @@ 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()) - .code_hash(Some(code_hash_h256)) - .done() - .await - .unwrap(); - let remove_result = remove.remove_code::().await; + let remove: RemoveExec = + RemoveCommandBuilder::default() + .extrinsic_opts(opts.clone()) + .code_hash(Some(code_hash_h256)) + .done() + .await + .unwrap(); + let remove_result = remove.remove_code().await; assert!(remove_result.is_ok(), "remove code failed"); remove_result.unwrap(); From 6adba9b2013a9c0b348b090afa1adfc9ee9c9471 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 2 Feb 2024 20:17:00 +0100 Subject: [PATCH 13/39] Fix tests --- crates/extrinsics/src/balance.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/extrinsics/src/balance.rs b/crates/extrinsics/src/balance.rs index b3668e51c..96e9892f6 100644 --- a/crates/extrinsics/src/balance.rs +++ b/crates/extrinsics/src/balance.rs @@ -178,7 +178,7 @@ where /// token_decimals: decimals, /// symbol: String::from("DOT"), /// }; - /// let sample_den_balance: BalanceVariant = "0.4\u{3bc}DOT".parse().unwrap(); + /// let sample_den_balance: BalanceVariant = "0.4\u{3bc}DOT".parse().unwrap(); /// let result = sample_den_balance.denominate_balance(&tm); /// assert!(result.is_err()); /// ``` @@ -194,7 +194,7 @@ where /// token_decimals: decimals, /// symbol: String::from("DOT"), /// }; - /// let sample_den_balance: BalanceVariant = "4123\u{3bc}DOT".parse().unwrap(); + /// let sample_den_balance: BalanceVariant = "4123\u{3bc}DOT".parse().unwrap(); /// let balance = 4123; /// let result = sample_den_balance.denominate_balance(&tm).unwrap(); /// assert_eq!(balance, result); @@ -250,7 +250,6 @@ where /// # Examples /// ```rust /// use contract_extrinsics::{ - /// Balance, /// BalanceVariant, /// TokenMetadata, /// }; @@ -259,8 +258,8 @@ where /// token_decimals: decimals, /// symbol: String::from("DOT"), /// }; - /// let sample_den_balance: BalanceVariant = "500.5MDOT".parse().unwrap(); - /// let balance: Balance = 5_005_000_000_000_000_000; + /// let sample_den_balance: BalanceVariant = "500.5MDOT".parse().unwrap(); + /// let balance: u128 = 5_005_000_000_000_000_000; /// let den_balance = BalanceVariant::from(balance, Some(&tm)).unwrap(); /// assert_eq!(sample_den_balance, den_balance); /// ``` From 54f48f703212448a165c162714a100b6c31c8ce2 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Mon, 5 Feb 2024 13:49:22 +0100 Subject: [PATCH 14/39] Code cleanup --- crates/cargo-contract/src/cmd/call.rs | 6 +- crates/cargo-contract/src/cmd/mod.rs | 30 +++++++++ crates/cargo-contract/src/cmd/remove.rs | 12 ++-- crates/extrinsics/src/call.rs | 5 +- crates/extrinsics/src/events.rs | 1 - crates/extrinsics/src/extrinsic_calls.rs | 24 ++++--- crates/extrinsics/src/instantiate.rs | 2 +- crates/extrinsics/src/lib.rs | 79 +++++++++--------------- 8 files changed, 83 insertions(+), 76 deletions(-) diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index c99561db1..0c83b9e0c 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -44,12 +44,14 @@ use contract_extrinsics::{ BalanceVariant, CallCommandBuilder, CallExec, - DefaultConfig, ExtrinsicOptsBuilder, }; use contract_transcode::Value; use sp_weights::Weight; -use subxt::Config; +use subxt::{ + Config, + PolkadotConfig as DefaultConfig, +}; #[derive(Debug, clap::Args)] #[clap(name = "call", about = "Call a contract")] diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index ba7a9222f..06724af6c 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -270,3 +270,33 @@ pub fn display_all_contracts(contracts: &[::AccountId]) .iter() .for_each(|e: &::AccountId| println!("{}", e)) } + +/// 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)?; + if bytes.len() != 32 { + anyhow::bail!("Code hash should be 32 bytes in length") + } + let mut arr = [0u8; 32]; + arr.copy_from_slice(&bytes); + Ok(arr.into()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_code_hash_works() { + // with 0x prefix + assert!(parse_code_hash( + "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" + ) + .is_ok()); + // without 0x prefix + assert!(parse_code_hash( + "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" + ) + .is_ok()) + } +} diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index 1a190a4a3..28480017b 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -17,18 +17,22 @@ use crate::ErrorVariant; use std::fmt::Debug; -use super::CLIExtrinsicOpts; +use super::{ + parse_code_hash, + CLIExtrinsicOpts, +}; use anyhow::Result; use contract_build::name_value_println; use contract_extrinsics::{ - parse_code_hash, - DefaultConfig, ExtrinsicOptsBuilder, RemoveCommandBuilder, RemoveExec, }; use ink_env::DefaultEnvironment; -use subxt::Config; +use subxt::{ + Config, + PolkadotConfig as DefaultConfig, +}; #[derive(Debug, clap::Args)] #[clap(name = "remove", about = "Remove a contract's code")] diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index d78fb6906..e873011f1 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -293,10 +293,7 @@ where /// /// Returns the events generated from the contract call, or an error in case of /// failure. - pub async fn call( - &self, - gas_limit: Option, - ) -> Result { + pub async fn call(&self, gas_limit: Option) -> Result { if !self .transcoder() .metadata() diff --git a/crates/extrinsics/src/events.rs b/crates/extrinsics/src/events.rs index 7beede2df..ce6e381b0 100644 --- a/crates/extrinsics/src/events.rs +++ b/crates/extrinsics/src/events.rs @@ -195,7 +195,6 @@ impl DisplayEvents { ) -> Result where C::AccountId: IntoVisitor, - // E::Balance: /*From +*/, { let mut events: Vec = vec![]; diff --git a/crates/extrinsics/src/extrinsic_calls.rs b/crates/extrinsics/src/extrinsic_calls.rs index d1450e859..370ace598 100644 --- a/crates/extrinsics/src/extrinsic_calls.rs +++ b/crates/extrinsics/src/extrinsic_calls.rs @@ -18,14 +18,12 @@ use crate::{ upload::Determinism, WasmCode, }; -use ink_env::Environment; use subxt::{ ext::{ codec::Compact, scale_encode::EncodeAsType, }, utils::MultiAddress, - Config, }; /// Copied from `sp_weight` to additionally implement `scale_encode::EncodeAsType`. @@ -142,29 +140,29 @@ impl InstantiateWithCode { /// A raw call to `pallet-contracts`'s `instantiate_with_code_hash`. #[derive(Debug, EncodeAsType)] -#[encode_as_type(trait_bounds = "", crate_path = "subxt::ext::scale_encode")] -pub(crate) struct Instantiate +#[encode_as_type(crate_path = "subxt::ext::scale_encode")] +pub(crate) struct Instantiate where - C::Hash: EncodeAsType, + Hash: EncodeAsType, { #[codec(compact)] - value: E::Balance, + value: Balance, gas_limit: Weight, - storage_deposit_limit: Option>, - code_hash: C::Hash, + storage_deposit_limit: Option>, + code_hash: Hash, data: Vec, salt: Vec, } -impl Instantiate +impl Instantiate where - C::Hash: EncodeAsType, + Hash: EncodeAsType, { pub fn new( - value: E::Balance, + value: Balance, gas_limit: sp_weights::Weight, - storage_deposit_limit: Option, - code_hash: C::Hash, + storage_deposit_limit: Option, + code_hash: Hash, data: Vec, salt: Vec, ) -> Self { diff --git a/crates/extrinsics/src/instantiate.rs b/crates/extrinsics/src/instantiate.rs index be4decb51..a13b28644 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -429,7 +429,7 @@ where code_hash: C::Hash, gas_limit: Weight, ) -> Result, ErrorVariant> { - let call = Instantiate::::new( + let call = Instantiate::::new( self.args.value, gas_limit, self.args.storage_deposit_limit, diff --git a/crates/extrinsics/src/lib.rs b/crates/extrinsics/src/lib.rs index 9c4c750a4..b4c527844 100644 --- a/crates/extrinsics/src/lib.rs +++ b/crates/extrinsics/src/lib.rs @@ -107,7 +107,6 @@ pub use remove::{ RemoveResult, }; -pub use subxt::PolkadotConfig as DefaultConfig; pub use upload::{ UploadCommandBuilder, UploadExec, @@ -119,9 +118,6 @@ pub use rpc::{ RpcRequest, }; -pub type Client = OnlineClient; -pub type CodeHash = ::Hash; - /// The Wasm code of a contract. #[derive(Debug, Clone)] pub struct WasmCode(Vec); @@ -156,20 +152,20 @@ where /// /// Currently this will report success once the transaction is included in a block. In the /// future there could be a flag to wait for finality before reporting success. -async fn submit_extrinsic( - client: &OnlineClient, - rpc: &LegacyRpcMethods, +async fn submit_extrinsic( + client: &OnlineClient, + rpc: &LegacyRpcMethods, call: &Call, signer: &Signer, -) -> core::result::Result, subxt::Error> +) -> core::result::Result, subxt::Error> where - T: Config, + C: Config, Call: tx::TxPayload, - Signer: tx::Signer, - T::Signature: From, - >::OtherParams: Default, - T::Address: From, - T::AccountId: From, + 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?; @@ -215,13 +211,13 @@ where } /// Return the account nonce at the *best* block for an account ID. -async fn get_account_nonce( - client: &OnlineClient, - rpc: &LegacyRpcMethods, - account_id: &T::AccountId, +async fn get_account_nonce( + client: &OnlineClient, + rpc: &LegacyRpcMethods, + account_id: &C::AccountId, ) -> core::result::Result where - T: Config, + C: Config, { let best_block = rpc .chain_get_block_hash(None) @@ -236,43 +232,38 @@ where Ok(account_nonce) } -async fn state_call( +async fn state_call( rpc: &LegacyRpcMethods, func: &str, args: A, -) -> Result { +) -> Result +where + C: Config, +{ let params = args.encode(); let bytes = rpc.state_call(func, Some(¶ms), None).await?; Ok(R::decode(&mut bytes.as_ref())?) } -/// 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)?; - if bytes.len() != 32 { - anyhow::bail!("Code hash should be 32 bytes in length") - } - let mut arr = [0u8; 32]; - arr.copy_from_slice(&bytes); - Ok(arr.into()) -} - /// Fetch the hash of the *best* block (included but not guaranteed to be finalized). -async fn get_best_block( +async fn get_best_block( rpc: &LegacyRpcMethods, -) -> core::result::Result { +) -> core::result::Result +where + C: Config, +{ rpc.chain_get_block_hash(None) .await? .ok_or(subxt::Error::Other("Best block not found".into())) } -fn check_env_types( - client: &OnlineClient, +fn check_env_types( + client: &OnlineClient, transcoder: &ContractMessageTranscoder, verbosity: &Verbosity, ) -> Result<()> where - T: Config, + C: Config, { compare_node_env_with_contract( client.metadata().types(), @@ -300,20 +291,6 @@ pub fn url_to_string(url: &url::Url) -> String { mod tests { use super::*; - #[test] - fn parse_code_hash_works() { - // with 0x prefix - assert!(parse_code_hash( - "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" - ) - .is_ok()); - // without 0x prefix - assert!(parse_code_hash( - "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" - ) - .is_ok()) - } - #[test] fn url_to_string_works() { // with custom port From 5b52af0075b86fa3e4c8e5d94f31b2256c4834e7 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Mon, 5 Feb 2024 15:55:10 +0100 Subject: [PATCH 15/39] fix test --- crates/extrinsics/src/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index e873011f1..b124c29f5 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -293,7 +293,7 @@ where /// /// Returns the events generated from the contract call, or an error in case of /// failure. - pub async fn call(&self, gas_limit: Option) -> Result { + pub async fn call(&self, gas_limit: Option) -> Result { if !self .transcoder() .metadata() From a063def102c4eadc7d74ec3ea5c2df39fe38bad0 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Mon, 5 Feb 2024 16:09:36 +0100 Subject: [PATCH 16/39] Cleanup --- crates/extrinsics/src/call.rs | 13 ++++++++----- crates/extrinsics/src/remove.rs | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index b124c29f5..40bdc5eb8 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -88,7 +88,7 @@ where } } -impl CallCommandBuilder, T> +impl CallCommandBuilder, X> where E::Balance: FromStr + From, { @@ -111,10 +111,10 @@ where } /// Sets the name of the contract message to call. - pub fn message>( + pub fn message>( self, - message: M, - ) -> CallCommandBuilder { + message: T, + ) -> CallCommandBuilder { CallCommandBuilder { opts: CallOpts { message: message.into(), @@ -293,7 +293,10 @@ where /// /// Returns the events generated from the contract call, or an error in case of /// failure. - pub async fn call(&self, gas_limit: Option) -> Result { + pub async fn call( + &self, + gas_limit: Option, + ) -> Result { if !self .transcoder() .metadata() diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index f2a5f6580..365fea208 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -101,7 +101,7 @@ where } } -impl RemoveCommandBuilder { +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; From 425e40d299d617613afd084a2cead77b8525b0db Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Mon, 5 Feb 2024 17:48:40 +0100 Subject: [PATCH 17/39] Cleanup --- crates/extrinsics/src/call.rs | 5 +---- crates/extrinsics/src/instantiate.rs | 2 +- crates/extrinsics/src/remove.rs | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index 40bdc5eb8..c038bd236 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -234,10 +234,7 @@ where } } -pub struct CallExec -where - E::Balance: From, -{ +pub struct CallExec { contract: C::AccountId, message: String, args: Vec, diff --git a/crates/extrinsics/src/instantiate.rs b/crates/extrinsics/src/instantiate.rs index a13b28644..89fc4c571 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -139,7 +139,7 @@ where } } -impl InstantiateCommandBuilder { +impl InstantiateCommandBuilder { /// Sets the name of the contract constructor to call. pub fn constructor>(self, constructor: T) -> Self { let mut this = self; diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index 365fea208..edc1380c9 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -179,7 +179,6 @@ where C::Address: From, C::Signature: From, >::OtherParams: Default, - // E::Balance: From { /// Removes a contract code from the blockchain. /// From 5e689f87f69abfe7ddcd014f74f246ec329da072 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 6 Feb 2024 15:35:57 +0100 Subject: [PATCH 18/39] Update crates/extrinsics/src/remove.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Müller --- crates/extrinsics/src/remove.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index edc1380c9..cbb90537a 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -134,7 +134,7 @@ where let final_code_hash = match (self.opts.code_hash.as_ref(), artifacts.code.as_ref()) { (Some(code_h), _) => Ok(*code_h), - (None, Some(_)) => artifacts.code_hash().map(|h| h.into() ), + (None, Some(_)) => artifacts.code_hash().map(Into::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 \ From d85fd249fa3d7f046af7e7fd07f7c7a6210f66b8 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 6 Feb 2024 15:54:59 +0100 Subject: [PATCH 19/39] Remove BalanceVariant from extrinsics --- crates/cargo-contract/src/cmd/call.rs | 27 +++++++-- crates/cargo-contract/src/cmd/instantiate.rs | 24 +++++--- crates/cargo-contract/src/cmd/remove.rs | 23 +++++++- crates/cargo-contract/src/cmd/upload.rs | 23 ++++++-- crates/extrinsics/src/balance.rs | 14 ++++- crates/extrinsics/src/call.rs | 53 +++++------------- crates/extrinsics/src/events.rs | 4 ++ crates/extrinsics/src/extrinsic_opts.rs | 47 +++------------- crates/extrinsics/src/instantiate.rs | 48 ++++++---------- crates/extrinsics/src/remove.rs | 44 ++++----------- crates/extrinsics/src/upload.rs | 58 ++++++-------------- 11 files changed, 161 insertions(+), 204 deletions(-) diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index 0c83b9e0c..4626c891e 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -44,7 +44,9 @@ use contract_extrinsics::{ BalanceVariant, CallCommandBuilder, CallExec, + DisplayEvents, ExtrinsicOptsBuilder, + TokenMetadata, }; use contract_transcode::Value; use sp_weights::Weight; @@ -92,12 +94,22 @@ impl CallCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { + let token_metadata = + TokenMetadata::query_url::(&self.extrinsic_cli_opts.url) + .await?; + let extrinsic_opts = ExtrinsicOptsBuilder::default() .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() @@ -107,9 +119,10 @@ impl CallCommand { .extrinsic_opts(extrinsic_opts) .gas_limit(self.gas_limit) .proof_size(self.proof_size) - .value(self.value.clone()) + .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 +156,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 +191,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}"); diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index 82ad40c90..33bd85793 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -47,6 +47,7 @@ use contract_extrinsics::{ InstantiateCommandBuilder, InstantiateDryRunResult, InstantiateExecResult, + TokenMetadata, }; use ink_env::{ DefaultEnvironment, @@ -100,19 +101,29 @@ impl InstantiateCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { + let token_metadata = + TokenMetadata::query_url::(&self.extrinsic_cli_opts.url) + .await?; + let extrinsic_opts = ExtrinsicOptsBuilder::default() .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()) + .value(self.value.denominate_balance(&token_metadata)?) .gas_limit(self.gas_limit) .proof_size(self.proof_size) .salt(self.salt.clone()) @@ -171,6 +182,7 @@ impl InstantiateCommand { display_result( &instantiate_exec, instantiate_result, + &token_metadata, self.output_json(), self.extrinsic_cli_opts.verbosity().unwrap(), ) @@ -240,11 +252,12 @@ async fn pre_submit_dry_run_gas_estimate_instantiate( pub async fn display_result( 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 +274,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:?}")); diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index 28480017b..80f4eb92c 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -24,9 +24,11 @@ use super::{ use anyhow::Result; use contract_build::name_value_println; use contract_extrinsics::{ + DisplayEvents, ExtrinsicOptsBuilder, RemoveCommandBuilder, RemoveExec, + TokenMetadata, }; use ink_env::DefaultEnvironment; use subxt::{ @@ -54,12 +56,22 @@ impl RemoveCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { + let token_metadata = + TokenMetadata::query_url::(&self.extrinsic_cli_opts.url) + .await?; + let extrinsic_opts = ExtrinsicOptsBuilder::default() .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() @@ -68,13 +80,18 @@ impl RemoveCommand { .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..6798fae2c 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -24,7 +24,9 @@ use super::{ use anyhow::Result; use contract_build::name_value_println; use contract_extrinsics::{ + DisplayEvents, ExtrinsicOptsBuilder, + TokenMetadata, UploadCommandBuilder, UploadExec, }; @@ -54,12 +56,22 @@ impl UploadCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { + let token_metadata = + TokenMetadata::query_url::(&self.extrinsic_cli_opts.url) + .await?; + let extrinsic_opts = ExtrinsicOptsBuilder::default() .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() @@ -68,6 +80,7 @@ impl UploadCommand { .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 +98,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 +108,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/src/balance.rs b/crates/extrinsics/src/balance.rs index 96e9892f6..ca1d3277f 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,6 +38,9 @@ use anyhow::{ Context, Result, }; +use url::Url; + +use crate::url_to_string; /// Represents different formats of a balance #[derive(Debug, Clone, PartialEq, Eq)] @@ -72,6 +78,12 @@ pub enum UnitPrefix { } impl TokenMetadata { + pub async fn query_url(url: &Url) -> Result { + let rpc_cli = RpcClient::from_url(url_to_string(url)).await?; + let rpc = LegacyRpcMethods::new(rpc_cli.clone()); + Self::query::(&rpc).await + } + /// Query [TokenMetadata] through the node's RPC pub async fn query(client: &LegacyRpcMethods) -> Result { let sys_props = client.system_properties().await?; diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index c038bd236..3b7a9daa9 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -16,16 +16,13 @@ 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, @@ -43,12 +40,12 @@ 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, @@ -64,7 +61,7 @@ pub struct CallOpts { extrinsic_opts: ExtrinsicOpts, gas_limit: Option, proof_size: Option, - value: BalanceVariant, + value: E::Balance, } /// A builder for the call command. @@ -81,7 +78,7 @@ impl Default Missing, > where - E::Balance: FromStr + From, + E::Balance: Default, { fn default() -> Self { Self::new() @@ -90,7 +87,7 @@ where impl CallCommandBuilder, X> where - E::Balance: FromStr + From, + E::Balance: Default, { /// Returns a clean builder for [`CallExec`]. pub fn new( @@ -104,7 +101,7 @@ where extrinsic_opts: ExtrinsicOpts::default(), gas_limit: None, proof_size: None, - value: "0".parse().unwrap(), + value: Default::default(), }, marker: PhantomData, } @@ -173,7 +170,7 @@ impl CallCommandBuilder { } /// 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 @@ -184,7 +181,6 @@ impl CallCommandBuilder where C::AccountId: IntoVisitor, - E::Balance: From, { /// Preprocesses contract artifacts and options for subsequent contract calls. /// @@ -210,7 +206,6 @@ where 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 @@ -223,13 +218,12 @@ where opts: self.opts.extrinsic_opts.clone(), gas_limit: self.opts.gas_limit, proof_size: self.opts.proof_size, - value: self.opts.value.clone(), + value: self.opts.value, rpc, client, transcoder, call_data, signer, - token_metadata, }) } } @@ -241,13 +235,12 @@ pub struct CallExec { 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 @@ -256,7 +249,6 @@ where >::OtherParams: Default, C::Address: From, C::AccountId: From + EncodeAsType + IntoVisitor, - E::Balance: From, { /// Simulates a contract call without modifying the blockchain. /// @@ -268,13 +260,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), 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 +283,7 @@ where pub async fn call( &self, gas_limit: Option, - ) -> Result { + ) -> Result, ErrorVariant> { if !self .transcoder() .metadata() @@ -317,13 +307,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(), @@ -333,13 +321,7 @@ where 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(), - )?; - - Ok(display_events) + Ok(result) } /// Estimates the gas required for a contract call without modifying the blockchain. @@ -412,7 +394,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 } @@ -435,11 +417,6 @@ where 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..ae0ecc85c 100644 --- a/crates/extrinsics/src/extrinsic_opts.rs +++ b/crates/extrinsics/src/extrinsic_opts.rs @@ -31,9 +31,7 @@ use anyhow::{ use crate::{ url_to_string, - BalanceVariant, ContractArtifacts, - TokenMetadata, }; use std::{ option::Option, @@ -47,7 +45,7 @@ pub struct ExtrinsicOpts { manifest_path: Option, url: url::Url, suri: String, - storage_deposit_limit: Option>, + storage_deposit_limit: Option, verbosity: Verbosity, } @@ -73,10 +71,7 @@ pub struct ExtrinsicOptsBuilder { marker: PhantomData Suri>, } -impl ExtrinsicOptsBuilder> -where - E::Balance: From, -{ +impl ExtrinsicOptsBuilder> { /// Returns a clean builder for `ExtrinsicOpts`. pub fn new() -> ExtrinsicOptsBuilder> { ExtrinsicOptsBuilder { @@ -97,10 +92,7 @@ where } } -impl Default for ExtrinsicOptsBuilder> -where - E::Balance: From, -{ +impl Default for ExtrinsicOptsBuilder> { fn default() -> Self { Self::new() } @@ -132,7 +124,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; @@ -147,10 +139,7 @@ impl ExtrinsicOptsBuilder { } } -impl ExtrinsicOptsBuilder -where - E::Balance: From, -{ +impl ExtrinsicOptsBuilder { /// Finishes construction of the extrinsic options. pub fn done(self) -> ExtrinsicOpts { self.opts @@ -158,10 +147,7 @@ where } #[allow(clippy::new_ret_no_self)] -impl ExtrinsicOpts -where - E::Balance: From, -{ +impl ExtrinsicOpts { /// Returns a clean builder for [`ExtrinsicOpts`]. pub fn new() -> ExtrinsicOptsBuilder> { ExtrinsicOptsBuilder { @@ -213,20 +199,8 @@ where } /// 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. @@ -235,10 +209,7 @@ where } } -impl Default for ExtrinsicOpts -where - E::Balance: From, -{ +impl Default for ExtrinsicOpts { 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..0a0362a01 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -27,11 +27,9 @@ use super::{ state, state_call, submit_extrinsic, - BalanceVariant, ContractMessageTranscoder, ErrorVariant, Missing, - TokenMetadata, }; use crate::{ check_env_types, @@ -58,10 +56,7 @@ use scale::{ }; use sp_core::Bytes; use sp_weights::Weight; -use std::{ - fmt::Display, - str::FromStr, -}; +use std::fmt::Display; use subxt::{ backend::{ legacy::LegacyRpcMethods, @@ -81,7 +76,7 @@ struct InstantiateOpts { constructor: String, args: Vec, extrinsic_opts: ExtrinsicOpts, - value: BalanceVariant, + value: E::Balance, gas_limit: Option, proof_size: Option, salt: Option, @@ -96,7 +91,7 @@ pub struct InstantiateCommandBuilder InstantiateCommandBuilder> where - E::Balance: From + FromStr, + E::Balance: Default, { /// Returns a clean builder for [`InstantiateExec`]. pub fn new() -> InstantiateCommandBuilder> { @@ -105,7 +100,7 @@ where constructor: String::from("new"), args: Vec::new(), extrinsic_opts: ExtrinsicOpts::default(), - value: "0".parse().unwrap(), + value: Default::default(), gas_limit: None, proof_size: None, salt: None, @@ -132,7 +127,7 @@ where impl Default for InstantiateCommandBuilder> where - E::Balance: From + FromStr, + E::Balance: Default, { fn default() -> Self { Self::new() @@ -155,7 +150,7 @@ impl InstantiateCommandBuilder { } /// 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 @@ -186,7 +181,6 @@ impl InstantiateCommandBuilder { impl InstantiateCommandBuilder where C::Hash: From<[u8; 32]>, - E::Balance: From, { /// Preprocesses contract artifacts and options for instantiation. /// @@ -216,18 +210,13 @@ where check_env_types(&client, &transcoder, self.opts.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)?, + value: self.opts.value, 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)?, + storage_deposit_limit: self.opts.extrinsic_opts.storage_deposit_limit(), code, data, salt, @@ -241,7 +230,6 @@ where client, signer, transcoder, - token_metadata, }) } } @@ -312,7 +300,6 @@ pub struct InstantiateExec { client: OnlineClient, signer: Keypair, transcoder: ContractMessageTranscoder, - token_metadata: TokenMetadata, } impl InstantiateExec @@ -403,24 +390,23 @@ where ) .build(); - let result = + let events = submit_extrinsic(&self.client, &self.rpc, &call, &self.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 +425,17 @@ where ) .build(); - let result = + let events = submit_extrinsic(&self.client, &self.rpc, &call, &self.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(), }) } @@ -552,10 +537,9 @@ where } 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/remove.rs b/crates/extrinsics/src/remove.rs index cbb90537a..d3a0e9385 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -15,16 +15,12 @@ // along with cargo-contract. If not, see . use super::{ - events::{ - CodeRemoved, - DisplayEvents, - }, + events::CodeRemoved, state, submit_extrinsic, ContractMessageTranscoder, ErrorVariant, Missing, - TokenMetadata, }; use crate::{ extrinsic_calls::RemoveCode, @@ -39,6 +35,7 @@ use subxt::{ legacy::LegacyRpcMethods, rpc::RpcClient, }, + blocks::ExtrinsicEvents, config, ext::{ scale_decode::IntoVisitor, @@ -62,8 +59,6 @@ pub struct RemoveCommandBuilder { impl RemoveCommandBuilder> -where - E::Balance: From, { /// Returns a clean builder for [`RemoveExec`]. pub fn new() -> RemoveCommandBuilder> { @@ -93,8 +88,6 @@ where impl Default for RemoveCommandBuilder> -where - E::Balance: From, { fn default() -> Self { Self::new() @@ -113,7 +106,6 @@ impl RemoveCommandBuilder { impl RemoveCommandBuilder where C::Hash: From<[u8; 32]>, - E::Balance: From, { /// Preprocesses contract artifacts and options for subsequent removal of contract /// code. @@ -134,7 +126,7 @@ where let final_code_hash = match (self.opts.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 \ @@ -148,8 +140,6 @@ where 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(), @@ -157,7 +147,6 @@ where client, transcoder, signer, - token_metadata, }) } } @@ -169,7 +158,6 @@ pub struct RemoveExec { client: OnlineClient, transcoder: ContractMessageTranscoder, signer: Keypair, - token_metadata: TokenMetadata, } impl RemoveExec @@ -189,9 +177,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 +185,14 @@ where let call = RemoveCode::new(code_hash).build(); - let result = + let events = submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; - let display_events = DisplayEvents::from_events::( - &result, - Some(&self.transcoder), - &self.client.metadata(), - )?; let code_removed = - result.find_first::>()?; + events.find_first::>()?; Ok(RemoveResult { code_removed, - display_events, + events, }) } @@ -239,14 +220,9 @@ where 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, +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..31dfd40c3 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -16,17 +16,13 @@ use super::{ account_id, - events::{ - CodeStored, - DisplayEvents, - }, + events::CodeStored, pallet_contracts_primitives::CodeUploadResult, state, state_call, submit_extrinsic, ErrorVariant, Missing, - TokenMetadata, WasmCode, }; use crate::{ @@ -44,6 +40,7 @@ use subxt::{ legacy::LegacyRpcMethods, rpc::RpcClient, }, + blocks::ExtrinsicEvents, config, ext::{ scale_decode::IntoVisitor, @@ -64,10 +61,7 @@ pub struct UploadCommandBuilder { marker: PhantomData ExtrinsicOptions>, } -impl UploadCommandBuilder> -where - E::Balance: From, -{ +impl UploadCommandBuilder> { /// Returns a clean builder for [`UploadExec`]. pub fn new() -> UploadCommandBuilder> { UploadCommandBuilder { @@ -90,19 +84,15 @@ where } } -impl Default for UploadCommandBuilder> -where - E::Balance: From, +impl Default + for UploadCommandBuilder> { fn default() -> Self { Self::new() } } -impl UploadCommandBuilder -where - E::Balance: From, -{ +impl UploadCommandBuilder { /// Preprocesses contract artifacts and options for subsequent upload. /// /// This function prepares the necessary data for uploading a contract @@ -131,15 +121,12 @@ where check_env_types(&client, &transcoder, self.opts.extrinsic_opts.verbosity())?; let rpc = LegacyRpcMethods::new(rpc_cli); - let token_metadata = TokenMetadata::query(&rpc).await?; - Ok(UploadExec { opts: self.opts.extrinsic_opts.clone(), rpc, client, code, signer, - token_metadata, transcoder, }) } @@ -151,7 +138,6 @@ pub struct UploadExec { client: OnlineClient, code: WasmCode, signer: Keypair, - token_metadata: TokenMetadata, transcoder: ContractMessageTranscoder, } @@ -162,7 +148,6 @@ where C::Address: From, C::Signature: From, >::OtherParams: Default, - E::Balance: From, { /// Uploads contract code to a specified URL using a JSON-RPC call. /// @@ -171,9 +156,7 @@ 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), code: self.code.0.clone(), @@ -189,10 +172,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,15 +182,13 @@ where ) .build(); - let result = + let events = submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; - let display_events = - DisplayEvents::from_events::(&result, None, &self.client.metadata())?; - let code_stored = result.find_first::>()?; - Ok(UploadResult:: { + let code_stored = events.find_first::>()?; + Ok(UploadResult { code_stored, - display_events, + events, }) } @@ -233,11 +212,6 @@ where &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 +227,9 @@ struct CodeUploadRequest { determinism: Determinism, } -pub struct UploadResult { - pub code_stored: Option>, - pub display_events: DisplayEvents, +pub struct UploadResult { + pub code_stored: Option>, + pub events: ExtrinsicEvents, } /// Copied from `pallet-contracts` to additionally implement `scale_encode::EncodeAsType`. From 916a2ddde38a19903850165869b08834ac1b4b1e Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 6 Feb 2024 17:29:47 +0100 Subject: [PATCH 20/39] Test fixed --- crates/cargo-contract/src/cmd/call.rs | 3 +-- crates/cargo-contract/src/cmd/instantiate.rs | 3 +-- crates/cargo-contract/src/cmd/remove.rs | 3 +-- crates/cargo-contract/src/cmd/upload.rs | 3 +-- crates/extrinsics/src/balance.rs | 16 ++++++---------- crates/extrinsics/src/integration_tests.rs | 10 +++++++++- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index 4626c891e..5b90d62d0 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -95,8 +95,7 @@ impl CallCommand { pub async fn handle(&self) -> Result<(), ErrorVariant> { let token_metadata = - TokenMetadata::query_url::(&self.extrinsic_cli_opts.url) - .await?; + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; let extrinsic_opts = ExtrinsicOptsBuilder::default() .file(self.extrinsic_cli_opts.file.clone()) diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index 33bd85793..32e4e4f20 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -102,8 +102,7 @@ impl InstantiateCommand { pub async fn handle(&self) -> Result<(), ErrorVariant> { let token_metadata = - TokenMetadata::query_url::(&self.extrinsic_cli_opts.url) - .await?; + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; let extrinsic_opts = ExtrinsicOptsBuilder::default() .file(self.extrinsic_cli_opts.file.clone()) diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index 80f4eb92c..a09de8273 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -57,8 +57,7 @@ impl RemoveCommand { pub async fn handle(&self) -> Result<(), ErrorVariant> { let token_metadata = - TokenMetadata::query_url::(&self.extrinsic_cli_opts.url) - .await?; + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; let extrinsic_opts = ExtrinsicOptsBuilder::default() .file(self.extrinsic_cli_opts.file.clone()) diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index 6798fae2c..4d7bc201e 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -57,8 +57,7 @@ impl UploadCommand { pub async fn handle(&self) -> Result<(), ErrorVariant> { let token_metadata = - TokenMetadata::query_url::(&self.extrinsic_cli_opts.url) - .await?; + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; let extrinsic_opts = ExtrinsicOptsBuilder::default() .file(self.extrinsic_cli_opts.file.clone()) diff --git a/crates/extrinsics/src/balance.rs b/crates/extrinsics/src/balance.rs index ca1d3277f..45fd64047 100644 --- a/crates/extrinsics/src/balance.rs +++ b/crates/extrinsics/src/balance.rs @@ -44,7 +44,7 @@ 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 @@ -78,15 +78,11 @@ pub enum UnitPrefix { } impl TokenMetadata { - pub async fn query_url(url: &Url) -> Result { - let rpc_cli = RpcClient::from_url(url_to_string(url)).await?; - let rpc = LegacyRpcMethods::new(rpc_cli.clone()); - Self::query::(&rpc).await - } - /// 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"); @@ -110,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/integration_tests.rs b/crates/extrinsics/src/integration_tests.rs index 05830c8e1..45d9ec861 100644 --- a/crates/extrinsics/src/integration_tests.rs +++ b/crates/extrinsics/src/integration_tests.rs @@ -16,6 +16,7 @@ use crate::{ CallCommandBuilder, + DisplayEvents, ExtrinsicOptsBuilder, InstantiateCommandBuilder, InstantiateExecResult, @@ -534,7 +535,14 @@ async fn api_build_upload_instantiate_call() { 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 From 8e62631f14cf577be690a1f448e01dce0a0375e2 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Wed, 7 Feb 2024 12:11:09 +0100 Subject: [PATCH 21/39] Add comments --- crates/extrinsics/src/instantiate.rs | 1 + crates/extrinsics/src/remove.rs | 1 + crates/extrinsics/src/upload.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/crates/extrinsics/src/instantiate.rs b/crates/extrinsics/src/instantiate.rs index 0a0362a01..9902fcd17 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -536,6 +536,7 @@ where } } +/// A struct representing the result of an instantiate command execution. pub struct InstantiateExecResult { pub events: ExtrinsicEvents, pub code_hash: Option, diff --git a/crates/extrinsics/src/remove.rs b/crates/extrinsics/src/remove.rs index d3a0e9385..84805420a 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -222,6 +222,7 @@ where } } +/// 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 31dfd40c3..095e4e4d4 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -227,6 +227,7 @@ struct CodeUploadRequest { determinism: Determinism, } +/// A struct representing the result of an upload command execution. pub struct UploadResult { pub code_stored: Option>, pub events: ExtrinsicEvents, From d91982d5f740eb0b8d999fe345277128c415d11c Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Wed, 7 Feb 2024 12:44:00 +0100 Subject: [PATCH 22/39] test --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a937ffe3..b93ec0f10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -180,7 +180,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2023-12-28 + toolchain: nightly-2024-01-23 default: true target: wasm32-unknown-unknown components: rust-src, clippy From 82434e2a90938b9ea82bc32d42967d98f59a0be2 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Wed, 14 Feb 2024 12:48:15 +0100 Subject: [PATCH 23/39] Fix integration tests --- crates/extrinsics/src/integration_tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/extrinsics/src/integration_tests.rs b/crates/extrinsics/src/integration_tests.rs index 78cca73e1..fa5b7cc4c 100644 --- a/crates/extrinsics/src/integration_tests.rs +++ b/crates/extrinsics/src/integration_tests.rs @@ -16,6 +16,7 @@ use crate::{ CallCommandBuilder, + CallExec, DisplayEvents, ExtrinsicOptsBuilder, InstantiateCommandBuilder, From 9cff1fcf5685f9af9381b91fdcbc072296bc1331 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Thu, 15 Feb 2024 15:46:08 +0100 Subject: [PATCH 24/39] Use generic signer --- Cargo.lock | 2 + crates/cargo-contract/Cargo.toml | 1 + crates/cargo-contract/src/cmd/call.rs | 27 ++- crates/cargo-contract/src/cmd/instantiate.rs | 36 ++-- crates/cargo-contract/src/cmd/mod.rs | 10 ++ crates/cargo-contract/src/cmd/remove.rs | 11 +- crates/cargo-contract/src/cmd/upload.rs | 13 +- crates/extrinsics/Cargo.toml | 3 +- crates/extrinsics/src/call.rs | 172 +++++-------------- crates/extrinsics/src/extrinsic_opts.rs | 123 ++++--------- crates/extrinsics/src/instantiate.rs | 139 +++++---------- crates/extrinsics/src/integration_tests.rs | 62 ++++--- crates/extrinsics/src/lib.rs | 21 +-- crates/extrinsics/src/remove.rs | 94 +++------- crates/extrinsics/src/upload.rs | 87 +++------- 15 files changed, 268 insertions(+), 533 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ed7cfe50..32b99df58 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 363692f92..5738dfa34 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.79" diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index 5b90d62d0..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, @@ -54,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 { @@ -97,11 +98,11 @@ impl CallCommand { let token_metadata = TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let extrinsic_opts = ExtrinsicOptsBuilder::default() + 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 @@ -111,16 +112,14 @@ impl CallCommand { ) .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.denominate_balance(&token_metadata)?) - .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 { @@ -212,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 32e4e4f20..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, @@ -56,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 { @@ -104,11 +106,11 @@ impl InstantiateCommand { let token_metadata = TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let extrinsic_opts = ExtrinsicOptsBuilder::default() + 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 @@ -117,17 +119,19 @@ impl InstantiateCommand { .transpose()?, ) .done(); - let instantiate_exec: InstantiateExec = - InstantiateCommandBuilder::default() - .constructor(self.constructor.clone()) - .args(self.args.clone()) - .extrinsic_opts(extrinsic_opts) - .value(self.value.denominate_balance(&token_metadata)?) - .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?; @@ -193,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 { @@ -249,7 +253,7 @@ 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, @@ -284,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..b3deb4854 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,12 @@ pub fn display_all_contracts(contracts: &[::AccountId]) .for_each(|e: &::AccountId| println!("{}", e)) } +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 a09de8273..ad7c7e8d1 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -18,6 +18,7 @@ use crate::ErrorVariant; use std::fmt::Debug; use super::{ + create_signer, parse_code_hash, CLIExtrinsicOpts, }; @@ -35,6 +36,7 @@ use subxt::{ Config, PolkadotConfig as DefaultConfig, }; +use subxt_signer::sr25519::Keypair; #[derive(Debug, clap::Args)] #[clap(name = "remove", about = "Remove a contract's code")] @@ -59,11 +61,11 @@ impl RemoveCommand { let token_metadata = TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let extrinsic_opts = ExtrinsicOptsBuilder::default() + 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 @@ -72,10 +74,9 @@ impl RemoveCommand { .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?; diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index 4d7bc201e..9fdd40762 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -18,6 +18,7 @@ use crate::ErrorVariant; use std::fmt::Debug; use super::{ + create_signer, display_dry_run_result_warning, CLIExtrinsicOpts, }; @@ -38,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")] @@ -59,11 +61,11 @@ impl UploadCommand { let token_metadata = TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let extrinsic_opts = ExtrinsicOptsBuilder::default() + 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 @@ -72,11 +74,8 @@ impl UploadCommand { .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(); diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index c20567678..56ef01ad8 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/call.rs b/crates/extrinsics/src/call.rs index 3b7a9daa9..12a5fbd13 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -15,14 +15,11 @@ // along with cargo-contract. If not, see . use super::{ - account_id, pallet_contracts_primitives::ContractExecResult, - state, state_call, submit_extrinsic, ContractMessageTranscoder, ErrorVariant, - Missing, }; use crate::{ check_env_types, @@ -37,9 +34,7 @@ use anyhow::{ use ink_env::Environment; use scale::Encode; use sp_weights::Weight; -use subxt_signer::sr25519::Keypair; -use core::marker::PhantomData; use subxt::{ backend::{ legacy::LegacyRpcMethods, @@ -50,189 +45,115 @@ use subxt::{ 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: E::Balance, } -/// 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: Default, -{ - fn default() -> Self { - Self::new() - } -} - -impl CallCommandBuilder, X> +impl CallCommandBuilder where 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: Default::default(), - }, - marker: PhantomData, - } - } - - /// Sets the name of the contract message to call. - pub fn message>( - self, - message: T, - ) -> CallCommandBuilder { + contract: C::AccountId, + message: &str, + extrinsic_opts: ExtrinsicOpts, + ) -> 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 { - 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: E::Balance) -> Self { let mut this = self; - this.opts.value = value; + this.value = value; this } -} -impl - CallCommandBuilder -where - C::AccountId: IntoVisitor, -{ /// 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 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, + 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, }) } } -pub struct CallExec { +pub struct CallExec { contract: C::AccountId, message: String, args: Vec, - opts: ExtrinsicOpts, + opts: ExtrinsicOpts, gas_limit: Option, proof_size: Option, value: E::Balance, @@ -240,15 +161,13 @@ pub struct CallExec { client: OnlineClient, transcoder: ContractMessageTranscoder, call_data: Vec, - signer: Keypair, } -impl CallExec +impl CallExec where - C::Signature: From, >::OtherParams: Default, - C::Address: From, - C::AccountId: From + EncodeAsType + IntoVisitor, + C::AccountId: EncodeAsType + IntoVisitor, + Signer: tx::Signer + Clone, { /// Simulates a contract call without modifying the blockchain. /// @@ -262,7 +181,7 @@ where pub async fn call_dry_run(&self) -> Result> { 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, gas_limit: None, @@ -319,7 +238,7 @@ where .build(); let result = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; + submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?; Ok(result) } @@ -379,7 +298,7 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } @@ -412,11 +331,6 @@ where pub fn call_data(&self) -> &Vec { &self.call_data } - - /// Returns the signer. - pub fn signer(&self) -> &Keypair { - &self.signer - } } /// A struct that encodes RPC parameters required for a call to a smart contract. diff --git a/crates/extrinsics/src/extrinsic_opts.rs b/crates/extrinsics/src/extrinsic_opts.rs index ae0ecc85c..cbc1b8b93 100644 --- a/crates/extrinsics/src/extrinsic_opts.rs +++ b/crates/extrinsics/src/extrinsic_opts.rs @@ -14,91 +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, ContractArtifacts, }; 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, + 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> { - /// 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 { +impl ExtrinsicOptsBuilder +where + Signer: tx::Signer + Clone, +{ + /// 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> { - 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; @@ -137,32 +109,16 @@ impl ExtrinsicOptsBuilder { this.opts.verbosity = verbosity; this } -} -impl ExtrinsicOptsBuilder { - /// 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 { - /// 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, - } - } - +impl ExtrinsicOpts +where + Signer: tx::Signer + Clone, +{ /// Load contract artifacts. pub fn contract_artifacts(&self) -> Result { ContractArtifacts::from_manifest_or_file( @@ -171,13 +127,6 @@ impl ExtrinsicOpts { ) } - /// 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() @@ -194,8 +143,8 @@ impl ExtrinsicOpts { } /// Return the secret URI of the signer. - pub fn suri(&self) -> &str { - &self.suri + pub fn signer(&self) -> &Signer { + &self.signer } /// Return the storage deposit limit. @@ -208,9 +157,3 @@ impl ExtrinsicOpts { &self.verbosity } } - -impl Default for ExtrinsicOpts { - 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 9902fcd17..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,12 +23,10 @@ use super::{ ContractInstantiateResult, StorageDeposit, }, - state, state_call, submit_extrinsic, ContractMessageTranscoder, ErrorVariant, - Missing, }; use crate::{ check_env_types, @@ -47,9 +44,7 @@ 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, @@ -68,120 +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, + 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: Default, + Signer: tx::Signer + Clone, + C::Hash: From<[u8; 32]>, { /// Returns a clean builder for [`InstantiateExec`]. - pub fn new() -> InstantiateCommandBuilder> { - InstantiateCommandBuilder { - opts: InstantiateOpts { - constructor: String::from("new"), - args: Vec::new(), - extrinsic_opts: ExtrinsicOpts::default(), - value: Default::default(), - gas_limit: None, - proof_size: None, - salt: None, - }, - _marker: PhantomData, - } - } - - /// Sets the extrinsic operation. - pub fn extrinsic_opts( - self, - extrinsic_opts: ExtrinsicOpts, - ) -> InstantiateCommandBuilder { + pub fn new( + 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: Default, -{ - 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: 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]>, -{ /// Preprocesses contract artifacts and options for instantiation. /// /// This function prepares the required data for instantiating a contract based on the @@ -191,32 +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 args = InstantiateArgs { - constructor: self.opts.constructor.clone(), - raw_args: self.opts.args.clone(), - value: self.opts.value, - gas_limit: self.opts.gas_limit, - proof_size: self.opts.proof_size, - storage_deposit_limit: self.opts.extrinsic_opts.storage_deposit_limit(), + 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, @@ -224,11 +183,10 @@ where Ok(InstantiateExec { args, - opts: self.opts.extrinsic_opts.clone(), + opts: self.extrinsic_opts, url, rpc, client, - signer, transcoder, }) } @@ -292,25 +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, } -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. /// @@ -364,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, @@ -391,7 +347,7 @@ where .build(); let events = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; + 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. @@ -426,7 +382,7 @@ where .build(); let events = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; + submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?; let instantiated = events .find_first::>()? @@ -506,7 +462,7 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } @@ -525,11 +481,6 @@ 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 diff --git a/crates/extrinsics/src/integration_tests.rs b/crates/extrinsics/src/integration_tests.rs index fa5b7cc4c..22aa2bfcb 100644 --- a/crates/extrinsics/src/integration_tests.rs +++ b/crates/extrinsics/src/integration_tests.rs @@ -42,6 +42,10 @@ use subxt::{ OnlineClient, PolkadotConfig as DefaultConfig, }; +use subxt_signer::{ + sr25519::Keypair, + SecretUri, +}; const CONTRACTS_NODE: &str = "substrate-contracts-node"; @@ -468,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(); @@ -483,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() @@ -499,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(); @@ -526,10 +531,12 @@ 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(); @@ -548,10 +555,12 @@ async fn api_build_upload_instantiate_call() { // 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(); @@ -603,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(); @@ -621,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 84805420a..ac4848d02 100644 --- a/crates/extrinsics/src/remove.rs +++ b/crates/extrinsics/src/remove.rs @@ -16,11 +16,9 @@ use super::{ events::CodeRemoved, - state, submit_extrinsic, ContractMessageTranscoder, ErrorVariant, - Missing, }; use crate::{ extrinsic_calls::RemoveCode, @@ -28,7 +26,6 @@ use crate::{ }; use anyhow::Result; -use core::marker::PhantomData; use ink_env::Environment; use subxt::{ backend::{ @@ -41,71 +38,43 @@ use subxt::{ 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 + Signer: tx::Signer + Clone, { /// Returns a clean builder for [`RemoveExec`]. - pub fn new() -> RemoveCommandBuilder> { - RemoveCommandBuilder { - opts: RemoveOpts { - code_hash: None, - extrinsic_opts: ExtrinsicOpts::default(), - }, - marker: PhantomData, - } - } - - /// Sets the extrinsic operation. - pub fn extrinsic_opts( - self, - extrinsic_opts: ExtrinsicOpts, - ) -> RemoveCommandBuilder { + pub fn new( + extrinsic_opts: ExtrinsicOpts, + ) -> RemoveCommandBuilder { RemoveCommandBuilder { - opts: RemoveOpts { - extrinsic_opts, - ..self.opts - }, - marker: PhantomData, + code_hash: None, + extrinsic_opts, } } -} -impl Default - for RemoveCommandBuilder> -{ - 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]>, + Signer: tx::Signer + Clone, { /// Preprocesses contract artifacts and options for subsequent removal of contract /// code. @@ -117,14 +86,13 @@ 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(|h| h.into() ), (None, None) => Err(anyhow::anyhow!( @@ -135,38 +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); Ok(RemoveExec { final_code_hash, - opts: self.opts.extrinsic_opts.clone(), + opts: self.extrinsic_opts, rpc, client, transcoder, - signer, }) } } -pub struct RemoveExec { +pub struct RemoveExec { final_code_hash: C::Hash, - opts: ExtrinsicOpts, + opts: ExtrinsicOpts, rpc: LegacyRpcMethods, client: OnlineClient, transcoder: ContractMessageTranscoder, - signer: Keypair, } -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. /// @@ -186,7 +151,7 @@ where let call = RemoveCode::new(code_hash).build(); let events = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; + submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?; let code_removed = events.find_first::>()?; @@ -202,7 +167,7 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } @@ -215,11 +180,6 @@ where pub fn transcoder(&self) -> &ContractMessageTranscoder { &self.transcoder } - - /// Returns the signer. - pub fn signer(&self) -> &Keypair { - &self.signer - } } /// A struct representing the result of an remove command execution. diff --git a/crates/extrinsics/src/upload.rs b/crates/extrinsics/src/upload.rs index 095e4e4d4..2de54a3f9 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -15,14 +15,11 @@ // along with cargo-contract. If not, see . use super::{ - account_id, events::CodeStored, pallet_contracts_primitives::CodeUploadResult, - state, state_call, submit_extrinsic, ErrorVariant, - Missing, WasmCode, }; use crate::{ @@ -32,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::{ @@ -46,53 +42,27 @@ use subxt::{ 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> { - /// 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> +impl UploadCommandBuilder +where + Signer: tx::Signer + Clone, { - fn default() -> Self { - Self::new() + /// Returns a clean builder for [`UploadExec`]. + pub fn new( + extrinsic_opts: ExtrinsicOpts, + ) -> UploadCommandBuilder { + UploadCommandBuilder { extrinsic_opts } } -} -impl UploadCommandBuilder { /// Preprocesses contract artifacts and options for subsequent upload. /// /// This function prepares the necessary data for uploading a contract @@ -102,10 +72,9 @@ impl UploadCommandBuilder { /// /// 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(|| { @@ -115,39 +84,36 @@ impl UploadCommandBuilder { ) })?; - 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); Ok(UploadExec { - opts: self.opts.extrinsic_opts.clone(), + opts: self.extrinsic_opts, rpc, client, code, - signer, transcoder, }) } } -pub struct UploadExec { - opts: ExtrinsicOpts, +pub struct UploadExec { + opts: ExtrinsicOpts, rpc: LegacyRpcMethods, client: OnlineClient, code: WasmCode, - signer: Keypair, 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, + Signer: tx::Signer + Clone, { /// Uploads contract code to a specified URL using a JSON-RPC call. /// @@ -158,7 +124,7 @@ where pub async fn upload_code_rpc(&self) -> Result> { 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, @@ -183,7 +149,7 @@ where .build(); let events = - submit_extrinsic(&self.client, &self.rpc, &call, &self.signer).await?; + submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?; let code_stored = events.find_first::>()?; Ok(UploadResult { @@ -193,7 +159,7 @@ where } /// Returns the extrinsic options. - pub fn opts(&self) -> &ExtrinsicOpts { + pub fn opts(&self) -> &ExtrinsicOpts { &self.opts } @@ -207,11 +173,6 @@ where &self.code } - /// Returns the signer. - pub fn signer(&self) -> &Keypair { - &self.signer - } - /// Returns the contract message transcoder. pub fn transcoder(&self) -> &ContractMessageTranscoder { &self.transcoder From cb1d83fcee11f8789076e1be15c9173ef485c753 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Thu, 15 Feb 2024 16:07:52 +0100 Subject: [PATCH 25/39] Update comments --- crates/cargo-contract/src/cmd/mod.rs | 1 + crates/extrinsics/src/extrinsic_opts.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index b3deb4854..f0fb9f125 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -275,6 +275,7 @@ 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)?; diff --git a/crates/extrinsics/src/extrinsic_opts.rs b/crates/extrinsics/src/extrinsic_opts.rs index cbc1b8b93..a1edab25c 100644 --- a/crates/extrinsics/src/extrinsic_opts.rs +++ b/crates/extrinsics/src/extrinsic_opts.rs @@ -142,7 +142,7 @@ where url_to_string(&self.url) } - /// Return the secret URI of the signer. + /// Return the signer. pub fn signer(&self) -> &Signer { &self.signer } From 59457e4307ca5ab4af23f0f046db05bca006dc2b Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 16 Feb 2024 16:21:05 +0100 Subject: [PATCH 26/39] Add config --- Cargo.lock | 10 + crates/cargo-contract/Cargo.toml | 5 +- crates/cargo-contract/src/cmd/call.rs | 77 +++-- crates/cargo-contract/src/cmd/info.rs | 55 ++-- crates/cargo-contract/src/cmd/instantiate.rs | 7 +- crates/cargo-contract/src/cmd/mod.rs | 71 +++-- crates/cargo-contract/src/cmd/remove.rs | 6 +- crates/cargo-contract/src/cmd/storage.rs | 38 ++- crates/cargo-contract/src/cmd/upload.rs | 4 +- crates/cargo-contract/src/config.rs | 110 +++++++ crates/cargo-contract/src/main.rs | 81 ++++- crates/extrinsics/Cargo.toml | 1 + crates/transcode/Cargo.toml | 9 + crates/transcode/src/account_id20.rs | 300 +++++++++++++++++++ crates/transcode/src/lib.rs | 6 + 15 files changed, 677 insertions(+), 103 deletions(-) create mode 100644 crates/cargo-contract/src/config.rs create mode 100644 crates/transcode/src/account_id20.rs diff --git a/Cargo.lock b/Cargo.lock index 32b99df58..be35d68a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -911,6 +911,7 @@ dependencies = [ "serde", "serde_json", "sp-core", + "sp-keyring", "sp-weights", "substrate-build-script-utils", "subxt", @@ -1261,11 +1262,14 @@ dependencies = [ "contract-metadata", "escape8259", "hex", + "impl-serde", "indexmap 2.2.3", "ink", "ink_env", "ink_metadata", "itertools 0.12.1", + "libsecp256k1", + "log", "nom", "nom-supreme", "parity-scale-codec", @@ -1273,9 +1277,13 @@ dependencies = [ "scale-info", "serde", "serde_json", + "sha3", "sp-core", + "sp-io", "sp-keyring", + "sp-runtime", "strsim 0.11.0", + "subxt", "thiserror", "tracing", ] @@ -5423,7 +5431,9 @@ dependencies = [ "scale-value", "serde", "serde_json", + "sp-core", "sp-core-hashing", + "sp-runtime", "subxt-lightclient", "subxt-macro", "subxt-metadata", diff --git a/crates/cargo-contract/Cargo.toml b/crates/cargo-contract/Cargo.toml index 5738dfa34..10f5e8c4a 100644 --- a/crates/cargo-contract/Cargo.toml +++ b/crates/cargo-contract/Cargo.toml @@ -43,11 +43,12 @@ comfy-table = "7.1.0" # dependencies for extrinsics (deploying and calling a contract) tokio = { version = "1", features = ["macros", "rt-multi-thread"] } -subxt = "0.34.0" +subxt = { version = "0.34.0", features = ["substrate-compat"] } sp-core = "28.0.0" sp-weights = "27.0.0" hex = "0.4.3" -subxt-signer = { version = "0.34.0", features = ["subxt", "sr25519"] } +subxt-signer = { version = "0.34.0", features = ["subxt", "sr25519"] } +sp-keyring = "31.0.0" [build-dependencies] anyhow = "1.0.79" diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index fc1b94e1d..ecffa726d 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -14,25 +14,20 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use crate::ErrorVariant; +use crate::{config::SignerConfig, ErrorVariant}; use contract_build::util::DEFAULT_KEY_COL_WIDTH; use ink_env::{ DefaultEnvironment, Environment, }; -use std::fmt::Debug; +use subxt::{tx::PairSigner, PolkadotConfig}; +use serde::Serialize; +use subxt_signer::{sr25519::{Keypair, PublicKey}, SecretUri}; +use std::{fmt::{Debug, Display}, str::FromStr}; use super::{ - create_signer, - display_contract_exec_result, - display_contract_exec_result_debug, - display_dry_run_result_warning, - print_dry_running_status, - print_gas_required_success, - prompt_confirm_tx, - CLIExtrinsicOpts, - MAX_KEY_COL_WIDTH, + create_signer, create_signer2, display_contract_exec_result, display_contract_exec_result_debug, display_dry_run_result_warning, print_dry_running_status, print_gas_required_success, prompt_confirm_tx, CLIExtrinsicOpts, MAX_KEY_COL_WIDTH }; use anyhow::{ anyhow, @@ -52,16 +47,22 @@ use contract_extrinsics::{ use contract_transcode::Value; use sp_weights::Weight; use subxt::{ - Config, - PolkadotConfig as DefaultConfig, + ext::{scale_decode::IntoVisitor, scale_encode::EncodeAsType}, tx::Signer, Config }; -use subxt_signer::sr25519::Keypair; #[derive(Debug, clap::Args)] #[clap(name = "call", about = "Call a contract")] -pub struct CallCommand { +pub struct CallCommand +where +::AccountId: Sync + Send + FromStr, +<::AccountId as FromStr>::Err: + Into>, +::Balance: Sync + Send +From + Debug + FromStr, + <::Balance as FromStr>::Err: + Into>, +{ /// The address of the the contract to call. #[clap(name = "contract", long, env = "CONTRACT")] - contract: ::AccountId, + contract: ::AccountId, /// The name of the contract message to call. #[clap(long, short)] message: String, @@ -69,7 +70,7 @@ pub struct CallCommand { #[clap(long, num_args = 0..)] args: Vec, #[clap(flatten)] - extrinsic_cli_opts: CLIExtrinsicOpts, + extrinsic_cli_opts: CLIExtrinsicOpts, /// Maximum amount of gas (execution time) to be used for this command. /// If not specified will perform a dry-run to estimate the gas consumed for the /// call. @@ -82,23 +83,39 @@ pub struct CallCommand { proof_size: Option, /// The value to be transferred as part of the call. #[clap(name = "value", long, default_value = "0")] - value: BalanceVariant<::Balance>, + value: BalanceVariant<::Balance>, /// Export the call output in JSON format. #[clap(long, conflicts_with = "verbose")] output_json: bool, } -impl CallCommand { +impl >CallCommand +where +::AccountId: Sync + Send + FromStr + IntoVisitor + EncodeAsType, +<::AccountId as FromStr>::Err: + Into>, +::Balance: Sync + Send + From + Display + Default + Debug + FromStr, +<::Balance as FromStr>::Err: + Into>, +<::ExtrinsicParams as subxt::config::ExtrinsicParams>::OtherParams: Default, +{ /// Returns whether to export the call output in JSON format. pub fn output_json(&self) -> bool { self.output_json } pub async fn handle(&self) -> Result<(), ErrorVariant> { + +// let keypair = sp_core::sr25519::Pair::from_string("vessel ladder alter error federal sibling chat ability sun glass valve picture/0/1///Password", None).unwrap(); +// let signer = PairSigner::::new(keypair); + + let uri = ::from_str("//Alice").unwrap(); + let signer = Keypair::from_uri(&uri).unwrap(); + let token_metadata = - TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let signer = create_signer(&self.extrinsic_cli_opts.suri)?; + let signer = create_signer2::(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()) @@ -191,14 +208,14 @@ impl CallCommand { } let events = call_exec.call(Some(gas_limit)).await?; let display_events = DisplayEvents::from_events::< - DefaultConfig, - DefaultEnvironment, + C, + C, >(&events, None, &metadata)?; let output = if self.output_json() { display_events.to_json()? } else { - display_events.display_events::( + display_events.display_events::( self.extrinsic_cli_opts.verbosity().unwrap(), &token_metadata, )? @@ -210,8 +227,8 @@ 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, +async fn pre_submit_dry_run_gas_estimate_call>( + call_exec: &CallExec>::Signer>, output_json: bool, skip_dry_run: bool, ) -> Result { @@ -260,17 +277,19 @@ async fn pre_submit_dry_run_gas_estimate_call( /// Result of the contract call #[derive(serde::Serialize)] -pub struct CallDryRunResult { +pub struct CallDryRunResult +{ /// Was the operation reverted pub reverted: bool, pub data: Value, pub gas_consumed: Weight, pub gas_required: Weight, /// Storage deposit after the operation - pub storage_deposit: StorageDeposit<::Balance>, + pub storage_deposit: StorageDeposit, } -impl CallDryRunResult { +impl CallDryRunResult +{ /// Returns a result in json format pub fn to_json(&self) -> Result { Ok(serde_json::to_string_pretty(self)?) diff --git a/crates/cargo-contract/src/cmd/info.rs b/crates/cargo-contract/src/cmd/info.rs index e9e19f49b..66966cacf 100644 --- a/crates/cargo-contract/src/cmd/info.rs +++ b/crates/cargo-contract/src/cmd/info.rs @@ -17,7 +17,6 @@ use super::{ basic_display_format_extended_contract_info, display_all_contracts, - DefaultConfig, }; use anyhow::Result; use contract_analyze::determine_language; @@ -30,26 +29,37 @@ use contract_extrinsics::{ ErrorVariant, TrieId, }; -use ink_env::{ - DefaultEnvironment, - Environment, -}; +use ink_env::Environment; +use serde::Serialize; use std::{ - fmt::Debug, + fmt::{ + Debug, + Display, + }, io::Write, + str::FromStr, }; use subxt::{ backend::{ legacy::LegacyRpcMethods, rpc::RpcClient, }, + ext::{ + codec::Decode, + scale_decode::IntoVisitor, + }, Config, OnlineClient, }; #[derive(Debug, clap::Args)] #[clap(name = "info", about = "Get infos from a contract")] -pub struct InfoCommand { +pub struct InfoCommand +where + ::AccountId: Sync + Send + FromStr, + <::AccountId as FromStr>::Err: + Into>, +{ /// The address of the contract to display info of. #[clap( name = "contract", @@ -57,7 +67,7 @@ pub struct InfoCommand { env = "CONTRACT", required_unless_present = "all" )] - contract: Option<::AccountId>, + contract: Option<::AccountId>, /// Websockets url of a substrate node. #[clap( name = "url", @@ -77,12 +87,19 @@ pub struct InfoCommand { all: bool, } -impl InfoCommand { +impl InfoCommand +where + ::AccountId: + Serialize + Display + IntoVisitor + Decode + AsRef<[u8]> + Send + Sync + FromStr, + ::Hash: IntoVisitor + Display, + ::Balance: Serialize + Debug + IntoVisitor, + <::AccountId as FromStr>::Err: + Into>, +{ pub async fn run(&self) -> Result<(), ErrorVariant> { let rpc_cli = RpcClient::from_url(url_to_string(&self.url)).await?; - let client = - OnlineClient::::from_rpc_client(rpc_cli.clone()).await?; - let rpc = LegacyRpcMethods::::new(rpc_cli.clone()); + let client = OnlineClient::::from_rpc_client(rpc_cli.clone()).await?; + let rpc = LegacyRpcMethods::::new(rpc_cli.clone()); // All flag applied if self.all { @@ -105,10 +122,8 @@ impl InfoCommand { .as_ref() .expect("Contract argument was not provided"); - let info_to_json = fetch_contract_info::( - contract, &rpc, &client, - ) - .await?; + let info_to_json = + fetch_contract_info::(contract, &rpc, &client).await?; let wasm_code = fetch_wasm_code(&client, &rpc, info_to_json.code_hash()).await?; @@ -128,16 +143,16 @@ impl InfoCommand { println!( "{}", serde_json::to_string_pretty(&ExtendedContractInfo::< - ::Hash, - ::Balance, + ::Hash, + C::Balance, >::new( info_to_json, &wasm_code ))? ) } else { basic_display_format_extended_contract_info(&ExtendedContractInfo::< - ::Hash, - ::Balance, + ::Hash, + C::Balance, >::new( info_to_json, &wasm_code )) diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index 58de3da4b..e75a91059 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -26,10 +26,7 @@ use super::{ MAX_KEY_COL_WIDTH, }; use crate::{ - anyhow, - ErrorVariant, - InstantiateExec, - Weight, + anyhow, config::PolkadotBaseConfig, ErrorVariant, InstantiateExec, Weight }; use anyhow::Result; use contract_build::{ @@ -68,7 +65,7 @@ pub struct InstantiateCommand { #[clap(long, num_args = 0..)] args: Vec, #[clap(flatten)] - extrinsic_cli_opts: CLIExtrinsicOpts, + extrinsic_cli_opts: CLIExtrinsicOpts, /// Transfers an initial balance to the instantiated contract #[clap(name = "value", long, default_value = "0")] value: BalanceVariant<::Balance>, diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index f0fb9f125..786c0dfa4 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -26,7 +26,8 @@ pub mod schema; pub mod storage; pub mod upload; pub mod verify; - +use subxt::tx::PairSigner; +use sp_core::Pair; pub(crate) use self::{ build::{ BuildCommand, @@ -71,18 +72,18 @@ use contract_extrinsics::{ pallet_contracts_primitives::ContractResult, BalanceVariant, }; +use subxt::{tx, Config}; use core::fmt; use ink_env::{ DefaultEnvironment, Environment, }; -use std::io::{ - self, - Write, -}; -pub use subxt::{ - Config, - PolkadotConfig as DefaultConfig, +use std::{ + fmt::{Debug, Display}, + io::{ + self, + Write, + }, str::FromStr, }; use subxt_signer::{ sr25519::Keypair, @@ -91,7 +92,11 @@ use subxt_signer::{ /// Arguments required for creating and sending an extrinsic to a substrate node. #[derive(Clone, Debug, clap::Args)] -pub struct CLIExtrinsicOpts { +pub struct CLIExtrinsicOpts +where ::Balance: Send + Sync + Debug + FromStr, +<::Balance as FromStr>::Err: + Into>, +{ /// Path to a contract build artifact file: a raw `.wasm` file, a `.contract` bundle, /// or a `.json` metadata file. #[clap(value_parser, conflicts_with = "manifest_path")] @@ -123,7 +128,7 @@ pub struct CLIExtrinsicOpts { /// storage. consumed. #[clap(long)] storage_deposit_limit: - Option::Balance>>, + Option::Balance>>, /// Before submitting a transaction, do not dry-run it via RPC first. #[clap(long)] skip_dry_run: bool, @@ -132,7 +137,11 @@ pub struct CLIExtrinsicOpts { skip_confirm: bool, } -impl CLIExtrinsicOpts { +impl CLIExtrinsicOpts +where ::Balance: Send + Sync + Debug + FromStr, +<::Balance as FromStr>::Err: + Into>, +{ /// Returns the verbosity pub fn verbosity(&self) -> Result { TryFrom::try_from(&self.verbosity) @@ -235,10 +244,11 @@ pub fn print_gas_required_success(gas: Weight) { } /// Display contract information in a formatted way -pub fn basic_display_format_extended_contract_info( - info: &ExtendedContractInfo::Balance>, +pub fn basic_display_format_extended_contract_info( + info: &ExtendedContractInfo, ) where Hash: fmt::Debug, + Balance: fmt::Debug, { name_value_println!("TrieId", info.trie_id, MAX_KEY_COL_WIDTH); name_value_println!( @@ -269,21 +279,40 @@ pub fn basic_display_format_extended_contract_info( } /// Display all contracts addresses in a formatted way -pub fn display_all_contracts(contracts: &[::AccountId]) { - contracts - .iter() - .for_each(|e: &::AccountId| println!("{}", e)) +pub fn display_all_contracts(contracts: &[AccountId]) +where + AccountId: Display, +{ + contracts.iter().for_each(|e: &AccountId| println!("{}", e)) } /// Create a Signer from a secret URI. -pub fn create_signer(suri: &str) -> Result { +pub fn create_signer2(signer: Signer/*suri: &str*/) -> Result +where Signer: tx::Signer,// C::AccountId: From, +//::AccountId: std::convert::From, +//C::Address: From, +//Pair: sp_core::Pair +{ + // let uri = ::from_str(suri)?; + // let keypair = Keypair::from_uri(&uri)?; + //let keypair = sp_core::sr25519::Pair::from_string("vessel ladder alter error federal sibling chat ability sun glass valve picture/0/1///Password", None).unwrap(); + //let keypair = PairSigner::::new(keypair); + Ok(signer) +} + +/// 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) + let signer = Keypair::from_uri(&uri)?; + Ok(signer) } /// Parse a hex encoded 32 byte hash. Returns error if not exactly 32 bytes. -pub fn parse_code_hash(input: &str) -> Result<::Hash> { +pub fn parse_code_hash(input: &str) -> Result +where + Hash: std::convert::From<[u8; 32]>, +{ let bytes = contract_build::util::decode_hex(input)?; if bytes.len() != 32 { anyhow::bail!("Code hash should be 32 bytes in length") diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index ad7c7e8d1..fa997fb55 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use crate::ErrorVariant; +use crate::{config::PolkadotBaseConfig, ErrorVariant}; use std::fmt::Debug; use super::{ @@ -42,10 +42,10 @@ use subxt_signer::sr25519::Keypair; #[clap(name = "remove", about = "Remove a contract's code")] pub struct RemoveCommand { /// The hash of the smart contract code already uploaded to the chain. - #[clap(long, value_parser = parse_code_hash)] + #[clap(long, value_parser = parse_code_hash::<::Hash>)] code_hash: Option<::Hash>, #[clap(flatten)] - extrinsic_cli_opts: CLIExtrinsicOpts, + extrinsic_cli_opts: CLIExtrinsicOpts, /// Export the call output as JSON. #[clap(long, conflicts_with = "verbose")] output_json: bool, diff --git a/crates/cargo-contract/src/cmd/storage.rs b/crates/cargo-contract/src/cmd/storage.rs index 73d198f7a..04f73b24d 100644 --- a/crates/cargo-contract/src/cmd/storage.rs +++ b/crates/cargo-contract/src/cmd/storage.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use super::DefaultConfig; use anyhow::Result; use colored::Colorize; use comfy_table::{ @@ -28,16 +27,29 @@ use contract_extrinsics::{ ContractStorageRpc, ErrorVariant, }; -use ink_env::DefaultEnvironment; -use std::path::PathBuf; -use subxt::Config; +use ink_env::Environment; +use serde::Serialize; +use std::{ + fmt::Display, + path::PathBuf, + str::FromStr, +}; +use subxt::{ + ext::scale_decode::IntoVisitor, + Config, +}; #[derive(Debug, clap::Args)] #[clap(name = "storage", about = "Inspect contract storage")] -pub struct StorageCommand { +pub struct StorageCommand +where + ::AccountId: Sync + Send + FromStr, + <::AccountId as FromStr>::Err: + Into>, +{ /// The address of the contract to inspect storage of. #[clap(name = "contract", long, env = "CONTRACT")] - contract: ::AccountId, + contract: ::AccountId, /// Fetch the "raw" storage keys and values for the contract. #[clap(long)] raw: bool, @@ -61,11 +73,17 @@ pub struct StorageCommand { url: url::Url, } -impl StorageCommand { +impl StorageCommand +where + ::AccountId: Display + IntoVisitor + AsRef<[u8]> + Send + Sync + FromStr, + <::AccountId as FromStr>::Err: + Into>, + ::Balance: Serialize + IntoVisitor, + ::Hash: IntoVisitor, +{ pub async fn run(&self) -> Result<(), ErrorVariant> { - let rpc = ContractStorageRpc::::new(&self.url).await?; - let storage_layout = - ContractStorage::::new(rpc); + let rpc = ContractStorageRpc::::new(&self.url).await?; + let storage_layout = ContractStorage::::new(rpc); if self.raw { let storage_data = storage_layout diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index 9fdd40762..504f81909 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use crate::ErrorVariant; +use crate::{config::PolkadotBaseConfig, ErrorVariant}; use std::fmt::Debug; use super::{ @@ -45,7 +45,7 @@ use subxt_signer::sr25519::Keypair; #[clap(name = "upload", about = "Upload a contract's code")] pub struct UploadCommand { #[clap(flatten)] - extrinsic_cli_opts: CLIExtrinsicOpts, + extrinsic_cli_opts: CLIExtrinsicOpts, /// Export the call output in JSON format. #[clap(long, conflicts_with = "verbose")] output_json: bool, diff --git a/crates/cargo-contract/src/config.rs b/crates/cargo-contract/src/config.rs new file mode 100644 index 000000000..27b5acb20 --- /dev/null +++ b/crates/cargo-contract/src/config.rs @@ -0,0 +1,110 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +use contract_transcode::{ + AccountId20, + EthereumSignature, +}; +use ink_env::{ + DefaultEnvironment, + Environment, +}; +use subxt::{ + config::{ + DefaultExtrinsicParams, + PolkadotExtrinsicParams, + }, + utils::MultiAddress, + Config, + SubstrateConfig, + tx::Signer as SignerT +}; +use subxt_signer::sr25519::Keypair; + +/// A runtime configuration for the Ethereum based chain like Moonbeam. +/// This thing is not meant to be instantiated; it is just a collection of types. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EthereumBaseConfig {} +impl Config for EthereumBaseConfig { + type Hash = ::Hash; + type AccountId = AccountId20; + type Address = AccountId20; + type Signature = EthereumSignature; + type Hasher = ::Hasher; + type Header = ::Header; + type ExtrinsicParams = DefaultExtrinsicParams; + type AssetId = ::AssetId; +} + +impl Environment for EthereumBaseConfig { + const MAX_EVENT_TOPICS: usize = ::MAX_EVENT_TOPICS; + type AccountId = ::AccountId; + type Balance = ::Balance; + type Hash = ::Hash; + type Timestamp = ::Timestamp; + type BlockNumber = ::BlockNumber; + type ChainExtension = ::ChainExtension; +} + +/// A runtime configuration for the Polkadot based chain. +// /// This thing is not meant to be instantiated; it is just a collection of types. +#[derive(Debug, Clone, PartialEq, Eq)] + +pub enum PolkadotBaseConfig {} +impl Config for PolkadotBaseConfig { + type Hash = ::Hash; + type AccountId = ::AccountId; + type Address = MultiAddress; + type Signature = ::Signature; + type Hasher = ::Hasher; + type Header = ::Header; + type ExtrinsicParams = PolkadotExtrinsicParams; + type AssetId = u32; +} + +impl Environment for PolkadotBaseConfig { + const MAX_EVENT_TOPICS: usize = ::MAX_EVENT_TOPICS; + type AccountId = ::AccountId; + type Balance = ::Balance; + type Hash = ::Hash; + type Timestamp = ::Timestamp; + type BlockNumber = ::BlockNumber; + type ChainExtension = ::ChainExtension; +} + +pub trait SignerConfig +where + Self: Clone, +{ + type Signer: Clone + SignerT; +} + +impl SignerConfig for PolkadotBaseConfig { + type Signer = Keypair; +} + +// impl SignerConfig for EthereumBaseConfig { +// type Signer = (); +// } + +pub enum ChainConfig { + PolkadotBaseConfig, + // EthereumBaseConfig, +} + +pub fn select_config(config: &str) -> ChainConfig { + ChainConfig::PolkadotBaseConfig +} diff --git a/crates/cargo-contract/src/main.rs b/crates/cargo-contract/src/main.rs index 916dd9d41..1862e83f9 100644 --- a/crates/cargo-contract/src/main.rs +++ b/crates/cargo-contract/src/main.rs @@ -17,6 +17,7 @@ #![deny(unused_crate_dependencies)] mod cmd; +mod config; use self::cmd::{ BuildCommand, @@ -46,17 +47,32 @@ use clap::{ }; use cmd::encode::EncodeCommand; use colored::Colorize; +use config::{ + PolkadotBaseConfig, SignerConfig, +}; use contract_build::{ util::DEFAULT_KEY_COL_WIDTH, OutputType, }; use contract_extrinsics::InstantiateExec; +use ink_env::Environment; +use serde::Serialize; use sp_weights::Weight; use std::{ - fmt::Debug, + fmt::{ + Debug, + Display, + }, path::PathBuf, str::FromStr, }; +use subxt::{ + ext::{ + codec::Decode, + scale_decode::IntoVisitor, scale_encode::EncodeAsType, + }, + Config, +}; use tokio::runtime::Runtime; // These crates are only used when we run integration tests `--features // integration-tests`. However since we can't have optional `dev-dependencies` we pretend @@ -77,18 +93,35 @@ use which as _; #[derive(Debug, Parser)] #[clap(bin_name = "cargo")] #[clap(version = env!("CARGO_CONTRACT_CLI_IMPL_VERSION"))] -pub(crate) enum Opts { +pub(crate) enum Opts> +where + ::AccountId: Sync + Send + FromStr, + <::AccountId as FromStr>::Err: + Into>, + ::Balance: Sync + Send + From + Debug + FromStr, + <::Balance as FromStr>::Err: + Into>, +{ /// Utilities to develop Wasm smart contracts. #[clap(name = "contract")] #[clap(version = env!("CARGO_CONTRACT_CLI_IMPL_VERSION"))] #[clap(action = ArgAction::DeriveDisplayOrder)] - Contract(ContractArgs), + Contract(ContractArgs), } #[derive(Debug, Args)] -pub(crate) struct ContractArgs { +pub(crate) struct ContractArgs> +where + ::AccountId: Sync + Send + FromStr, + <::AccountId as FromStr>::Err: + Into>, + ::Balance: Sync + Send + From + Debug + FromStr, + <::Balance as FromStr>::Err: + Into>, + +{ #[clap(subcommand)] - cmd: Command, + cmd: Command, } #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -103,7 +136,15 @@ impl FromStr for HexData { } #[derive(Debug, Subcommand)] -enum Command { +enum Command> +where + ::AccountId: Sync + Send + FromStr, + <::AccountId as FromStr>::Err: + Into>, + ::Balance: Sync + Send + From + Debug + FromStr, + <::Balance as FromStr>::Err: + Into>, +{ /// Setup and create a new smart contract project #[clap(name = "new")] New { @@ -129,7 +170,7 @@ enum Command { Instantiate(InstantiateCommand), /// Call a contract #[clap(name = "call")] - Call(CallCommand), + Call(CallCommand), /// Encodes a contracts input calls and their arguments #[clap(name = "encode")] Encode(EncodeCommand), @@ -141,10 +182,10 @@ enum Command { Remove(RemoveCommand), /// Display information about a contract #[clap(name = "info")] - Info(InfoCommand), + Info(InfoCommand), /// Inspect the on-chain storage of a contract. #[clap(name = "storage")] - Storage(StorageCommand), + Storage(StorageCommand), /// Verifies that a given contract binary matches the build result of the specified /// workspace. #[clap(name = "verify")] @@ -162,8 +203,14 @@ enum Command { fn main() { tracing_subscriber::fmt::init(); + let config = config::select_config("eth"); + + // let Opts::Contract(args) = match config { + // ChainConfig::EthereumBaseConfig => Opts::::parse(), + // ChainConfig::PolkadotBaseConfig => Opts::::parse(), + // }; - let Opts::Contract(args) = Opts::parse(); + let Opts::Contract(args) = Opts::::parse(); match exec(args.cmd) { Ok(()) => {} @@ -174,7 +221,19 @@ fn main() { } } -fn exec(cmd: Command) -> Result<()> { +fn exec>(cmd: Command) -> Result<()> +where + ::AccountId: + Serialize + Display + IntoVisitor + Decode + AsRef<[u8]> + Send + Sync + FromStr + EncodeAsType, + <::AccountId as FromStr>::Err: + Into>, + ::Hash: IntoVisitor + Display, + ::Balance: Serialize + Decode + Debug + IntoVisitor + Sync + Send + From + Default + Display + FromStr, + <::Balance as FromStr>::Err: + Into>, + <::ExtrinsicParams as subxt::config::ExtrinsicParams>::OtherParams: Default, + +{ let runtime = Runtime::new().expect("Failed to create Tokio runtime"); match &cmd { Command::New { name, target_dir } => { diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index 56ef01ad8..a27bb2fbb 100644 --- a/crates/extrinsics/Cargo.toml +++ b/crates/extrinsics/Cargo.toml @@ -53,5 +53,6 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } subxt-signer = { version = "0.34.0", features = ["subxt", "sr25519"] } [features] +default = ["integration-tests"] integration-tests = [] test-ci-only = [] diff --git a/crates/transcode/Cargo.toml b/crates/transcode/Cargo.toml index 741484ea1..5b1c49666 100644 --- a/crates/transcode/Cargo.toml +++ b/crates/transcode/Cargo.toml @@ -38,6 +38,15 @@ serde_json = "1.0.113" thiserror = "1.0.57" strsim = "0.11.0" +impl-serde = { version = "0.4.0" } +libsecp256k1 = { version = "0.7.1", features = [ "hmac" ] } +sha3 = { version = "0.10.3", default-features = false } +sp-core = "28.0.0" +sp-io = "30.0.0" +sp-runtime = "31.0.0" +log = "0.4.20" +subxt = "0.34.0" + [dev-dependencies] assert_matches = "1.5.0" ink = "5.0.0-rc.1" diff --git a/crates/transcode/src/account_id20.rs b/crates/transcode/src/account_id20.rs new file mode 100644 index 000000000..a1c9b931e --- /dev/null +++ b/crates/transcode/src/account_id20.rs @@ -0,0 +1,300 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Moonbeam is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Moonbeam. If not, see . + +//! The Ethereum Signature implementation. +//! +//! It includes the Verify and IdentifyAccount traits for the AccountId20 + +use scale::{ + Decode, + Encode, + MaxEncodedLen, +}; +use scale_info::TypeInfo; +pub use serde::Serialize; +use sha3::{ + Digest, + Keccak256, +}; +use sp_core::{ + ecdsa, + H160, +}; +use subxt::ext::scale_decode; +use subxt::ext::scale_encode; + +// TODO Maybe this should be upstreamed into Frontier (And renamed accordingly) so that it +// can be used in palletEVM as well. It may also need more traits such as AsRef, AsMut, +// etc like AccountId32 has. + +/// The account type to be used in Moonbeam. It is a wrapper for 20 fixed bytes. We prefer +/// to use a dedicated type to prevent using arbitrary 20 byte arrays were AccountIds are +/// expected. With the introduction of the `scale-info` crate this benefit extends even to +/// non-Rust tools like Polkadot JS. + +#[derive( + Eq, + PartialEq, + Copy, + Clone, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + Default, + PartialOrd, + Ord, + scale_decode::DecodeAsType, + scale_encode::EncodeAsType, +)] +#[decode_as_type(crate_path = "subxt::ext::scale_decode")] +#[encode_as_type(crate_path = "subxt::ext::scale_encode")] +pub struct AccountId20(pub [u8; 20]); + +impl_serde::impl_fixed_hash_serde!(AccountId20, 20); + +impl std::fmt::Display for AccountId20 { + // TODO This is a pretty quck-n-dirty implementation. Perhaps we should add + // checksum casing here? I bet there is a crate for that. + // Maybe this one https://github.com/miguelmota/rust-eth-checksum + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl core::fmt::Debug for AccountId20 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", H160(self.0)) + } +} + +impl From<[u8; 20]> for AccountId20 { + fn from(bytes: [u8; 20]) -> Self { + Self(bytes) + } +} + +impl From for [u8; 20] { + fn from(value: AccountId20) -> Self { + value.0 + } +} + +impl From<[u8; 32]> for AccountId20 { + fn from(bytes: [u8; 32]) -> Self { + let mut buffer = [0u8; 20]; + buffer.copy_from_slice(&bytes[..20]); + Self(buffer) + } +} + +impl From for AccountId20 { + fn from(h160: H160) -> Self { + Self(h160.0) + } +} + +impl From for H160 { + fn from(value: AccountId20) -> Self { + H160(value.0) + } +} + +impl std::str::FromStr for AccountId20 { + type Err = &'static str; + fn from_str(input: &str) -> Result { + H160::from_str(input) + .map(Into::into) + .map_err(|_| "invalid hex address.") + } +} + +impl AsRef<[u8]> for AccountId20 { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +#[derive( + serde::Serialize, + serde::Deserialize, + Eq, + PartialEq, + Clone, + Encode, + Decode, + sp_core::RuntimeDebug, + TypeInfo, +)] +pub struct EthereumSignature(ecdsa::Signature); + +impl From for EthereumSignature { + fn from(x: ecdsa::Signature) -> Self { + EthereumSignature(x) + } +} + +impl sp_runtime::traits::Verify for EthereumSignature { + type Signer = EthereumSigner; + fn verify>( + &self, + mut msg: L, + signer: &AccountId20, + ) -> bool { + let mut m = [0u8; 32]; + m.copy_from_slice(Keccak256::digest(msg.get()).as_slice()); + match sp_io::crypto::secp256k1_ecdsa_recover(self.0.as_ref(), &m) { + Ok(pubkey) => { + AccountId20( + H160::from_slice(&Keccak256::digest(pubkey).as_slice()[12..32]).0, + ) == *signer + } + Err(sp_io::EcdsaVerifyError::BadRS) => { + log::error!(target: "evm", "Error recovering: Incorrect value of R or S"); + false + } + Err(sp_io::EcdsaVerifyError::BadV) => { + log::error!(target: "evm", "Error recovering: Incorrect value of V"); + false + } + Err(sp_io::EcdsaVerifyError::BadSignature) => { + log::error!(target: "evm", "Error recovering: Invalid signature"); + false + } + } + } +} + +/// Public key for an Ethereum / Moonbeam compatible account +#[derive( + Eq, + PartialEq, + Ord, + PartialOrd, + Clone, + Encode, + Decode, + sp_core::RuntimeDebug, + TypeInfo, + serde::Serialize, + serde::Deserialize, +)] +pub struct EthereumSigner([u8; 20]); + +impl sp_runtime::traits::IdentifyAccount for EthereumSigner { + type AccountId = AccountId20; + fn into_account(self) -> AccountId20 { + AccountId20(self.0) + } +} + +impl From<[u8; 20]> for EthereumSigner { + fn from(x: [u8; 20]) -> Self { + EthereumSigner(x) + } +} + +impl From for EthereumSigner { + fn from(x: ecdsa::Public) -> Self { + let decompressed = libsecp256k1::PublicKey::parse_slice( + &x.0, + Some(libsecp256k1::PublicKeyFormat::Compressed), + ) + .expect("Wrong compressed public key provided") + .serialize(); + let mut m = [0u8; 64]; + m.copy_from_slice(&decompressed[1..65]); + let account = H160::from_slice(&Keccak256::digest(m).as_slice()[12..32]); + EthereumSigner(account.into()) + } +} + +impl From for EthereumSigner { + fn from(x: libsecp256k1::PublicKey) -> Self { + let mut m = [0u8; 64]; + m.copy_from_slice(&x.serialize()[1..65]); + let account = H160::from_slice(&Keccak256::digest(m).as_slice()[12..32]); + EthereumSigner(account.into()) + } +} + +impl std::fmt::Display for EthereumSigner { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "ethereum signature: {:?}", H160::from_slice(&self.0)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::{ + ecdsa, + Pair, + H256, + }; + use sp_runtime::traits::IdentifyAccount; + + #[test] + fn test_account_derivation_1() { + // Test from https://asecuritysite.com/encryption/ethadd + let secret_key = hex::decode( + "502f97299c472b88754accd412b7c9a6062ef3186fba0c0388365e1edec24875", + ) + .unwrap(); + let mut expected_hex_account = [0u8; 20]; + hex::decode_to_slice( + "976f8456e4e2034179b284a23c0e0c8f6d3da50c", + &mut expected_hex_account, + ) + .expect("example data is 20 bytes of valid hex"); + + let public_key = ecdsa::Pair::from_seed_slice(&secret_key).unwrap().public(); + let account: EthereumSigner = public_key.into(); + let expected_account = AccountId20::from(expected_hex_account); + assert_eq!(account.into_account(), expected_account); + } + #[test] + fn test_account_derivation_2() { + // Test from https://asecuritysite.com/encryption/ethadd + let secret_key = hex::decode( + "0f02ba4d7f83e59eaa32eae9c3c4d99b68ce76decade21cdab7ecce8f4aef81a", + ) + .unwrap(); + let mut expected_hex_account = [0u8; 20]; + hex::decode_to_slice( + "420e9f260b40af7e49440cead3069f8e82a5230f", + &mut expected_hex_account, + ) + .expect("example data is 20 bytes of valid hex"); + + let public_key = ecdsa::Pair::from_seed_slice(&secret_key).unwrap().public(); + let account: EthereumSigner = public_key.into(); + let expected_account = AccountId20::from(expected_hex_account); + assert_eq!(account.into_account(), expected_account); + } + #[test] + fn test_account_derivation_3() { + let m = hex::decode( + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + ) + .unwrap(); + let old = + AccountId20(H160::from(H256::from_slice(Keccak256::digest(&m).as_slice())).0); + let new = + AccountId20(H160::from_slice(&Keccak256::digest(&m).as_slice()[12..32]).0); + assert_eq!(new, old); + } +} diff --git a/crates/transcode/src/lib.rs b/crates/transcode/src/lib.rs index 38aa7c3fc..0edd08fd8 100644 --- a/crates/transcode/src/lib.rs +++ b/crates/transcode/src/lib.rs @@ -99,6 +99,7 @@ //! ``` mod account_id; +mod account_id20; mod decode; mod encode; pub mod env_types; @@ -108,6 +109,11 @@ mod util; pub use self::{ account_id::AccountId32, + account_id20::{ + AccountId20, + EthereumSignature, + EthereumSigner, + }, scon::{ Hex, Map, From 886266cccec072a6c9a6458311173fdb273a085f Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Thu, 22 Feb 2024 23:23:30 +0100 Subject: [PATCH 27/39] Add chain config support --- crates/cargo-contract/Cargo.toml | 2 - crates/cargo-contract/src/cmd/call.rs | 160 +++++++++-------- crates/cargo-contract/src/cmd/info.rs | 55 +++--- crates/cargo-contract/src/cmd/instantiate.rs | 164 ++++++++++++------ crates/cargo-contract/src/cmd/mod.rs | 101 +++++------ crates/cargo-contract/src/cmd/remove.rs | 105 ++++++++---- crates/cargo-contract/src/cmd/storage.rs | 52 +++--- crates/cargo-contract/src/cmd/upload.rs | 96 +++++++---- crates/cargo-contract/src/config.rs | 170 +++++++++++++++---- crates/cargo-contract/src/main.rs | 84 ++------- crates/extrinsics/Cargo.toml | 1 - crates/transcode/src/lib.rs | 6 - 12 files changed, 590 insertions(+), 406 deletions(-) diff --git a/crates/cargo-contract/Cargo.toml b/crates/cargo-contract/Cargo.toml index 10f5e8c4a..25ea3eed5 100644 --- a/crates/cargo-contract/Cargo.toml +++ b/crates/cargo-contract/Cargo.toml @@ -47,8 +47,6 @@ subxt = { version = "0.34.0", features = ["substrate-compat"] } sp-core = "28.0.0" sp-weights = "27.0.0" hex = "0.4.3" -subxt-signer = { version = "0.34.0", features = ["subxt", "sr25519"] } -sp-keyring = "31.0.0" [build-dependencies] anyhow = "1.0.79" diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index ecffa726d..23a473ddd 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -14,20 +14,34 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use crate::{config::SignerConfig, ErrorVariant}; +use crate::{ + call_with_config, + config::SignerConfig, + ErrorVariant, +}; use contract_build::util::DEFAULT_KEY_COL_WIDTH; -use ink_env::{ - DefaultEnvironment, - Environment, -}; -use subxt::{tx::PairSigner, PolkadotConfig}; +use ink_env::Environment; use serde::Serialize; -use subxt_signer::{sr25519::{Keypair, PublicKey}, SecretUri}; -use std::{fmt::{Debug, Display}, str::FromStr}; +use std::{ + fmt::{ + Debug, + Display, + }, + str::FromStr, +}; use super::{ - create_signer, create_signer2, display_contract_exec_result, display_contract_exec_result_debug, display_dry_run_result_warning, print_dry_running_status, print_gas_required_success, prompt_confirm_tx, CLIExtrinsicOpts, MAX_KEY_COL_WIDTH + display_contract_exec_result, + display_contract_exec_result_debug, + display_dry_run_result_warning, + parse_account, + parse_balance, + print_dry_running_status, + print_gas_required_success, + prompt_confirm_tx, + CLIExtrinsicOpts, + MAX_KEY_COL_WIDTH, }; use anyhow::{ anyhow, @@ -37,7 +51,6 @@ use anyhow::{ use contract_build::name_value_println; use contract_extrinsics::{ pallet_contracts_primitives::StorageDeposit, - BalanceVariant, CallCommandBuilder, CallExec, DisplayEvents, @@ -47,22 +60,19 @@ use contract_extrinsics::{ use contract_transcode::Value; use sp_weights::Weight; use subxt::{ - ext::{scale_decode::IntoVisitor, scale_encode::EncodeAsType}, tx::Signer, Config + config::ExtrinsicParams, + ext::{ + scale_decode::IntoVisitor, + scale_encode::EncodeAsType, + }, + Config, }; #[derive(Debug, clap::Args)] #[clap(name = "call", about = "Call a contract")] -pub struct CallCommand -where -::AccountId: Sync + Send + FromStr, -<::AccountId as FromStr>::Err: - Into>, -::Balance: Sync + Send +From + Debug + FromStr, - <::Balance as FromStr>::Err: - Into>, -{ +pub struct CallCommand { /// The address of the the contract to call. #[clap(name = "contract", long, env = "CONTRACT")] - contract: ::AccountId, + contract: String, /// The name of the contract message to call. #[clap(long, short)] message: String, @@ -70,7 +80,7 @@ where #[clap(long, num_args = 0..)] args: Vec, #[clap(flatten)] - extrinsic_cli_opts: CLIExtrinsicOpts, + extrinsic_cli_opts: CLIExtrinsicOpts, /// Maximum amount of gas (execution time) to be used for this command. /// If not specified will perform a dry-run to estimate the gas consumed for the /// call. @@ -83,60 +93,70 @@ where proof_size: Option, /// The value to be transferred as part of the call. #[clap(name = "value", long, default_value = "0")] - value: BalanceVariant<::Balance>, + value: String, /// Export the call output in JSON format. #[clap(long, conflicts_with = "verbose")] output_json: bool, + /// The chain config to be used as part of the call. + #[clap(name = "config", long, default_value = "Polkadot")] + config: String, } -impl >CallCommand -where -::AccountId: Sync + Send + FromStr + IntoVisitor + EncodeAsType, -<::AccountId as FromStr>::Err: - Into>, -::Balance: Sync + Send + From + Display + Default + Debug + FromStr, -<::Balance as FromStr>::Err: - Into>, -<::ExtrinsicParams as subxt::config::ExtrinsicParams>::OtherParams: Default, -{ +impl CallCommand { /// Returns whether to export the call output in JSON format. pub fn output_json(&self) -> bool { self.output_json } pub async fn handle(&self) -> Result<(), ErrorVariant> { + call_with_config!(self, run, self.config.as_str()) + } -// let keypair = sp_core::sr25519::Pair::from_string("vessel ladder alter error federal sibling chat ability sun glass valve picture/0/1///Password", None).unwrap(); -// let signer = PairSigner::::new(keypair); - - let uri = ::from_str("//Alice").unwrap(); - let signer = Keypair::from_uri(&uri).unwrap(); - + async fn run>( + &self, + ) -> Result<(), ErrorVariant> + where + >::Signer: subxt::tx::Signer + Clone + FromStr, + ::AccountId: IntoVisitor + FromStr + EncodeAsType, + <::AccountId as FromStr>::Err: Display, + C::Balance: From + Display + Default + FromStr + Serialize + Debug, + >::OtherParams: Default, + { + let contract = parse_account(&self.contract) + .map_err(|e| anyhow::anyhow!("Failed to parse contract option: {}", e))?; + let signer = C::Signer::from_str(&self.extrinsic_cli_opts.suri) + .map_err(|_| anyhow::anyhow!("Failed to parse suri option"))?; let token_metadata = TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let signer = create_signer2::(signer/*&self.extrinsic_cli_opts.suri*/)?; + let storage_deposit_limit = self + .extrinsic_cli_opts + .storage_deposit_limit + .clone() + .map(|b| parse_balance(&b, &token_metadata)) + .transpose() + .map_err(|e| { + anyhow::anyhow!("Failed to parse storage_deposit_limit option: {}", e) + })?; + + let value = parse_balance(&self.value, &token_metadata) + .map_err(|e| anyhow::anyhow!("Failed to parse value option: {}", e))?; + + // 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()) - .storage_deposit_limit( - self.extrinsic_cli_opts - .storage_deposit_limit - .clone() - .map(|bv| bv.denominate_balance(&token_metadata)) - .transpose()?, - ) + .storage_deposit_limit(storage_deposit_limit) .verbosity(self.extrinsic_cli_opts.verbosity()?) .done(); - 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 call_exec = CallCommandBuilder::new(contract, &self.message, extrinsic_opts) + .args(self.args.clone()) + .gas_limit(self.gas_limit) + .proof_size(self.proof_size) + .value(value) + .done() + .await?; let metadata = call_exec.client().metadata(); if !self.extrinsic_cli_opts.execute { @@ -164,7 +184,7 @@ where println!("{}", dry_run_result.to_json()?); } else { dry_run_result.print(); - display_contract_exec_result_debug::<_, DEFAULT_KEY_COL_WIDTH>( + display_contract_exec_result_debug::<_, DEFAULT_KEY_COL_WIDTH, _>( &result, )?; display_dry_run_result_warning("message"); @@ -176,7 +196,7 @@ where return Err(object) } else { name_value_println!("Result", object, MAX_KEY_COL_WIDTH); - display_contract_exec_result::<_, MAX_KEY_COL_WIDTH>(&result)?; + display_contract_exec_result::<_, MAX_KEY_COL_WIDTH, _>(&result)?; } } } @@ -207,10 +227,8 @@ where })?; } let events = call_exec.call(Some(gas_limit)).await?; - let display_events = DisplayEvents::from_events::< - C, - C, - >(&events, None, &metadata)?; + let display_events = + DisplayEvents::from_events::(&events, None, &metadata)?; let output = if self.output_json() { display_events.to_json()? @@ -227,11 +245,17 @@ where } /// A helper function to estimate the gas required for a contract call. -async fn pre_submit_dry_run_gas_estimate_call>( - call_exec: &CallExec>::Signer>, +async fn pre_submit_dry_run_gas_estimate_call( + call_exec: &CallExec, output_json: bool, skip_dry_run: bool, -) -> Result { +) -> Result +where + Signer: subxt::tx::Signer + Clone, + ::AccountId: IntoVisitor + EncodeAsType, + C::Balance: Debug, + >::OtherParams: Default, +{ if skip_dry_run { return match (call_exec.gas_limit(), call_exec.proof_size()) { (Some(ref_time), Some(proof_size)) => Ok(Weight::from_parts(ref_time, proof_size)), @@ -267,7 +291,7 @@ async fn pre_submit_dry_run_gas_estimate_call(&call_result)?; + 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.")) } @@ -277,8 +301,7 @@ async fn pre_submit_dry_run_gas_estimate_call -{ +pub struct CallDryRunResult { /// Was the operation reverted pub reverted: bool, pub data: Value, @@ -288,8 +311,7 @@ pub struct CallDryRunResult pub storage_deposit: StorageDeposit, } -impl CallDryRunResult -{ +impl CallDryRunResult { /// Returns a result in json format pub fn to_json(&self) -> Result { Ok(serde_json::to_string_pretty(self)?) diff --git a/crates/cargo-contract/src/cmd/info.rs b/crates/cargo-contract/src/cmd/info.rs index 66966cacf..1c0699ef2 100644 --- a/crates/cargo-contract/src/cmd/info.rs +++ b/crates/cargo-contract/src/cmd/info.rs @@ -14,9 +14,12 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . +use crate::call_with_config; + use super::{ basic_display_format_extended_contract_info, display_all_contracts, + parse_account, }; use anyhow::Result; use contract_analyze::determine_language; @@ -54,20 +57,10 @@ use subxt::{ #[derive(Debug, clap::Args)] #[clap(name = "info", about = "Get infos from a contract")] -pub struct InfoCommand -where - ::AccountId: Sync + Send + FromStr, - <::AccountId as FromStr>::Err: - Into>, -{ +pub struct InfoCommand { /// The address of the contract to display info of. - #[clap( - name = "contract", - long, - env = "CONTRACT", - required_unless_present = "all" - )] - contract: Option<::AccountId>, + #[clap(name = "contract", long, env = "CONTRACT", conflicts_with = "all")] + contract: String, /// Websockets url of a substrate node. #[clap( name = "url", @@ -85,21 +78,30 @@ where /// Display all contracts addresses #[clap(name = "all", long)] all: bool, + /// The chain config to be used as part of the call. + #[clap(name = "config", long, default_value = "Polkadot")] + config: String, } -impl InfoCommand -where - ::AccountId: - Serialize + Display + IntoVisitor + Decode + AsRef<[u8]> + Send + Sync + FromStr, - ::Hash: IntoVisitor + Display, - ::Balance: Serialize + Debug + IntoVisitor, - <::AccountId as FromStr>::Err: - Into>, -{ - pub async fn run(&self) -> Result<(), ErrorVariant> { +impl InfoCommand { + pub async fn handle(&self) -> Result<(), ErrorVariant> { + call_with_config!(self, run, self.config.as_str()) + } + + pub async fn run(&self) -> Result<(), ErrorVariant> + where + ::AccountId: + Serialize + Display + IntoVisitor + Decode + AsRef<[u8]> + FromStr, + ::Hash: IntoVisitor + Display, + ::Balance: Serialize + Debug + IntoVisitor, + <::AccountId as FromStr>::Err: + Into> + Display, + { let rpc_cli = RpcClient::from_url(url_to_string(&self.url)).await?; let client = OnlineClient::::from_rpc_client(rpc_cli.clone()).await?; let rpc = LegacyRpcMethods::::new(rpc_cli.clone()); + let contract = parse_account(&self.contract) + .map_err(|e| anyhow::anyhow!("Failed to parse contract option: {}", e))?; // All flag applied if self.all { @@ -117,13 +119,8 @@ where } else { // Contract arg shall be always present in this case, it is enforced by // clap configuration - let contract = self - .contract - .as_ref() - .expect("Contract argument was not provided"); - let info_to_json = - fetch_contract_info::(contract, &rpc, &client).await?; + fetch_contract_info::(&contract, &rpc, &client).await?; let wasm_code = fetch_wasm_code(&client, &rpc, info_to_json.code_hash()).await?; diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index e75a91059..a9648384f 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -15,10 +15,10 @@ // 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, + parse_balance, print_dry_running_status, print_gas_required_success, prompt_confirm_tx, @@ -26,7 +26,12 @@ use super::{ MAX_KEY_COL_WIDTH, }; use crate::{ - anyhow, config::PolkadotBaseConfig, ErrorVariant, InstantiateExec, Weight + anyhow, + call_with_config, + config::SignerConfig, + ErrorVariant, + InstantiateExec, + Weight, }; use anyhow::Result; use contract_build::{ @@ -38,7 +43,6 @@ use contract_build::{ Verbosity, }; use contract_extrinsics::{ - BalanceVariant, Code, DisplayEvents, ExtrinsicOptsBuilder, @@ -47,14 +51,25 @@ use contract_extrinsics::{ InstantiateExecResult, TokenMetadata, }; -use ink_env::{ - DefaultEnvironment, - Environment, -}; +use ink_env::Environment; +use serde::Serialize; use sp_core::Bytes; -use std::fmt::Debug; -use subxt::PolkadotConfig as DefaultConfig; -use subxt_signer::sr25519::Keypair; +use std::{ + fmt::{ + Debug, + Display, + }, + str::FromStr, +}; +use subxt::{ + config::ExtrinsicParams, + ext::{ + codec::Decode, + scale_decode::IntoVisitor, + scale_encode::EncodeAsType, + }, + Config, +}; #[derive(Debug, clap::Args)] pub struct InstantiateCommand { @@ -65,10 +80,10 @@ pub struct InstantiateCommand { #[clap(long, num_args = 0..)] args: Vec, #[clap(flatten)] - extrinsic_cli_opts: CLIExtrinsicOpts, + extrinsic_cli_opts: CLIExtrinsicOpts, /// Transfers an initial balance to the instantiated contract #[clap(name = "value", long, default_value = "0")] - value: BalanceVariant<::Balance>, + value: String, /// Maximum amount of gas to be used for this command. /// If not specified will perform a dry-run to estimate the gas consumed for the /// instantiation. @@ -85,6 +100,9 @@ pub struct InstantiateCommand { /// Export the instantiate output in JSON format. #[clap(long, conflicts_with = "verbose")] output_json: bool, + /// The chain config to be used as part of the call. + #[clap(name = "config", long, default_value = "Polkadot")] + config: String, } /// Parse hex encoded bytes. @@ -100,35 +118,54 @@ impl InstantiateCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { + call_with_config!(self, run, self.config.as_str()) + } + + async fn run>( + &self, + ) -> Result<(), ErrorVariant> + where + >::Signer: subxt::tx::Signer + Clone + FromStr, + ::AccountId: IntoVisitor + FromStr + EncodeAsType + Decode + Display, + <::AccountId as FromStr>::Err: Display, + C::Balance: From + Display + Default + FromStr + Serialize + Debug, + >::OtherParams: Default, + ::Hash: From<[u8; 32]> + IntoVisitor + EncodeAsType, + { + let signer = C::Signer::from_str(&self.extrinsic_cli_opts.suri) + .map_err(|_| anyhow::anyhow!("Failed to parse suri option"))?; let token_metadata = - TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + + let storage_deposit_limit = self + .extrinsic_cli_opts + .storage_deposit_limit + .clone() + .map(|b| parse_balance(&b, &token_metadata)) + .transpose() + .map_err(|e| { + anyhow::anyhow!("Failed to parse storage_deposit_limit option: {}", e) + })?; + + let value = parse_balance(&self.value, &token_metadata) + .map_err(|e| anyhow::anyhow!("Failed to parse value option: {}", e))?; - 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()) - .storage_deposit_limit( - self.extrinsic_cli_opts - .storage_deposit_limit - .clone() - .map(|bv| bv.denominate_balance(&token_metadata)) - .transpose()?, - ) + .storage_deposit_limit(storage_deposit_limit) .done(); - 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?; + let instantiate_exec: InstantiateExec = + InstantiateCommandBuilder::new(extrinsic_opts) + .constructor(self.constructor.clone()) + .args(self.args.clone()) + .value(value) + .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?; @@ -138,7 +175,7 @@ impl InstantiateCommand { println!("{}", dry_run_result.to_json()?); } else { print_instantiate_dry_run_result(&dry_run_result); - display_contract_exec_result_debug::<_, DEFAULT_KEY_COL_WIDTH>( + display_contract_exec_result_debug::<_, DEFAULT_KEY_COL_WIDTH, _>( &result, )?; display_dry_run_result_warning("instantiate"); @@ -150,7 +187,7 @@ impl InstantiateCommand { return Err(object) } else { name_value_println!("Result", object, MAX_KEY_COL_WIDTH); - display_contract_exec_result::<_, MAX_KEY_COL_WIDTH>(&result)?; + display_contract_exec_result::<_, MAX_KEY_COL_WIDTH, _>(&result)?; } Err(object) } @@ -193,11 +230,20 @@ 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, +async fn pre_submit_dry_run_gas_estimate_instantiate< + C: Config + Environment + SignerConfig, +>( + instantiate_exec: &InstantiateExec, output_json: bool, skip_dry_run: bool, -) -> Result { +) -> Result +where + C::Signer: subxt::tx::Signer + Clone, + ::AccountId: IntoVisitor + Display + Decode, + ::Hash: IntoVisitor + EncodeAsType, + C::Balance: Serialize + Debug, + >::OtherParams: Default, +{ if skip_dry_run { return match (instantiate_exec.args().gas_limit(), instantiate_exec.args().proof_size()) { (Some(ref_time), Some(proof_size)) => Ok(Weight::from_parts(ref_time, proof_size)), @@ -237,7 +283,7 @@ async fn pre_submit_dry_run_gas_estimate_instantiate( Err(anyhow!("{}", serde_json::to_string_pretty(&object)?)) } else { name_value_println!("Result", object, MAX_KEY_COL_WIDTH); - display_contract_exec_result::<_, MAX_KEY_COL_WIDTH>( + display_contract_exec_result::<_, MAX_KEY_COL_WIDTH, _>( &instantiate_result, )?; @@ -249,14 +295,21 @@ 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_result: InstantiateExecResult, +pub async fn display_result>( + instantiate_exec: &InstantiateExec, + instantiate_exec_result: InstantiateExecResult, token_metadata: &TokenMetadata, output_json: bool, verbosity: Verbosity, -) -> Result<(), ErrorVariant> { - let events = DisplayEvents::from_events::( +) -> Result<(), ErrorVariant> +where + C::Signer: subxt::tx::Signer + Clone, + ::AccountId: IntoVisitor + EncodeAsType + Display + Decode, + ::Hash: IntoVisitor + EncodeAsType, + C::Balance: Serialize + From + Display, + >::OtherParams: Default, +{ + let events = DisplayEvents::from_events::( &instantiate_exec_result.events, Some(instantiate_exec.transcoder()), &instantiate_exec.client().metadata(), @@ -272,10 +325,7 @@ pub async fn display_result( }; println!("{}", display_instantiate_result.to_json()?) } else { - println!( - "{}", - events.display_events::(verbosity, token_metadata)? - ); + println!("{}", events.display_events::(verbosity, token_metadata)?); if let Some(code_hash) = instantiate_exec_result.code_hash { name_value_println!("Code hash", format!("{code_hash:?}")); } @@ -284,10 +334,16 @@ pub async fn display_result( Ok(()) } -pub fn print_default_instantiate_preview( - instantiate_exec: &InstantiateExec, +pub fn print_default_instantiate_preview>( + instantiate_exec: &InstantiateExec, gas_limit: Weight, -) { +) where + C::Signer: subxt::tx::Signer + Clone, + ::AccountId: IntoVisitor + EncodeAsType + Display + Decode, + ::Hash: IntoVisitor + EncodeAsType, + C::Balance: Serialize, + >::OtherParams: Default, +{ name_value_println!( "Constructor", instantiate_exec.args().constructor(), @@ -320,8 +376,8 @@ impl InstantiateResult { } } -pub fn print_instantiate_dry_run_result( - result: &InstantiateDryRunResult<::Balance>, +pub fn print_instantiate_dry_run_result( + result: &InstantiateDryRunResult, ) { name_value_println!( "Result", diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index 786c0dfa4..29c0908c8 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -26,8 +26,7 @@ pub mod schema; pub mod storage; pub mod upload; pub mod verify; -use subxt::tx::PairSigner; -use sp_core::Pair; + pub(crate) use self::{ build::{ BuildCommand, @@ -71,32 +70,24 @@ pub(crate) use contract_extrinsics::ErrorVariant; use contract_extrinsics::{ pallet_contracts_primitives::ContractResult, BalanceVariant, + TokenMetadata, }; -use subxt::{tx, Config}; -use core::fmt; -use ink_env::{ - DefaultEnvironment, - Environment, -}; + use std::{ - fmt::{Debug, Display}, + fmt::{ + Debug, + Display, + }, io::{ self, Write, - }, str::FromStr, -}; -use subxt_signer::{ - sr25519::Keypair, - SecretUri, + }, + str::FromStr, }; /// Arguments required for creating and sending an extrinsic to a substrate node. #[derive(Clone, Debug, clap::Args)] -pub struct CLIExtrinsicOpts -where ::Balance: Send + Sync + Debug + FromStr, -<::Balance as FromStr>::Err: - Into>, -{ +pub struct CLIExtrinsicOpts { /// Path to a contract build artifact file: a raw `.wasm` file, a `.contract` bundle, /// or a `.json` metadata file. #[clap(value_parser, conflicts_with = "manifest_path")] @@ -127,8 +118,7 @@ where ::Balance: Send + Sync + Debug + FromStr, /// The maximum amount of balance that can be charged from the caller to pay for the /// storage. consumed. #[clap(long)] - storage_deposit_limit: - Option::Balance>>, + storage_deposit_limit: Option, /// Before submitting a transaction, do not dry-run it via RPC first. #[clap(long)] skip_dry_run: bool, @@ -137,11 +127,7 @@ where ::Balance: Send + Sync + Debug + FromStr, skip_confirm: bool, } -impl CLIExtrinsicOpts -where ::Balance: Send + Sync + Debug + FromStr, -<::Balance as FromStr>::Err: - Into>, -{ +impl CLIExtrinsicOpts { /// Returns the verbosity pub fn verbosity(&self) -> Result { TryFrom::try_from(&self.verbosity) @@ -152,9 +138,12 @@ const STORAGE_DEPOSIT_KEY: &str = "Storage Total Deposit"; pub const MAX_KEY_COL_WIDTH: usize = STORAGE_DEPOSIT_KEY.len() + 1; /// Print to stdout the fields of the result of a `instantiate` or `call` dry-run via RPC. -pub fn display_contract_exec_result( - result: &ContractResult::Balance, ()>, -) -> Result<()> { +pub fn display_contract_exec_result( + result: &ContractResult, +) -> Result<()> +where + Balance: Debug, +{ let mut debug_message_lines = std::str::from_utf8(&result.debug_message) .context("Error decoding UTF8 debug message bytes")? .lines(); @@ -177,8 +166,8 @@ pub fn display_contract_exec_result( Ok(()) } -pub fn display_contract_exec_result_debug( - result: &ContractResult::Balance, ()>, +pub fn display_contract_exec_result_debug( + result: &ContractResult, ) -> Result<()> { let mut debug_message_lines = std::str::from_utf8(&result.debug_message) .context("Error decoding UTF8 debug message bytes")? @@ -247,8 +236,8 @@ pub fn print_gas_required_success(gas: Weight) { pub fn basic_display_format_extended_contract_info( info: &ExtendedContractInfo, ) where - Hash: fmt::Debug, - Balance: fmt::Debug, + Hash: Debug, + Balance: Debug, { name_value_println!("TrieId", info.trie_id, MAX_KEY_COL_WIDTH); name_value_println!( @@ -286,32 +275,29 @@ where contracts.iter().for_each(|e: &AccountId| println!("{}", e)) } -/// Create a Signer from a secret URI. -pub fn create_signer2(signer: Signer/*suri: &str*/) -> Result -where Signer: tx::Signer,// C::AccountId: From, -//::AccountId: std::convert::From, -//C::Address: From, -//Pair: sp_core::Pair -{ - // let uri = ::from_str(suri)?; - // let keypair = Keypair::from_uri(&uri)?; - //let keypair = sp_core::sr25519::Pair::from_string("vessel ladder alter error federal sibling chat ability sun glass valve picture/0/1///Password", None).unwrap(); - //let keypair = PairSigner::::new(keypair); - Ok(signer) +/// Parse a balance from string format +pub fn parse_balance + Clone>( + balance: &str, + token_metadata: &TokenMetadata, +) -> Result { + BalanceVariant::from_str(&balance) + .map_err(|e| anyhow!("Balance parsing failed: {e}")) + .and_then(|bv| bv.denominate_balance(&token_metadata)) } -/// Create a Signer from a secret URI. -pub fn create_signer(suri: &str) -> Result +/// Parse a account from string format +pub fn parse_account(account: &str) -> Result +where + ::Err: Display, { - let uri = ::from_str(suri)?; - let signer = Keypair::from_uri(&uri)?; - Ok(signer) + AccountId::from_str(account) + .map_err(|e| anyhow::anyhow!("Account address parsing failed: {e}")) } /// Parse a hex encoded 32 byte hash. Returns error if not exactly 32 bytes. pub fn parse_code_hash(input: &str) -> Result where - Hash: std::convert::From<[u8; 32]>, + Hash: From<[u8; 32]>, { let bytes = contract_build::util::decode_hex(input)?; if bytes.len() != 32 { @@ -324,17 +310,22 @@ where #[cfg(test)] mod tests { + use subxt::{ + Config, + SubstrateConfig, + }; + use super::*; #[test] fn parse_code_hash_works() { // with 0x prefix - assert!(parse_code_hash( + assert!(parse_code_hash::<::Hash>( "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" ) .is_ok()); // without 0x prefix - assert!(parse_code_hash( + assert!(parse_code_hash::<::Hash>( "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" ) .is_ok()) @@ -343,7 +334,7 @@ mod tests { #[test] fn parse_incorrect_len_code_hash_fails() { // with len not equal to 32 - assert!(parse_code_hash( + assert!(parse_code_hash::<::Hash>( "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da2" ) .is_err()) @@ -352,7 +343,7 @@ mod tests { #[test] fn parse_bad_format_code_hash_fails() { // with bad format - assert!(parse_code_hash( + assert!(parse_code_hash::<::Hash>( "x43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" ) .is_err()) diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index fa997fb55..665c87ae9 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -14,11 +14,21 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use crate::{config::PolkadotBaseConfig, ErrorVariant}; -use std::fmt::Debug; +use crate::{ + call_with_config, + config::SignerConfig, + ErrorVariant, +}; +use std::{ + fmt::{ + Debug, + Display, + }, + str::FromStr, +}; use super::{ - create_signer, + parse_balance, parse_code_hash, CLIExtrinsicOpts, }; @@ -31,24 +41,30 @@ use contract_extrinsics::{ RemoveExec, TokenMetadata, }; -use ink_env::DefaultEnvironment; +use ink_env::Environment; +use serde::Serialize; use subxt::{ + config::ExtrinsicParams, + ext::{ + scale_decode::IntoVisitor, + scale_encode::EncodeAsType, + }, Config, - PolkadotConfig as DefaultConfig, }; -use subxt_signer::sr25519::Keypair; - #[derive(Debug, clap::Args)] #[clap(name = "remove", about = "Remove a contract's code")] pub struct RemoveCommand { /// The hash of the smart contract code already uploaded to the chain. - #[clap(long, value_parser = parse_code_hash::<::Hash>)] - code_hash: Option<::Hash>, + #[clap(long)] + code_hash: Option, #[clap(flatten)] - extrinsic_cli_opts: CLIExtrinsicOpts, + extrinsic_cli_opts: CLIExtrinsicOpts, /// Export the call output as JSON. #[clap(long, conflicts_with = "verbose")] output_json: bool, + /// The chain config to be used as part of the call. + #[clap(name = "config", long, default_value = "Polkadot")] + config: String, } impl RemoveCommand { @@ -58,44 +74,69 @@ impl RemoveCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { + call_with_config!(self, run, self.config.as_str()) + } + + async fn run>( + &self, + ) -> Result<(), ErrorVariant> + where + >::Signer: subxt::tx::Signer + Clone + FromStr, + ::AccountId: IntoVisitor + FromStr + EncodeAsType, + <::AccountId as FromStr>::Err: Display, + C::Balance: + Into + From + Display + Default + FromStr + Serialize + Debug, + >::OtherParams: Default, + ::Hash: IntoVisitor + EncodeAsType + From<[u8; 32]>, + { + let signer = C::Signer::from_str(&self.extrinsic_cli_opts.suri) + .map_err(|_| anyhow::anyhow!("Failed to parse suri option"))?; let token_metadata = - TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + + let storage_deposit_limit = self + .extrinsic_cli_opts + .storage_deposit_limit + .clone() + .map(|b| parse_balance(&b, &token_metadata)) + .transpose() + .map_err(|e| { + anyhow::anyhow!("Failed to parse storage_deposit_limit option: {}", e) + })?; + + let code_hash = self + .code_hash + .clone() + .map(|h| parse_code_hash(&h)) + .transpose() + .map_err(|e| anyhow::anyhow!("Failed to parse code_hash option: {}", e))?; - 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()) - .storage_deposit_limit( - self.extrinsic_cli_opts - .storage_deposit_limit - .clone() - .map(|bv| bv.denominate_balance(&token_metadata)) - .transpose()?, - ) + .storage_deposit_limit(storage_deposit_limit) .done(); - let remove_exec: RemoveExec = - RemoveCommandBuilder::new(extrinsic_opts) - .code_hash(self.code_hash) - .done() - .await?; + let remove_exec: RemoveExec = RemoveCommandBuilder::new(extrinsic_opts) + .code_hash(code_hash) + .done() + .await?; let remove_result = remove_exec.remove_code().await?; - let display_events = - DisplayEvents::from_events::( - &remove_result.events, - Some(remove_exec.transcoder()), - &remove_exec.client().metadata(), - )?; + 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::( + display_events.display_events::( self.extrinsic_cli_opts.verbosity().unwrap(), &token_metadata, )? }; if let Some(code_removed) = remove_result.code_removed { - let remove_result: ::Hash = code_removed.code_hash; + let remove_result: ::Hash = code_removed.code_hash; if self.output_json() { // Create a JSON object with the events and the removed code hash. diff --git a/crates/cargo-contract/src/cmd/storage.rs b/crates/cargo-contract/src/cmd/storage.rs index 04f73b24d..cfaff1f5f 100644 --- a/crates/cargo-contract/src/cmd/storage.rs +++ b/crates/cargo-contract/src/cmd/storage.rs @@ -39,17 +39,16 @@ use subxt::{ Config, }; +use crate::call_with_config; + +use super::parse_account; + #[derive(Debug, clap::Args)] #[clap(name = "storage", about = "Inspect contract storage")] -pub struct StorageCommand -where - ::AccountId: Sync + Send + FromStr, - <::AccountId as FromStr>::Err: - Into>, -{ +pub struct StorageCommand { /// The address of the contract to inspect storage of. #[clap(name = "contract", long, env = "CONTRACT")] - contract: ::AccountId, + contract: String, /// Fetch the "raw" storage keys and values for the contract. #[clap(long)] raw: bool, @@ -71,24 +70,32 @@ where default_value = "ws://localhost:9944" )] url: url::Url, + /// The chain config to be used as part of the call. + #[clap(name = "config", long, default_value = "Polkadot")] + config: String, } -impl StorageCommand -where - ::AccountId: Display + IntoVisitor + AsRef<[u8]> + Send + Sync + FromStr, - <::AccountId as FromStr>::Err: - Into>, - ::Balance: Serialize + IntoVisitor, - ::Hash: IntoVisitor, -{ - pub async fn run(&self) -> Result<(), ErrorVariant> { +impl StorageCommand { + pub async fn handle(&self) -> Result<(), ErrorVariant> { + call_with_config!(self, run, self.config.as_str()) + } + + pub async fn run(&self) -> Result<(), ErrorVariant> + where + ::AccountId: Display + IntoVisitor + AsRef<[u8]> + FromStr, + <::AccountId as FromStr>::Err: + Into> + Display, + C::Balance: Serialize + IntoVisitor, + ::Hash: IntoVisitor, + { let rpc = ContractStorageRpc::::new(&self.url).await?; let storage_layout = ContractStorage::::new(rpc); + let contract = parse_account(&self.contract) + .map_err(|e| anyhow::anyhow!("Failed to parse contract option: {}", e))?; if self.raw { - let storage_data = storage_layout - .load_contract_storage_data(&self.contract) - .await?; + let storage_data = + storage_layout.load_contract_storage_data(&contract).await?; println!( "{json}", json = serde_json::to_string_pretty(&storage_data)? @@ -105,7 +112,7 @@ where Ok(contract_artifacts) => { let transcoder = contract_artifacts.contract_transcoder()?; let contract_storage = storage_layout - .load_contract_storage_with_layout(&self.contract, &transcoder) + .load_contract_storage_with_layout(&contract, &transcoder) .await?; if self.output_json { println!( @@ -122,9 +129,8 @@ where "{} Displaying raw storage: no valid contract metadata artifacts found", "Info:".cyan().bold(), ); - let storage_data = storage_layout - .load_contract_storage_data(&self.contract) - .await?; + let storage_data = + storage_layout.load_contract_storage_data(&contract).await?; println!( "{json}", json = serde_json::to_string_pretty(&storage_data)? diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index 504f81909..26bca0ea1 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -14,12 +14,22 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use crate::{config::PolkadotBaseConfig, ErrorVariant}; -use std::fmt::Debug; +use crate::{ + call_with_config, + config::SignerConfig, + ErrorVariant, +}; +use std::{ + fmt::{ + Debug, + Display, + }, + str::FromStr, +}; use super::{ - create_signer, display_dry_run_result_warning, + parse_balance, CLIExtrinsicOpts, }; use anyhow::Result; @@ -31,24 +41,28 @@ use contract_extrinsics::{ UploadCommandBuilder, UploadExec, }; -use ink_env::{ - DefaultEnvironment, - Environment, -}; +use ink_env::Environment; +use serde::Serialize; use subxt::{ + config::ExtrinsicParams, + ext::{ + scale_decode::IntoVisitor, + scale_encode::EncodeAsType, + }, Config, - PolkadotConfig as DefaultConfig, }; -use subxt_signer::sr25519::Keypair; #[derive(Debug, clap::Args)] #[clap(name = "upload", about = "Upload a contract's code")] pub struct UploadCommand { #[clap(flatten)] - extrinsic_cli_opts: CLIExtrinsicOpts, + extrinsic_cli_opts: CLIExtrinsicOpts, /// Export the call output in JSON format. #[clap(long, conflicts_with = "verbose")] output_json: bool, + /// The chain config to be used as part of the call. + #[clap(name = "config", long, default_value = "Polkadot")] + config: String, } impl UploadCommand { @@ -58,23 +72,43 @@ impl UploadCommand { } pub async fn handle(&self) -> Result<(), ErrorVariant> { + call_with_config!(self, run, self.config.as_str()) + } + + async fn run>( + &self, + ) -> Result<(), ErrorVariant> + where + >::Signer: subxt::tx::Signer + Clone + FromStr, + ::AccountId: IntoVisitor + FromStr + EncodeAsType, + <::AccountId as FromStr>::Err: Display, + C::Balance: + Into + From + Display + Default + FromStr + Serialize + Debug, + >::OtherParams: Default, + ::Hash: IntoVisitor + EncodeAsType + From<[u8; 32]>, + { + let signer = C::Signer::from_str(&self.extrinsic_cli_opts.suri) + .map_err(|_| anyhow::anyhow!("Failed to parse suri option"))?; let token_metadata = - TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; + + let storage_deposit_limit = self + .extrinsic_cli_opts + .storage_deposit_limit + .clone() + .map(|b| parse_balance(&b, &token_metadata)) + .transpose() + .map_err(|e| { + anyhow::anyhow!("Failed to parse storage_deposit_limit option: {}", e) + })?; - 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()) - .storage_deposit_limit( - self.extrinsic_cli_opts - .storage_deposit_limit - .clone() - .map(|bv| bv.denominate_balance(&token_metadata)) - .transpose()?, - ) + .storage_deposit_limit(storage_deposit_limit) .done(); - let upload_exec: UploadExec = + let upload_exec: UploadExec = UploadCommandBuilder::new(extrinsic_opts).done().await?; let code_hash = upload_exec.code().code_hash(); @@ -106,20 +140,21 @@ impl UploadCommand { } } else { let upload_result = upload_exec.upload_code().await?; - let display_events = DisplayEvents::from_events::< - DefaultConfig, - DefaultEnvironment, - >(&upload_result.events, None, &metadata)?; + let display_events = DisplayEvents::from_events::( + &upload_result.events, + None, + &metadata, + )?; let output_events = if self.output_json() { display_events.to_json()? } else { - display_events.display_events::( + display_events.display_events::( self.extrinsic_cli_opts.verbosity()?, &token_metadata, )? }; if let Some(code_stored) = upload_result.code_stored { - let code_hash: ::Hash = code_stored.code_hash; + let code_hash: ::Hash = code_stored.code_hash; if self.output_json() { // Create a JSON object with the events and the code hash. let json_object = serde_json::json!({ @@ -144,13 +179,16 @@ impl UploadCommand { } #[derive(serde::Serialize)] -pub struct UploadDryRunResult { +pub struct UploadDryRunResult { pub result: String, pub code_hash: String, - pub deposit: ::Balance, + pub deposit: Balance, } -impl UploadDryRunResult { +impl UploadDryRunResult +where + Balance: Debug + Serialize, +{ pub fn to_json(&self) -> Result { Ok(serde_json::to_string_pretty(self)?) } diff --git a/crates/cargo-contract/src/config.rs b/crates/cargo-contract/src/config.rs index 27b5acb20..e58706d83 100644 --- a/crates/cargo-contract/src/config.rs +++ b/crates/cargo-contract/src/config.rs @@ -14,42 +14,41 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use contract_transcode::{ - AccountId20, - EthereumSignature, -}; +use std::str::FromStr; + use ink_env::{ DefaultEnvironment, Environment, }; +use sp_core::Pair; use subxt::{ - config::{ - DefaultExtrinsicParams, - PolkadotExtrinsicParams, + config::PolkadotExtrinsicParams, + tx::{ + PairSigner, + Signer as SignerT, }, utils::MultiAddress, Config, SubstrateConfig, - tx::Signer as SignerT }; -use subxt_signer::sr25519::Keypair; -/// A runtime configuration for the Ethereum based chain like Moonbeam. -/// This thing is not meant to be instantiated; it is just a collection of types. +/// A runtime configuration for the Polkadot based chain. +// /// This thing is not meant to be instantiated; it is just a collection of types. #[derive(Debug, Clone, PartialEq, Eq)] -pub enum EthereumBaseConfig {} -impl Config for EthereumBaseConfig { +pub enum Ecdsachain {} + +impl Config for Ecdsachain { type Hash = ::Hash; - type AccountId = AccountId20; - type Address = AccountId20; - type Signature = EthereumSignature; + type AccountId = ::AccountId; + type Address = MultiAddress; + type Signature = ::Signature; type Hasher = ::Hasher; type Header = ::Header; - type ExtrinsicParams = DefaultExtrinsicParams; + type ExtrinsicParams = PolkadotExtrinsicParams; type AssetId = ::AssetId; } -impl Environment for EthereumBaseConfig { +impl Environment for Ecdsachain { const MAX_EVENT_TOPICS: usize = ::MAX_EVENT_TOPICS; type AccountId = ::AccountId; type Balance = ::Balance; @@ -59,12 +58,19 @@ impl Environment for EthereumBaseConfig { type ChainExtension = ::ChainExtension; } +impl SignerConfig for Ecdsachain +where + ::Signature: From, +{ + type Signer = SignerEcdsa; +} + /// A runtime configuration for the Polkadot based chain. // /// This thing is not meant to be instantiated; it is just a collection of types. #[derive(Debug, Clone, PartialEq, Eq)] +pub enum Polkadot {} -pub enum PolkadotBaseConfig {} -impl Config for PolkadotBaseConfig { +impl Config for Polkadot { type Hash = ::Hash; type AccountId = ::AccountId; type Address = MultiAddress; @@ -72,10 +78,10 @@ impl Config for PolkadotBaseConfig { type Hasher = ::Hasher; type Header = ::Header; type ExtrinsicParams = PolkadotExtrinsicParams; - type AssetId = u32; + type AssetId = ::AssetId; } -impl Environment for PolkadotBaseConfig { +impl Environment for Polkadot { const MAX_EVENT_TOPICS: usize = ::MAX_EVENT_TOPICS; type AccountId = ::AccountId; type Balance = ::Balance; @@ -85,26 +91,120 @@ impl Environment for PolkadotBaseConfig { type ChainExtension = ::ChainExtension; } -pub trait SignerConfig +pub trait SignerConfig { + type Signer: SignerT + FromStr; +} + +impl SignerConfig for Polkadot { + type Signer = SignerSR25519; +} + +/// Struct representing the implementation of the sr25519 signer +#[derive(Clone)] +pub struct SignerSR25519(pub PairSigner); + +impl FromStr for SignerSR25519 +where + ::AccountId: From, +{ + type Err = anyhow::Error; + + /// Attempts to parse the Signer suri string + fn from_str(input: &str) -> Result, Self::Err> { + let keypair = sp_core::sr25519::Pair::from_string(input, None)?; + let signer = PairSigner::::new(keypair); + Ok(Self(signer)) + } +} + +impl SignerT for SignerSR25519 +where + ::Signature: From, +{ + fn account_id(&self) -> ::AccountId { + self.0.account_id().clone() + } + + fn address(&self) -> C::Address { + self.0.address() + } + + fn sign(&self, signer_payload: &[u8]) -> C::Signature { + self.0.sign(signer_payload) + } +} + +/// Struct representing the implementation of the sr25519 signer +#[derive(Clone)] +pub struct SignerEcdsa(pub PairSigner); + +impl FromStr for SignerEcdsa where - Self: Clone, + // Requirements of `PairSigner where: + // T::AccountId: From` + ::AccountId: From, { - type Signer: Clone + SignerT; + type Err = anyhow::Error; + + /// Attempts to parse the Signer suri string + fn from_str(input: &str) -> Result, Self::Err> { + let keypair = sp_core::ecdsa::Pair::from_string(input, None)?; + let signer = PairSigner::::new(keypair); + Ok(Self(signer)) + } } -impl SignerConfig for PolkadotBaseConfig { - type Signer = Keypair; +impl SignerT for SignerEcdsa +where + ::Signature: From, +{ + fn account_id(&self) -> ::AccountId { + self.0.account_id().clone() + } + + fn address(&self) -> C::Address { + self.0.address() + } + + fn sign(&self, signer_payload: &[u8]) -> C::Signature { + self.0.sign(signer_payload) + } } -// impl SignerConfig for EthereumBaseConfig { -// type Signer = (); -// } +#[macro_export] +macro_rules! call_with_config_internal { + ($obj:tt ,$function:tt, $config_name:expr, $($config:ty),*) => { + match $config_name { + $( + stringify!($config) => $obj.$function::<$config>().await, + )* + _ => { -pub enum ChainConfig { - PolkadotBaseConfig, - // EthereumBaseConfig, + let configs = vec![$(stringify!($config)),*].iter() + .map(|s| s.trim_start_matches("crate::config::")) + .collect::>() + .join(", "); + Err(ErrorVariant::Generic( + contract_extrinsics::GenericError::from_message( + format!("Chain configuration not found, Allowed configurations: {configs}") + ))) + }, + } + }; } -pub fn select_config(config: &str) -> ChainConfig { - ChainConfig::PolkadotBaseConfig +/// Macro that allows calling the command member function with chain configuration +#[macro_export] +macro_rules! call_with_config { + ($obj:tt, $function:ident, $config_name:expr) => {{ + let config_name = format!("crate::config::{}", $config_name); + $crate::call_with_config_internal!( + $obj, + $function, + config_name.as_str(), + // All available chain configs need to be specified here + $crate::config::Polkadot, + $crate::config::Ecdsachain + ) + }}; } diff --git a/crates/cargo-contract/src/main.rs b/crates/cargo-contract/src/main.rs index 1862e83f9..564f01d57 100644 --- a/crates/cargo-contract/src/main.rs +++ b/crates/cargo-contract/src/main.rs @@ -47,32 +47,17 @@ use clap::{ }; use cmd::encode::EncodeCommand; use colored::Colorize; -use config::{ - PolkadotBaseConfig, SignerConfig, -}; use contract_build::{ util::DEFAULT_KEY_COL_WIDTH, OutputType, }; use contract_extrinsics::InstantiateExec; -use ink_env::Environment; -use serde::Serialize; use sp_weights::Weight; use std::{ - fmt::{ - Debug, - Display, - }, + fmt::Debug, path::PathBuf, str::FromStr, }; -use subxt::{ - ext::{ - codec::Decode, - scale_decode::IntoVisitor, scale_encode::EncodeAsType, - }, - Config, -}; use tokio::runtime::Runtime; // These crates are only used when we run integration tests `--features // integration-tests`. However since we can't have optional `dev-dependencies` we pretend @@ -93,35 +78,18 @@ use which as _; #[derive(Debug, Parser)] #[clap(bin_name = "cargo")] #[clap(version = env!("CARGO_CONTRACT_CLI_IMPL_VERSION"))] -pub(crate) enum Opts> -where - ::AccountId: Sync + Send + FromStr, - <::AccountId as FromStr>::Err: - Into>, - ::Balance: Sync + Send + From + Debug + FromStr, - <::Balance as FromStr>::Err: - Into>, -{ +pub(crate) enum Opts { /// Utilities to develop Wasm smart contracts. #[clap(name = "contract")] #[clap(version = env!("CARGO_CONTRACT_CLI_IMPL_VERSION"))] #[clap(action = ArgAction::DeriveDisplayOrder)] - Contract(ContractArgs), + Contract(ContractArgs), } #[derive(Debug, Args)] -pub(crate) struct ContractArgs> -where - ::AccountId: Sync + Send + FromStr, - <::AccountId as FromStr>::Err: - Into>, - ::Balance: Sync + Send + From + Debug + FromStr, - <::Balance as FromStr>::Err: - Into>, - -{ +pub(crate) struct ContractArgs { #[clap(subcommand)] - cmd: Command, + cmd: Command, } #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -136,15 +104,7 @@ impl FromStr for HexData { } #[derive(Debug, Subcommand)] -enum Command> -where - ::AccountId: Sync + Send + FromStr, - <::AccountId as FromStr>::Err: - Into>, - ::Balance: Sync + Send + From + Debug + FromStr, - <::Balance as FromStr>::Err: - Into>, -{ +enum Command { /// Setup and create a new smart contract project #[clap(name = "new")] New { @@ -170,7 +130,7 @@ where Instantiate(InstantiateCommand), /// Call a contract #[clap(name = "call")] - Call(CallCommand), + Call(CallCommand), /// Encodes a contracts input calls and their arguments #[clap(name = "encode")] Encode(EncodeCommand), @@ -182,10 +142,10 @@ where Remove(RemoveCommand), /// Display information about a contract #[clap(name = "info")] - Info(InfoCommand), + Info(InfoCommand), /// Inspect the on-chain storage of a contract. #[clap(name = "storage")] - Storage(StorageCommand), + Storage(StorageCommand), /// Verifies that a given contract binary matches the build result of the specified /// workspace. #[clap(name = "verify")] @@ -203,14 +163,8 @@ where fn main() { tracing_subscriber::fmt::init(); - let config = config::select_config("eth"); - - // let Opts::Contract(args) = match config { - // ChainConfig::EthereumBaseConfig => Opts::::parse(), - // ChainConfig::PolkadotBaseConfig => Opts::::parse(), - // }; - let Opts::Contract(args) = Opts::::parse(); + let Opts::Contract(args) = Opts::parse(); match exec(args.cmd) { Ok(()) => {} @@ -221,19 +175,7 @@ fn main() { } } -fn exec>(cmd: Command) -> Result<()> -where - ::AccountId: - Serialize + Display + IntoVisitor + Decode + AsRef<[u8]> + Send + Sync + FromStr + EncodeAsType, - <::AccountId as FromStr>::Err: - Into>, - ::Hash: IntoVisitor + Display, - ::Balance: Serialize + Decode + Debug + IntoVisitor + Sync + Send + From + Default + Display + FromStr, - <::Balance as FromStr>::Err: - Into>, - <::ExtrinsicParams as subxt::config::ExtrinsicParams>::OtherParams: Default, - -{ +fn exec(cmd: Command) -> Result<()> { let runtime = Runtime::new().expect("Failed to create Tokio runtime"); match &cmd { Command::New { name, target_dir } => { @@ -293,10 +235,10 @@ where }) } Command::Info(info) => { - runtime.block_on(async { info.run().await.map_err(format_err) }) + runtime.block_on(async { info.handle().await.map_err(format_err) }) } Command::Storage(storage) => { - runtime.block_on(async { storage.run().await.map_err(format_err) }) + runtime.block_on(async { storage.handle().await.map_err(format_err) }) } Command::Verify(verify) => { let result = verify.run().map_err(format_err)?; diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index a27bb2fbb..56ef01ad8 100644 --- a/crates/extrinsics/Cargo.toml +++ b/crates/extrinsics/Cargo.toml @@ -53,6 +53,5 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } subxt-signer = { version = "0.34.0", features = ["subxt", "sr25519"] } [features] -default = ["integration-tests"] integration-tests = [] test-ci-only = [] diff --git a/crates/transcode/src/lib.rs b/crates/transcode/src/lib.rs index 0edd08fd8..38aa7c3fc 100644 --- a/crates/transcode/src/lib.rs +++ b/crates/transcode/src/lib.rs @@ -99,7 +99,6 @@ //! ``` mod account_id; -mod account_id20; mod decode; mod encode; pub mod env_types; @@ -109,11 +108,6 @@ mod util; pub use self::{ account_id::AccountId32, - account_id20::{ - AccountId20, - EthereumSignature, - EthereumSigner, - }, scon::{ Hex, Map, From 0e3ab82dc9be341cd63d5a0a81b1679ab07b604e Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Thu, 22 Feb 2024 23:49:50 +0100 Subject: [PATCH 28/39] Fix clippy --- crates/cargo-contract/src/cmd/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index 29c0908c8..86177dee6 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -280,9 +280,9 @@ pub fn parse_balance + Clone>( balance: &str, token_metadata: &TokenMetadata, ) -> Result { - BalanceVariant::from_str(&balance) + BalanceVariant::from_str(balance) .map_err(|e| anyhow!("Balance parsing failed: {e}")) - .and_then(|bv| bv.denominate_balance(&token_metadata)) + .and_then(|bv| bv.denominate_balance(token_metadata)) } /// Parse a account from string format From e9bbc8ce8aa9531a760c32a4c45b74d5dafc6c6c Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 23 Feb 2024 00:02:44 +0100 Subject: [PATCH 29/39] Remove unused AccountId20 --- crates/transcode/src/account_id20.rs | 300 --------------------------- 1 file changed, 300 deletions(-) delete mode 100644 crates/transcode/src/account_id20.rs diff --git a/crates/transcode/src/account_id20.rs b/crates/transcode/src/account_id20.rs deleted file mode 100644 index a1c9b931e..000000000 --- a/crates/transcode/src/account_id20.rs +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2019-2022 PureStake Inc. -// This file is part of Moonbeam. - -// Moonbeam is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Moonbeam is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Moonbeam. If not, see . - -//! The Ethereum Signature implementation. -//! -//! It includes the Verify and IdentifyAccount traits for the AccountId20 - -use scale::{ - Decode, - Encode, - MaxEncodedLen, -}; -use scale_info::TypeInfo; -pub use serde::Serialize; -use sha3::{ - Digest, - Keccak256, -}; -use sp_core::{ - ecdsa, - H160, -}; -use subxt::ext::scale_decode; -use subxt::ext::scale_encode; - -// TODO Maybe this should be upstreamed into Frontier (And renamed accordingly) so that it -// can be used in palletEVM as well. It may also need more traits such as AsRef, AsMut, -// etc like AccountId32 has. - -/// The account type to be used in Moonbeam. It is a wrapper for 20 fixed bytes. We prefer -/// to use a dedicated type to prevent using arbitrary 20 byte arrays were AccountIds are -/// expected. With the introduction of the `scale-info` crate this benefit extends even to -/// non-Rust tools like Polkadot JS. - -#[derive( - Eq, - PartialEq, - Copy, - Clone, - Encode, - Decode, - TypeInfo, - MaxEncodedLen, - Default, - PartialOrd, - Ord, - scale_decode::DecodeAsType, - scale_encode::EncodeAsType, -)] -#[decode_as_type(crate_path = "subxt::ext::scale_decode")] -#[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub struct AccountId20(pub [u8; 20]); - -impl_serde::impl_fixed_hash_serde!(AccountId20, 20); - -impl std::fmt::Display for AccountId20 { - // TODO This is a pretty quck-n-dirty implementation. Perhaps we should add - // checksum casing here? I bet there is a crate for that. - // Maybe this one https://github.com/miguelmota/rust-eth-checksum - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl core::fmt::Debug for AccountId20 { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{:?}", H160(self.0)) - } -} - -impl From<[u8; 20]> for AccountId20 { - fn from(bytes: [u8; 20]) -> Self { - Self(bytes) - } -} - -impl From for [u8; 20] { - fn from(value: AccountId20) -> Self { - value.0 - } -} - -impl From<[u8; 32]> for AccountId20 { - fn from(bytes: [u8; 32]) -> Self { - let mut buffer = [0u8; 20]; - buffer.copy_from_slice(&bytes[..20]); - Self(buffer) - } -} - -impl From for AccountId20 { - fn from(h160: H160) -> Self { - Self(h160.0) - } -} - -impl From for H160 { - fn from(value: AccountId20) -> Self { - H160(value.0) - } -} - -impl std::str::FromStr for AccountId20 { - type Err = &'static str; - fn from_str(input: &str) -> Result { - H160::from_str(input) - .map(Into::into) - .map_err(|_| "invalid hex address.") - } -} - -impl AsRef<[u8]> for AccountId20 { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -#[derive( - serde::Serialize, - serde::Deserialize, - Eq, - PartialEq, - Clone, - Encode, - Decode, - sp_core::RuntimeDebug, - TypeInfo, -)] -pub struct EthereumSignature(ecdsa::Signature); - -impl From for EthereumSignature { - fn from(x: ecdsa::Signature) -> Self { - EthereumSignature(x) - } -} - -impl sp_runtime::traits::Verify for EthereumSignature { - type Signer = EthereumSigner; - fn verify>( - &self, - mut msg: L, - signer: &AccountId20, - ) -> bool { - let mut m = [0u8; 32]; - m.copy_from_slice(Keccak256::digest(msg.get()).as_slice()); - match sp_io::crypto::secp256k1_ecdsa_recover(self.0.as_ref(), &m) { - Ok(pubkey) => { - AccountId20( - H160::from_slice(&Keccak256::digest(pubkey).as_slice()[12..32]).0, - ) == *signer - } - Err(sp_io::EcdsaVerifyError::BadRS) => { - log::error!(target: "evm", "Error recovering: Incorrect value of R or S"); - false - } - Err(sp_io::EcdsaVerifyError::BadV) => { - log::error!(target: "evm", "Error recovering: Incorrect value of V"); - false - } - Err(sp_io::EcdsaVerifyError::BadSignature) => { - log::error!(target: "evm", "Error recovering: Invalid signature"); - false - } - } - } -} - -/// Public key for an Ethereum / Moonbeam compatible account -#[derive( - Eq, - PartialEq, - Ord, - PartialOrd, - Clone, - Encode, - Decode, - sp_core::RuntimeDebug, - TypeInfo, - serde::Serialize, - serde::Deserialize, -)] -pub struct EthereumSigner([u8; 20]); - -impl sp_runtime::traits::IdentifyAccount for EthereumSigner { - type AccountId = AccountId20; - fn into_account(self) -> AccountId20 { - AccountId20(self.0) - } -} - -impl From<[u8; 20]> for EthereumSigner { - fn from(x: [u8; 20]) -> Self { - EthereumSigner(x) - } -} - -impl From for EthereumSigner { - fn from(x: ecdsa::Public) -> Self { - let decompressed = libsecp256k1::PublicKey::parse_slice( - &x.0, - Some(libsecp256k1::PublicKeyFormat::Compressed), - ) - .expect("Wrong compressed public key provided") - .serialize(); - let mut m = [0u8; 64]; - m.copy_from_slice(&decompressed[1..65]); - let account = H160::from_slice(&Keccak256::digest(m).as_slice()[12..32]); - EthereumSigner(account.into()) - } -} - -impl From for EthereumSigner { - fn from(x: libsecp256k1::PublicKey) -> Self { - let mut m = [0u8; 64]; - m.copy_from_slice(&x.serialize()[1..65]); - let account = H160::from_slice(&Keccak256::digest(m).as_slice()[12..32]); - EthereumSigner(account.into()) - } -} - -impl std::fmt::Display for EthereumSigner { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "ethereum signature: {:?}", H160::from_slice(&self.0)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use sp_core::{ - ecdsa, - Pair, - H256, - }; - use sp_runtime::traits::IdentifyAccount; - - #[test] - fn test_account_derivation_1() { - // Test from https://asecuritysite.com/encryption/ethadd - let secret_key = hex::decode( - "502f97299c472b88754accd412b7c9a6062ef3186fba0c0388365e1edec24875", - ) - .unwrap(); - let mut expected_hex_account = [0u8; 20]; - hex::decode_to_slice( - "976f8456e4e2034179b284a23c0e0c8f6d3da50c", - &mut expected_hex_account, - ) - .expect("example data is 20 bytes of valid hex"); - - let public_key = ecdsa::Pair::from_seed_slice(&secret_key).unwrap().public(); - let account: EthereumSigner = public_key.into(); - let expected_account = AccountId20::from(expected_hex_account); - assert_eq!(account.into_account(), expected_account); - } - #[test] - fn test_account_derivation_2() { - // Test from https://asecuritysite.com/encryption/ethadd - let secret_key = hex::decode( - "0f02ba4d7f83e59eaa32eae9c3c4d99b68ce76decade21cdab7ecce8f4aef81a", - ) - .unwrap(); - let mut expected_hex_account = [0u8; 20]; - hex::decode_to_slice( - "420e9f260b40af7e49440cead3069f8e82a5230f", - &mut expected_hex_account, - ) - .expect("example data is 20 bytes of valid hex"); - - let public_key = ecdsa::Pair::from_seed_slice(&secret_key).unwrap().public(); - let account: EthereumSigner = public_key.into(); - let expected_account = AccountId20::from(expected_hex_account); - assert_eq!(account.into_account(), expected_account); - } - #[test] - fn test_account_derivation_3() { - let m = hex::decode( - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ) - .unwrap(); - let old = - AccountId20(H160::from(H256::from_slice(Keccak256::digest(&m).as_slice())).0); - let new = - AccountId20(H160::from_slice(&Keccak256::digest(&m).as_slice()[12..32]).0); - assert_eq!(new, old); - } -} From 00cc81ede525564c9b3f45baee58cb4bb5f6935a Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 23 Feb 2024 00:07:10 +0100 Subject: [PATCH 30/39] Remove unused crates --- Cargo.lock | 7 ------- crates/transcode/Cargo.toml | 9 --------- 2 files changed, 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5218cbf9..dac614426 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1260,14 +1260,11 @@ dependencies = [ "contract-metadata", "escape8259", "hex", - "impl-serde", "indexmap 2.2.3", "ink", "ink_env", "ink_metadata", "itertools 0.12.1", - "libsecp256k1", - "log", "nom", "nom-supreme", "parity-scale-codec", @@ -1275,13 +1272,9 @@ dependencies = [ "scale-info", "serde", "serde_json", - "sha3", "sp-core", - "sp-io", "sp-keyring", - "sp-runtime", "strsim 0.11.0", - "subxt", "thiserror", "tracing", ] diff --git a/crates/transcode/Cargo.toml b/crates/transcode/Cargo.toml index 0aa0311a4..ef44fe24d 100644 --- a/crates/transcode/Cargo.toml +++ b/crates/transcode/Cargo.toml @@ -38,15 +38,6 @@ serde_json = "1.0.114" thiserror = "1.0.57" strsim = "0.11.0" -impl-serde = { version = "0.4.0" } -libsecp256k1 = { version = "0.7.1", features = [ "hmac" ] } -sha3 = { version = "0.10.3", default-features = false } -sp-core = "28.0.0" -sp-io = "30.0.0" -sp-runtime = "31.0.0" -log = "0.4.20" -subxt = "0.34.0" - [dev-dependencies] assert_matches = "1.5.0" ink = "5.0.0-rc.1" From 8f34df4f1121929c3abfd5a813d4ea4f3cb9df38 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 23 Feb 2024 10:20:09 +0100 Subject: [PATCH 31/39] Fix for info -all command --- Cargo.lock | 1 - crates/cargo-contract/src/cmd/call.rs | 4 +--- crates/cargo-contract/src/cmd/info.rs | 18 ++++++++++++++---- crates/cargo-contract/src/cmd/instantiate.rs | 4 +--- crates/cargo-contract/src/cmd/remove.rs | 4 +--- crates/cargo-contract/src/cmd/storage.rs | 1 - crates/cargo-contract/src/cmd/upload.rs | 4 +--- crates/extrinsics/Cargo.toml | 1 - 8 files changed, 18 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dac614426..6779a6dab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1204,7 +1204,6 @@ dependencies = [ "anyhow", "assert_cmd", "blake2", - "clap", "colored", "contract-build", "contract-metadata", diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index 29db74e1f..675e4a504 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -129,7 +129,6 @@ impl CallCommand { .map_err(|_| anyhow::anyhow!("Failed to parse suri option"))?; let token_metadata = TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let storage_deposit_limit = self .extrinsic_cli_opts .storage_deposit_limit @@ -139,10 +138,8 @@ impl CallCommand { .map_err(|e| { anyhow::anyhow!("Failed to parse storage_deposit_limit option: {}", e) })?; - let value = parse_balance(&self.value, &token_metadata) .map_err(|e| anyhow::anyhow!("Failed to parse value option: {}", e))?; - let extrinsic_opts = ExtrinsicOptsBuilder::new(signer) .file(self.extrinsic_cli_opts.file.clone()) .manifest_path(self.extrinsic_cli_opts.manifest_path.clone()) @@ -150,6 +147,7 @@ impl CallCommand { .storage_deposit_limit(storage_deposit_limit) .verbosity(self.extrinsic_cli_opts.verbosity()?) .done(); + let call_exec = CallCommandBuilder::new(contract, &self.message, extrinsic_opts) .args(self.args.clone()) .gas_limit(self.gas_limit) diff --git a/crates/cargo-contract/src/cmd/info.rs b/crates/cargo-contract/src/cmd/info.rs index 1c0699ef2..73a9573ec 100644 --- a/crates/cargo-contract/src/cmd/info.rs +++ b/crates/cargo-contract/src/cmd/info.rs @@ -59,8 +59,13 @@ use subxt::{ #[clap(name = "info", about = "Get infos from a contract")] pub struct InfoCommand { /// The address of the contract to display info of. - #[clap(name = "contract", long, env = "CONTRACT", conflicts_with = "all")] - contract: String, + #[clap( + name = "contract", + long, + env = "CONTRACT", + required_unless_present = "all" + )] + contract: Option, /// Websockets url of a substrate node. #[clap( name = "url", @@ -100,8 +105,6 @@ impl InfoCommand { let rpc_cli = RpcClient::from_url(url_to_string(&self.url)).await?; let client = OnlineClient::::from_rpc_client(rpc_cli.clone()).await?; let rpc = LegacyRpcMethods::::new(rpc_cli.clone()); - let contract = parse_account(&self.contract) - .map_err(|e| anyhow::anyhow!("Failed to parse contract option: {}", e))?; // All flag applied if self.all { @@ -119,6 +122,13 @@ impl InfoCommand { } else { // Contract arg shall be always present in this case, it is enforced by // clap configuration + let contract = self + .contract + .as_ref() + .map(|c| parse_account(c)) + .transpose()? + .expect("Contract argument shall be present"); + let info_to_json = fetch_contract_info::(&contract, &rpc, &client).await?; diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index a9648384f..a8dacd5b7 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -136,7 +136,6 @@ impl InstantiateCommand { .map_err(|_| anyhow::anyhow!("Failed to parse suri option"))?; let token_metadata = TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let storage_deposit_limit = self .extrinsic_cli_opts .storage_deposit_limit @@ -146,16 +145,15 @@ impl InstantiateCommand { .map_err(|e| { anyhow::anyhow!("Failed to parse storage_deposit_limit option: {}", e) })?; - let value = parse_balance(&self.value, &token_metadata) .map_err(|e| anyhow::anyhow!("Failed to parse value option: {}", e))?; - 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()) .storage_deposit_limit(storage_deposit_limit) .done(); + let instantiate_exec: InstantiateExec = InstantiateCommandBuilder::new(extrinsic_opts) .constructor(self.constructor.clone()) diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index fa0cbc202..b27fdce03 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -94,7 +94,6 @@ impl RemoveCommand { .map_err(|_| anyhow::anyhow!("Failed to parse suri option"))?; let token_metadata = TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let storage_deposit_limit = self .extrinsic_cli_opts .storage_deposit_limit @@ -104,20 +103,19 @@ impl RemoveCommand { .map_err(|e| { anyhow::anyhow!("Failed to parse storage_deposit_limit option: {}", e) })?; - let code_hash = self .code_hash .clone() .map(|h| parse_code_hash(&h)) .transpose() .map_err(|e| anyhow::anyhow!("Failed to parse code_hash option: {}", e))?; - 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()) .storage_deposit_limit(storage_deposit_limit) .done(); + let remove_exec: RemoveExec = RemoveCommandBuilder::new(extrinsic_opts) .code_hash(code_hash) .done() diff --git a/crates/cargo-contract/src/cmd/storage.rs b/crates/cargo-contract/src/cmd/storage.rs index cfaff1f5f..73806a87f 100644 --- a/crates/cargo-contract/src/cmd/storage.rs +++ b/crates/cargo-contract/src/cmd/storage.rs @@ -135,7 +135,6 @@ impl StorageCommand { "{json}", json = serde_json::to_string_pretty(&storage_data)? ); - return Ok(()) } } diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index 26bca0ea1..f69262662 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -91,7 +91,6 @@ impl UploadCommand { .map_err(|_| anyhow::anyhow!("Failed to parse suri option"))?; let token_metadata = TokenMetadata::query::(&self.extrinsic_cli_opts.url).await?; - let storage_deposit_limit = self .extrinsic_cli_opts .storage_deposit_limit @@ -101,16 +100,15 @@ impl UploadCommand { .map_err(|e| { anyhow::anyhow!("Failed to parse storage_deposit_limit option: {}", e) })?; - 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()) .storage_deposit_limit(storage_deposit_limit) .done(); + let upload_exec: UploadExec = UploadCommandBuilder::new(extrinsic_opts).done().await?; - let code_hash = upload_exec.code().code_hash(); let metadata = upload_exec.client().metadata(); diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index 6203d23d9..c873e17e5 100644 --- a/crates/extrinsics/Cargo.toml +++ b/crates/extrinsics/Cargo.toml @@ -21,7 +21,6 @@ contract-transcode = { version = "4.0.0-rc.2", path = "../transcode" } anyhow = "1.0.80" blake2 = { version = "0.10.6", default-features = false } -clap = { version = "4.5.1", features = ["derive", "env"] } futures = { version = "0.3.30", default-features = false, features = ["std"] } itertools = { version = "0.12", default-features = false } tracing = "0.1.40" From 25d01c006d516560d55753ff0716e7c0b6387b3b Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 23 Feb 2024 10:31:40 +0100 Subject: [PATCH 32/39] Fix description --- crates/cargo-contract/src/config.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cargo-contract/src/config.rs b/crates/cargo-contract/src/config.rs index e58706d83..2c7f7f99c 100644 --- a/crates/cargo-contract/src/config.rs +++ b/crates/cargo-contract/src/config.rs @@ -32,7 +32,7 @@ use subxt::{ SubstrateConfig, }; -/// A runtime configuration for the Polkadot based chain. +/// A runtime configuration for the ecdsa test chain. // /// This thing is not meant to be instantiated; it is just a collection of types. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Ecdsachain {} @@ -134,13 +134,13 @@ where } } -/// Struct representing the implementation of the sr25519 signer +/// Struct representing the implementation of the ecdsa signer #[derive(Clone)] pub struct SignerEcdsa(pub PairSigner); impl FromStr for SignerEcdsa where - // Requirements of `PairSigner where: + // Requirements of the `PairSigner where: // T::AccountId: From` ::AccountId: From, { From 53dd7e1822b55233266c84a6e5c7a1ab0de46a83 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 23 Feb 2024 10:44:25 +0100 Subject: [PATCH 33/39] Fix comment --- crates/cargo-contract/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cargo-contract/src/config.rs b/crates/cargo-contract/src/config.rs index 2c7f7f99c..b014674af 100644 --- a/crates/cargo-contract/src/config.rs +++ b/crates/cargo-contract/src/config.rs @@ -33,7 +33,7 @@ use subxt::{ }; /// A runtime configuration for the ecdsa test chain. -// /// This thing is not meant to be instantiated; it is just a collection of types. +/// This thing is not meant to be instantiated; it is just a collection of types. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Ecdsachain {} @@ -66,7 +66,7 @@ where } /// A runtime configuration for the Polkadot based chain. -// /// This thing is not meant to be instantiated; it is just a collection of types. +/// This thing is not meant to be instantiated; it is just a collection of types. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Polkadot {} From d311a1f17cddd56ba7f4b4d32b0adc782b657736 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Wed, 28 Feb 2024 16:45:47 +0100 Subject: [PATCH 34/39] Move config --- crates/cargo-contract/src/cmd/call.rs | 3 +-- crates/cargo-contract/src/{ => cmd}/config.rs | 21 +++++++++++-------- crates/cargo-contract/src/cmd/instantiate.rs | 3 +-- crates/cargo-contract/src/cmd/mod.rs | 2 ++ crates/cargo-contract/src/cmd/remove.rs | 3 +-- crates/cargo-contract/src/cmd/upload.rs | 3 +-- crates/cargo-contract/src/main.rs | 1 - 7 files changed, 18 insertions(+), 18 deletions(-) rename crates/cargo-contract/src/{ => cmd}/config.rs (95%) diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index 675e4a504..ac405b7df 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -16,7 +16,7 @@ use crate::{ call_with_config, - config::SignerConfig, + cmd::config::SignerConfig, ErrorVariant, }; @@ -117,7 +117,6 @@ impl CallCommand { &self, ) -> Result<(), ErrorVariant> where - >::Signer: subxt::tx::Signer + Clone + FromStr, ::AccountId: IntoVisitor + FromStr + EncodeAsType, <::AccountId as FromStr>::Err: Display, C::Balance: From + Display + Default + FromStr + Serialize + Debug, diff --git a/crates/cargo-contract/src/config.rs b/crates/cargo-contract/src/cmd/config.rs similarity index 95% rename from crates/cargo-contract/src/config.rs rename to crates/cargo-contract/src/cmd/config.rs index b014674af..88e07aeed 100644 --- a/crates/cargo-contract/src/config.rs +++ b/crates/cargo-contract/src/cmd/config.rs @@ -14,13 +14,15 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use std::str::FromStr; - use ink_env::{ DefaultEnvironment, Environment, }; use sp_core::Pair; +use std::{ + fmt::Debug, + str::FromStr, +}; use subxt::{ config::PolkadotExtrinsicParams, tx::{ @@ -32,6 +34,11 @@ use subxt::{ SubstrateConfig, }; +/// Configuration for signer +pub trait SignerConfig { + type Signer: SignerT + FromStr + Clone; +} + /// A runtime configuration for the ecdsa test chain. /// This thing is not meant to be instantiated; it is just a collection of types. #[derive(Debug, Clone, PartialEq, Eq)] @@ -91,10 +98,6 @@ impl Environment for Polkadot { type ChainExtension = ::ChainExtension; } -pub trait SignerConfig { - type Signer: SignerT + FromStr; -} - impl SignerConfig for Polkadot { type Signer = SignerSR25519; } @@ -181,7 +184,7 @@ macro_rules! call_with_config_internal { _ => { let configs = vec![$(stringify!($config)),*].iter() - .map(|s| s.trim_start_matches("crate::config::")) + .map(|s| s.trim_start_matches("crate::cmd::config::")) .collect::>() .join(", "); Err(ErrorVariant::Generic( @@ -203,8 +206,8 @@ macro_rules! call_with_config { $function, config_name.as_str(), // All available chain configs need to be specified here - $crate::config::Polkadot, - $crate::config::Ecdsachain + $crate::cmd::config::Polkadot, + $crate::cmd::config::Ecdsachain ) }}; } diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index a8dacd5b7..18e40c2b3 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -28,7 +28,7 @@ use super::{ use crate::{ anyhow, call_with_config, - config::SignerConfig, + cmd::config::SignerConfig, ErrorVariant, InstantiateExec, Weight, @@ -301,7 +301,6 @@ pub async fn display_result>( verbosity: Verbosity, ) -> Result<(), ErrorVariant> where - C::Signer: subxt::tx::Signer + Clone, ::AccountId: IntoVisitor + EncodeAsType + Display + Decode, ::Hash: IntoVisitor + EncodeAsType, C::Balance: Serialize + From + Display, diff --git a/crates/cargo-contract/src/cmd/mod.rs b/crates/cargo-contract/src/cmd/mod.rs index 86177dee6..b38c6a6e4 100644 --- a/crates/cargo-contract/src/cmd/mod.rs +++ b/crates/cargo-contract/src/cmd/mod.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . +mod config; + pub mod build; pub mod call; pub mod decode; diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index b27fdce03..407319cc0 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -16,7 +16,7 @@ use crate::{ call_with_config, - config::SignerConfig, + cmd::config::SignerConfig, ErrorVariant, }; use std::{ @@ -82,7 +82,6 @@ impl RemoveCommand { &self, ) -> Result<(), ErrorVariant> where - >::Signer: subxt::tx::Signer + Clone + FromStr, ::AccountId: IntoVisitor + FromStr + EncodeAsType, <::AccountId as FromStr>::Err: Display, C::Balance: diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index f69262662..d08173f60 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -16,7 +16,7 @@ use crate::{ call_with_config, - config::SignerConfig, + cmd::config::SignerConfig, ErrorVariant, }; use std::{ @@ -79,7 +79,6 @@ impl UploadCommand { &self, ) -> Result<(), ErrorVariant> where - >::Signer: subxt::tx::Signer + Clone + FromStr, ::AccountId: IntoVisitor + FromStr + EncodeAsType, <::AccountId as FromStr>::Err: Display, C::Balance: diff --git a/crates/cargo-contract/src/main.rs b/crates/cargo-contract/src/main.rs index 564f01d57..4ab46a13e 100644 --- a/crates/cargo-contract/src/main.rs +++ b/crates/cargo-contract/src/main.rs @@ -17,7 +17,6 @@ #![deny(unused_crate_dependencies)] mod cmd; -mod config; use self::cmd::{ BuildCommand, From f45308d865c77ea29bce371c4ea55e18b8343713 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Wed, 28 Feb 2024 17:28:53 +0100 Subject: [PATCH 35/39] Fix tests --- crates/cargo-contract/src/cmd/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cargo-contract/src/cmd/config.rs b/crates/cargo-contract/src/cmd/config.rs index 88e07aeed..0d1532186 100644 --- a/crates/cargo-contract/src/cmd/config.rs +++ b/crates/cargo-contract/src/cmd/config.rs @@ -200,7 +200,7 @@ macro_rules! call_with_config_internal { #[macro_export] macro_rules! call_with_config { ($obj:tt, $function:ident, $config_name:expr) => {{ - let config_name = format!("crate::config::{}", $config_name); + let config_name = format!("crate::cmd::config::{}", $config_name); $crate::call_with_config_internal!( $obj, $function, From 2f28cd5879d3ad1bc4cdacd0de66fe1a04abebfd Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Thu, 29 Feb 2024 00:09:48 +0100 Subject: [PATCH 36/39] Move config --- crates/cargo-contract/src/cmd/call.rs | 2 +- crates/cargo-contract/src/cmd/instantiate.rs | 2 +- crates/cargo-contract/src/cmd/remove.rs | 2 +- crates/cargo-contract/src/cmd/upload.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cargo-contract/src/cmd/call.rs b/crates/cargo-contract/src/cmd/call.rs index ac405b7df..a0939bff3 100644 --- a/crates/cargo-contract/src/cmd/call.rs +++ b/crates/cargo-contract/src/cmd/call.rs @@ -16,7 +16,6 @@ use crate::{ call_with_config, - cmd::config::SignerConfig, ErrorVariant, }; @@ -32,6 +31,7 @@ use std::{ }; use super::{ + config::SignerConfig, display_contract_exec_result, display_contract_exec_result_debug, display_dry_run_result_warning, diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index 18e40c2b3..0f4d7c96b 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::{ + config::SignerConfig, display_contract_exec_result, display_contract_exec_result_debug, display_dry_run_result_warning, @@ -28,7 +29,6 @@ use super::{ use crate::{ anyhow, call_with_config, - cmd::config::SignerConfig, ErrorVariant, InstantiateExec, Weight, diff --git a/crates/cargo-contract/src/cmd/remove.rs b/crates/cargo-contract/src/cmd/remove.rs index 407319cc0..ef619b124 100644 --- a/crates/cargo-contract/src/cmd/remove.rs +++ b/crates/cargo-contract/src/cmd/remove.rs @@ -16,7 +16,6 @@ use crate::{ call_with_config, - cmd::config::SignerConfig, ErrorVariant, }; use std::{ @@ -28,6 +27,7 @@ use std::{ }; use super::{ + config::SignerConfig, parse_balance, parse_code_hash, CLIExtrinsicOpts, diff --git a/crates/cargo-contract/src/cmd/upload.rs b/crates/cargo-contract/src/cmd/upload.rs index d08173f60..b047e7925 100644 --- a/crates/cargo-contract/src/cmd/upload.rs +++ b/crates/cargo-contract/src/cmd/upload.rs @@ -16,7 +16,6 @@ use crate::{ call_with_config, - cmd::config::SignerConfig, ErrorVariant, }; use std::{ @@ -28,6 +27,7 @@ use std::{ }; use super::{ + config::SignerConfig, display_dry_run_result_warning, parse_balance, CLIExtrinsicOpts, From 24d296d9e6220361d52f96d9123e2cda85761676 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Thu, 29 Feb 2024 15:12:02 +0100 Subject: [PATCH 37/39] Add Substrate config --- crates/cargo-contract/src/cmd/config.rs | 51 ++++++++++++++++++++----- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/crates/cargo-contract/src/cmd/config.rs b/crates/cargo-contract/src/cmd/config.rs index 0d1532186..d3e56bcd1 100644 --- a/crates/cargo-contract/src/cmd/config.rs +++ b/crates/cargo-contract/src/cmd/config.rs @@ -24,13 +24,16 @@ use std::{ str::FromStr, }; use subxt::{ - config::PolkadotExtrinsicParams, + config::{ + PolkadotExtrinsicParams, + SubstrateExtrinsicParams, + }, tx::{ PairSigner, Signer as SignerT, }, - utils::MultiAddress, Config, + PolkadotConfig, SubstrateConfig, }; @@ -47,11 +50,11 @@ pub enum Ecdsachain {} impl Config for Ecdsachain { type Hash = ::Hash; type AccountId = ::AccountId; - type Address = MultiAddress; + type Address = ::Address; type Signature = ::Signature; type Hasher = ::Hasher; type Header = ::Header; - type ExtrinsicParams = PolkadotExtrinsicParams; + type ExtrinsicParams = SubstrateExtrinsicParams; type AssetId = ::AssetId; } @@ -72,22 +75,52 @@ where type Signer = SignerEcdsa; } -/// A runtime configuration for the Polkadot based chain. +/// A runtime configuration for the Substrate based chain. /// This thing is not meant to be instantiated; it is just a collection of types. #[derive(Debug, Clone, PartialEq, Eq)] -pub enum Polkadot {} +pub enum Substrate {} -impl Config for Polkadot { +impl Config for Substrate { type Hash = ::Hash; type AccountId = ::AccountId; - type Address = MultiAddress; + type Address = ::Address; type Signature = ::Signature; type Hasher = ::Hasher; type Header = ::Header; - type ExtrinsicParams = PolkadotExtrinsicParams; + type ExtrinsicParams = SubstrateExtrinsicParams; type AssetId = ::AssetId; } +impl Environment for Substrate { + const MAX_EVENT_TOPICS: usize = ::MAX_EVENT_TOPICS; + type AccountId = ::AccountId; + type Balance = ::Balance; + type Hash = ::Hash; + type Timestamp = ::Timestamp; + type BlockNumber = ::BlockNumber; + type ChainExtension = ::ChainExtension; +} + +impl SignerConfig for Substrate { + type Signer = SignerSR25519; +} + +/// A runtime configuration for the Polkadot based chain. +/// This thing is not meant to be instantiated; it is just a collection of types. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Polkadot {} + +impl Config for Polkadot { + type Hash = ::Hash; + type AccountId = ::AccountId; + type Address = ::Address; + type Signature = ::Signature; + type Hasher = ::Hasher; + type Header = ::Header; + type ExtrinsicParams = PolkadotExtrinsicParams; + type AssetId = ::AssetId; +} + impl Environment for Polkadot { const MAX_EVENT_TOPICS: usize = ::MAX_EVENT_TOPICS; type AccountId = ::AccountId; From 17a4e59c3ca32531e22f37f3f4d64c74ee9563b8 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Thu, 29 Feb 2024 15:23:18 +0100 Subject: [PATCH 38/39] Add substrate config to configs list --- crates/cargo-contract/src/cmd/config.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/cargo-contract/src/cmd/config.rs b/crates/cargo-contract/src/cmd/config.rs index d3e56bcd1..53cf9e0eb 100644 --- a/crates/cargo-contract/src/cmd/config.rs +++ b/crates/cargo-contract/src/cmd/config.rs @@ -240,6 +240,7 @@ macro_rules! call_with_config { config_name.as_str(), // All available chain configs need to be specified here $crate::cmd::config::Polkadot, + $crate::cmd::config::Substrate, $crate::cmd::config::Ecdsachain ) }}; From 0be6eeb6ef1cfe2970d59addf1bb4c7e0e4b8145 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 12 Mar 2024 15:55:45 +0100 Subject: [PATCH 39/39] Update deps --- Cargo.lock | 3 +++ crates/cargo-contract/Cargo.toml | 1 + crates/cargo-contract/src/cmd/instantiate.rs | 2 +- crates/extrinsics/Cargo.toml | 2 ++ crates/extrinsics/src/contract_storage.rs | 16 +++++++--------- crates/extrinsics/src/error.rs | 2 +- crates/extrinsics/src/instantiate.rs | 2 +- .../src/pallet_contracts_primitives.rs | 4 ++-- 8 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 110ff782f..19ebfea00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -893,6 +893,7 @@ dependencies = [ "semver", "serde", "serde_json", + "sp-core 30.0.0", "sp-weights 29.0.0", "substrate-build-script-utils", "subxt", @@ -1205,6 +1206,8 @@ dependencies = [ "scale-info", "serde", "serde_json", + "sp-core 30.0.0", + "sp-runtime 33.0.0", "sp-weights 29.0.0", "subxt", "subxt-signer", diff --git a/crates/cargo-contract/Cargo.toml b/crates/cargo-contract/Cargo.toml index 0d2f83e1e..8202f9022 100644 --- a/crates/cargo-contract/Cargo.toml +++ b/crates/cargo-contract/Cargo.toml @@ -44,6 +44,7 @@ comfy-table = "7.1.0" # dependencies for extrinsics (deploying and calling a contract) tokio = { version = "1", features = ["macros", "rt-multi-thread"] } subxt = { version = "0.34.0", features = ["substrate-compat"] } +sp-core = "30.0.0" sp-weights = "29.0.0" hex = "0.4.3" diff --git a/crates/cargo-contract/src/cmd/instantiate.rs b/crates/cargo-contract/src/cmd/instantiate.rs index a33ab42e3..0f4d7c96b 100644 --- a/crates/cargo-contract/src/cmd/instantiate.rs +++ b/crates/cargo-contract/src/cmd/instantiate.rs @@ -53,6 +53,7 @@ use contract_extrinsics::{ }; use ink_env::Environment; use serde::Serialize; +use sp_core::Bytes; use std::{ fmt::{ Debug, @@ -66,7 +67,6 @@ use subxt::{ codec::Decode, scale_decode::IntoVisitor, scale_encode::EncodeAsType, - sp_core::Bytes, }, Config, }; diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index 4d0f19adf..7118e00bf 100644 --- a/crates/extrinsics/Cargo.toml +++ b/crates/extrinsics/Cargo.toml @@ -31,6 +31,8 @@ serde_json = "1.0.114" url = { version = "2.5.0", features = ["serde"] } rust_decimal = "1.34" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +sp-core = "30.0.0" +sp-runtime = "33.0.0" sp-weights = "29.0.0" pallet-contracts-uapi = { package = "pallet-contracts-uapi-next", version = "=6.0.3", features = ["scale"] } scale-info = "2.10.0" diff --git a/crates/extrinsics/src/contract_storage.rs b/crates/extrinsics/src/contract_storage.rs index 43915e373..0c7f25354 100644 --- a/crates/extrinsics/src/contract_storage.rs +++ b/crates/extrinsics/src/contract_storage.rs @@ -40,6 +40,10 @@ use serde::{ Serialize, Serializer, }; +use sp_core::{ + hexdisplay::AsBytesRef, + storage::ChildInfo, +}; use std::{ collections::BTreeMap, fmt, @@ -61,15 +65,9 @@ use subxt::{ }, }, error::DecodeError, - ext::{ - scale_decode::{ - IntoVisitor, - Visitor, - }, - sp_core::{ - hexdisplay::AsBytesRef, - storage::ChildInfo, - }, + ext::scale_decode::{ + IntoVisitor, + Visitor, }, Config, OnlineClient, diff --git a/crates/extrinsics/src/error.rs b/crates/extrinsics/src/error.rs index 9ae619805..92d342c83 100644 --- a/crates/extrinsics/src/error.rs +++ b/crates/extrinsics/src/error.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . +use sp_runtime::DispatchError; use std::fmt::{ self, Debug, Display, }; -use subxt::ext::sp_runtime::DispatchError; #[derive(serde::Serialize)] pub enum ErrorVariant { diff --git a/crates/extrinsics/src/instantiate.rs b/crates/extrinsics/src/instantiate.rs index 179a533dd..3bb62b405 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -49,6 +49,7 @@ use scale::{ Decode, Encode, }; +use sp_core::Bytes; use sp_weights::Weight; use std::fmt::Display; use subxt::{ @@ -61,7 +62,6 @@ use subxt::{ ext::{ scale_decode::IntoVisitor, scale_encode::EncodeAsType, - sp_core::Bytes, }, tx, Config, diff --git a/crates/extrinsics/src/pallet_contracts_primitives.rs b/crates/extrinsics/src/pallet_contracts_primitives.rs index 32a74983b..141bb0c1d 100644 --- a/crates/extrinsics/src/pallet_contracts_primitives.rs +++ b/crates/extrinsics/src/pallet_contracts_primitives.rs @@ -21,11 +21,11 @@ use scale::{ MaxEncodedLen, }; use scale_info::TypeInfo; -use sp_weights::Weight; -use subxt::ext::sp_runtime::{ +use sp_runtime::{ DispatchError, RuntimeDebug, }; +use sp_weights::Weight; // A copy of primitive types defined within `pallet_contracts`, required for RPC calls.