Skip to content

Commit

Permalink
Add multi-config support (#1503)
Browse files Browse the repository at this point in the history
  • Loading branch information
smiasojed authored Mar 21, 2024
1 parent 5db3806 commit 04e42b9
Show file tree
Hide file tree
Showing 15 changed files with 1,032 additions and 282 deletions.
352 changes: 312 additions & 40 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions crates/cargo-contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,10 @@ 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 = "30.0.0"
sp-weights = "29.0.0"
hex = "0.4.3"
subxt-signer = { version = "0.34.0", features = ["subxt", "sr25519"] }

[build-dependencies]
anyhow = "1.0.81"
Expand Down
124 changes: 81 additions & 43 deletions crates/cargo-contract/src/cmd/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,29 @@
// You should have received a copy of the GNU General Public License
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.

use crate::ErrorVariant;
use crate::{
call_with_config,
ErrorVariant,
};

use contract_build::util::DEFAULT_KEY_COL_WIDTH;
use ink_env::{
DefaultEnvironment,
Environment,
use ink_env::Environment;
use serde::Serialize;
use std::{
fmt::{
Debug,
Display,
},
str::FromStr,
};
use std::fmt::Debug;

use super::{
create_signer,
config::SignerConfig,
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,
Expand All @@ -42,7 +51,6 @@ use anyhow::{
use contract_build::name_value_println;
use contract_extrinsics::{
pallet_contracts_primitives::StorageDeposit,
BalanceVariant,
CallCommandBuilder,
CallExec,
DisplayEvents,
Expand All @@ -52,16 +60,20 @@ use contract_extrinsics::{
use contract_transcode::Value;
use sp_weights::Weight;
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 = "call", about = "Call a contract")]
pub struct CallCommand {
/// The address of the the contract to call.
#[clap(name = "contract", long, env = "CONTRACT")]
contract: <DefaultConfig as Config>::AccountId,
contract: String,
/// The name of the contract message to call.
#[clap(long, short)]
message: String,
Expand All @@ -82,10 +94,13 @@ pub struct CallCommand {
proof_size: Option<u64>,
/// The value to be transferred as part of the call.
#[clap(name = "value", long, default_value = "0")]
value: BalanceVariant<<DefaultEnvironment as Environment>::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 {
Expand All @@ -95,31 +110,50 @@ impl CallCommand {
}

pub async fn handle(&self) -> Result<(), ErrorVariant> {
let token_metadata =
TokenMetadata::query::<DefaultConfig>(&self.extrinsic_cli_opts.url).await?;
call_with_config!(self, run, self.config.as_str())
}

let signer = create_signer(&self.extrinsic_cli_opts.suri)?;
async fn run<C: Config + Environment + SignerConfig<C>>(
&self,
) -> Result<(), ErrorVariant>
where
<C as Config>::AccountId: IntoVisitor + FromStr + EncodeAsType,
<<C as Config>::AccountId as FromStr>::Err: Display,
C::Balance: From<u128> + Display + Default + FromStr + Serialize + Debug,
<C::ExtrinsicParams as ExtrinsicParams<C>>::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::<C>(&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 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 {
Expand Down Expand Up @@ -147,7 +181,7 @@ impl CallCommand {
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");
Expand All @@ -159,7 +193,7 @@ impl CallCommand {
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)?;
}
}
}
Expand Down Expand Up @@ -190,15 +224,13 @@ impl CallCommand {
})?;
}
let events = call_exec.call(Some(gas_limit)).await?;
let display_events = DisplayEvents::from_events::<
DefaultConfig,
DefaultEnvironment,
>(&events, None, &metadata)?;
let display_events =
DisplayEvents::from_events::<C, C>(&events, None, &metadata)?;

let output = if self.output_json() {
display_events.to_json()?
} else {
display_events.display_events::<DefaultEnvironment>(
display_events.display_events::<C>(
self.extrinsic_cli_opts.verbosity().unwrap(),
&token_metadata,
)?
Expand All @@ -210,11 +242,17 @@ 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<DefaultConfig, DefaultEnvironment, Keypair>,
async fn pre_submit_dry_run_gas_estimate_call<C: Config + Environment, Signer>(
call_exec: &CallExec<C, C, Signer>,
output_json: bool,
skip_dry_run: bool,
) -> Result<Weight> {
) -> Result<Weight>
where
Signer: subxt::tx::Signer<C> + Clone,
<C as Config>::AccountId: IntoVisitor + EncodeAsType,
C::Balance: Debug,
<C::ExtrinsicParams as ExtrinsicParams<C>>::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)),
Expand Down Expand Up @@ -250,7 +288,7 @@ async fn pre_submit_dry_run_gas_estimate_call(
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>(&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."))
}
Expand All @@ -260,17 +298,17 @@ async fn pre_submit_dry_run_gas_estimate_call(

/// Result of the contract call
#[derive(serde::Serialize)]
pub struct CallDryRunResult {
pub struct CallDryRunResult<Balance> {
/// 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<<DefaultEnvironment as Environment>::Balance>,
pub storage_deposit: StorageDeposit<Balance>,
}

impl CallDryRunResult {
impl<Balance: Serialize> CallDryRunResult<Balance> {
/// Returns a result in json format
pub fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string_pretty(self)?)
Expand Down
Loading

0 comments on commit 04e42b9

Please sign in to comment.