Skip to content

Commit

Permalink
refactor: generic sign and submit
Browse files Browse the repository at this point in the history
  • Loading branch information
Daanvdplas committed Dec 12, 2024
1 parent b787e08 commit 780ef4b
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 57 deletions.
31 changes: 22 additions & 9 deletions crates/pop-cli/src/commands/call/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use clap::Args;
use pop_parachains::{
construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data,
find_dispatchable_by_name, find_pallet_by_name, parse_chain_metadata, set_up_client,
sign_and_submit_extrinsic, sign_and_submit_extrinsic_with_call_data, supported_actions, Action,
DynamicPayload, Function, OnlineClient, Pallet, Param, SubstrateConfig,
sign_and_submit_extrinsic, supported_actions, Action, CallData, DynamicPayload, Function,
OnlineClient, Pallet, Param, SubstrateConfig,
};
use url::Url;

Expand Down Expand Up @@ -62,7 +62,12 @@ impl CallChainCommand {
// Execute the call if call_data is provided.
if let Some(call_data) = self.call_data.as_ref() {
if let Err(e) = self
.submit_extrinsic_from_call_data(&chain.client, call_data, &mut cli::Cli)
.submit_extrinsic_from_call_data(
&chain.client,
&chain.url,
call_data,
&mut cli::Cli,
)
.await
{
display_message(&e.to_string(), false, &mut cli::Cli)?;
Expand Down Expand Up @@ -213,6 +218,7 @@ impl CallChainCommand {
async fn submit_extrinsic_from_call_data(
&self,
client: &OnlineClient<SubstrateConfig>,
url: &Url,
call_data: &str,
cli: &mut impl Cli,
) -> Result<()> {
Expand All @@ -238,7 +244,7 @@ impl CallChainCommand {
spinner.start("Signing and submitting the extrinsic and then waiting for finalization, please be patient...");
let call_data_bytes =
decode_call_data(call_data).map_err(|err| anyhow!("{}", format!("{err:?}")))?;
let result = sign_and_submit_extrinsic_with_call_data(client, call_data_bytes, suri)
let result = sign_and_submit_extrinsic(client, &url, CallData::new(call_data_bytes), suri)

Check warning on line 247 in crates/pop-cli/src/commands/call/chain.rs

View workflow job for this annotation

GitHub Actions / clippy

this expression creates a reference which is immediately dereferenced by the compiler

warning: this expression creates a reference which is immediately dereferenced by the compiler --> crates/pop-cli/src/commands/call/chain.rs:247:50 | 247 | let result = sign_and_submit_extrinsic(client, &url, CallData::new(call_data_bytes), suri) | ^^^^ help: change this to: `url` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `#[warn(clippy::needless_borrow)]` on by default
.await
.map_err(|err| anyhow!("{}", format!("{err:?}")))?;

Expand Down Expand Up @@ -754,19 +760,21 @@ mod tests {
.expect_confirm("Do you want to submit the extrinsic?", false)
.expect_outro_cancel("Extrinsic for `remark` was not submitted.");
let xt = call_config.prepare_extrinsic(&client, &mut cli)?;
call_config.submit_extrinsic(&client, xt, &mut cli).await?;
call_config
.submit_extrinsic(&client, &Url::parse(POP_NETWORK_TESTNET_URL)?, xt, &mut cli)
.await?;

cli.verify()
}

#[tokio::test]
async fn user_cancel_submit_extrinsic_from_call_data_works() -> Result<()> {
let client = set_up_client("wss://rpc1.paseo.popnetwork.xyz").await?;
let client = set_up_client(POP_NETWORK_TESTNET_URL).await?;
let call_config = CallChainCommand {
pallet: None,
function: None,
args: vec![].to_vec(),
url: Some(Url::parse("wss://rpc1.paseo.popnetwork.xyz")?),
url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?),
suri: None,
skip_confirm: false,
call_data: Some("0x00000411".to_string()),
Expand All @@ -777,7 +785,12 @@ mod tests {
.expect_confirm("Do you want to submit the extrinsic?", false)
.expect_outro_cancel("Extrinsic with call data 0x00000411 was not submitted.");
call_config
.submit_extrinsic_from_call_data(&client, "0x00000411", &mut cli)
.submit_extrinsic_from_call_data(
&client,
&Url::parse(POP_NETWORK_TESTNET_URL)?,
"0x00000411",
&mut cli,
)
.await?;

cli.verify()
Expand Down Expand Up @@ -809,7 +822,7 @@ mod tests {
"Would you like to dispatch this function call with `Root` origin?",
true,
);
call_config.url = Some(Url::parse("wss://rpc1.paseo.popnetwork.xyz")?);
call_config.url = Some(Url::parse(POP_NETWORK_TESTNET_URL)?);
let chain = call_config.configure_chain(&mut cli).await?;
call_config.configure_sudo(&chain, &mut cli)?;
assert!(call_config.sudo);
Expand Down
9 changes: 4 additions & 5 deletions crates/pop-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ repository.workspace = true
[dependencies]
anyhow.workspace = true
cargo_toml.workspace = true
contract-build.workspace = true
contract-extrinsics.workspace = true
duct.workspace = true
flate2.workspace = true
git2.workspace = true
git2_credentials.workspace = true
ink_env.workspace = true
regex.workspace = true
reqwest.workspace = true
scale-info.workspace = true
Expand All @@ -27,13 +30,9 @@ tar.workspace = true
tempfile.workspace = true
thiserror.workspace = true
tokio.workspace = true
toml.workspace = true
toml_edit.workspace = true
url.workspace = true
toml.workspace = true

contract-extrinsics.workspace = true
contract-build.workspace = true
ink_env.workspace = true

[dev-dependencies]
mockito.workspace = true
Expand Down
7 changes: 5 additions & 2 deletions crates/pop-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,13 @@ pub fn find_free_port() -> u16 {
.port()
}

/// Provides functionality for making calls to parachains or smart contracts.
pub mod call {
pub use contract_extrinsics::{DisplayEvents, TokenMetadata};
pub use ink_env::{DefaultEnvironment};
// Note: parsing events after calling a chain is done using cargo contract logic. This could be
// refactored in the future.
pub use contract_build::Verbosity;
pub use contract_extrinsics::{DisplayEvents, TokenMetadata};
pub use ink_env::DefaultEnvironment;
}

#[cfg(test)]
Expand Down
67 changes: 27 additions & 40 deletions crates/pop-parachains/src/call/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// SPDX-License-Identifier: GPL-3.0

use crate::{errors::Error, Function};
use pop_common::{create_signer};
use pop_common::{
call::{DefaultEnvironment, DisplayEvents, TokenMetadata, Verbosity},
create_signer,
};
use subxt::{
dynamic::Value,
tx::{DynamicPayload, Payload},
OnlineClient, SubstrateConfig,
};
use pop_common::call::TokenMetadata;

pub mod metadata;

Expand Down Expand Up @@ -47,12 +49,13 @@ pub fn construct_sudo_extrinsic(xt: DynamicPayload) -> Result<DynamicPayload, Er
///
/// # Arguments
/// * `client` - The client used to interact with the chain.
/// * `url` - Endpoint of the node.
/// * `xt` - The extrinsic to be signed and submitted.
/// * `suri` - The secret URI (e.g., mnemonic or private key) for signing the extrinsic.
pub async fn sign_and_submit_extrinsic(
pub async fn sign_and_submit_extrinsic<C: Payload>(
client: &OnlineClient<SubstrateConfig>,
url: &url::Url,
xt: DynamicPayload,
xt: C,
suri: &str,
) -> Result<String, Error> {
let signer = create_signer(suri)?;
Expand All @@ -65,18 +68,18 @@ pub async fn sign_and_submit_extrinsic(
.await
.map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?;

// Obtain required metadata and parse events. The following is using existing logic from
// `cargo-contract`, also used in calling contracts, due to simplicity and can be refactored in
// the future.
let metadata = client.metadata();
let display_events =
pop_common::call::DisplayEvents::from_events::<SubstrateConfig, pop_common::call::DefaultEnvironment>(&result, None, &metadata)?;
let token_metadata = TokenMetadata::query::<SubstrateConfig>(url).await?;
let events = DisplayEvents::from_events::<SubstrateConfig, DefaultEnvironment>(
&result, None, &metadata,
)?;
let events =
display_events.display_events::<pop_common::call::DefaultEnvironment>(pop_common::call::Verbosity::Default, &token_metadata)?;
events.display_events::<DefaultEnvironment>(Verbosity::Default, &token_metadata)?;

Ok(format!(
"Extrinsic Submitted with hash: {:?}\n\n{}",
result.extrinsic_hash(),
events,
))
Ok(format!("Extrinsic Submitted with hash: {:?}\n\n{}", result.extrinsic_hash(), events,))
}

/// Encodes the call data for a given extrinsic into a hexadecimal string.
Expand All @@ -103,9 +106,16 @@ pub fn decode_call_data(call_data: &str) -> Result<Vec<u8>, Error> {
.map_err(|e| Error::CallDataDecodingError(e.to_string()))
}

// This struct implements the [`Payload`] trait and is used to submit
// pre-encoded SCALE call data directly, without the dynamic construction of transactions.
struct CallData(Vec<u8>);
/// This struct implements the [`Payload`] trait and is used to submit
/// pre-encoded SCALE call data directly, without the dynamic construction of transactions.
pub struct CallData(Vec<u8>);

impl CallData {
/// Create a new instance of `CallData`.
pub fn new(data: Vec<u8>) -> CallData {
CallData(data)
}
}

impl Payload for CallData {
fn encode_call_data_to(
Expand All @@ -118,35 +128,12 @@ impl Payload for CallData {
}
}

/// Signs and submits a given extrinsic.
///
/// # Arguments
/// * `client` - Reference to an `OnlineClient` connected to the chain.
/// * `call_data` - SCALE encoded bytes representing the extrinsic's call data.
/// * `suri` - The secret URI (e.g., mnemonic or private key) for signing the extrinsic.
pub async fn sign_and_submit_extrinsic_with_call_data(
client: &OnlineClient<SubstrateConfig>,
call_data: Vec<u8>,
suri: &str,
) -> Result<String, Error> {
let signer = create_signer(suri)?;
let payload = CallData(call_data);
let result = client
.tx()
.sign_and_submit_then_watch_default(&payload, &signer)
.await
.map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?
.wait_for_finalized_success()
.await
.map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?;
Ok(format!("{:?}", result.extrinsic_hash()))
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{find_dispatchable_by_name, parse_chain_metadata, set_up_client};
use anyhow::Result;
use url::Url;

const ALICE_SURI: &str = "//Alice";
pub(crate) const POP_NETWORK_TESTNET_URL: &str = "wss://rpc1.paseo.popnetwork.xyz";
Expand Down Expand Up @@ -228,7 +215,7 @@ mod tests {
};
let xt = construct_extrinsic(&function, vec!["0x11".to_string()])?;
assert!(matches!(
sign_and_submit_extrinsic(&client, xt, ALICE_SURI).await,
sign_and_submit_extrinsic(&client, &Url::parse(POP_NETWORK_TESTNET_URL)?, xt, ALICE_SURI).await,
Err(Error::ExtrinsicSubmissionError(message)) if message.contains("PalletNameNotFound(\"WrongPallet\"))")
));
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion crates/pop-parachains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub use call::{
params::Param,
parse_chain_metadata, Function, Pallet,
},
set_up_client, sign_and_submit_extrinsic, sign_and_submit_extrinsic_with_call_data,
set_up_client, sign_and_submit_extrinsic, CallData,
};
pub use errors::Error;
pub use indexmap::IndexSet;
Expand Down

0 comments on commit 780ef4b

Please sign in to comment.