-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: wallet integration pop call chain #379
Changes from all commits
25e8df1
b5fa054
4cd63cd
bfbbd0f
8b1c8bf
b709778
d3c3b12
e82d018
ddbc759
604cecd
26b8f47
1f95acb
2a09b0a
81ed681
4c1431b
47ececb
40c7ce6
cfa4edb
e5e1786
e0b1f4e
10a3bb8
bbe8d6f
76b7fb5
3a8043b
c6aae64
620790e
9f1a410
8b4dd63
76fde8e
ba2e946
c07902a
dc2c989
304c706
15fbc45
bfb4619
ddf4768
d2bbc5f
018e8b1
3dee596
ad03c74
8391835
7935009
84bde34
ec56f5f
2cb9a70
9177dc0
64997f2
79a70cf
c607a10
a10ec5a
9140c14
fee5dd7
7aa808f
5b5ebf7
5cbb729
8c8d65c
0a3271c
6ea2002
ffb57f7
6c6de9a
16f2da6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,17 @@ | |
|
||
use std::path::Path; | ||
|
||
use crate::cli::{self, traits::*}; | ||
use crate::{ | ||
cli::{self, traits::*}, | ||
common::wallet::wait_for_signature, | ||
}; | ||
use anyhow::{anyhow, Result}; | ||
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, supported_actions, Action, CallData, DynamicPayload, Function, | ||
OnlineClient, Pallet, Param, SubstrateConfig, | ||
sign_and_submit_extrinsic, submit_signed_extrinsic, supported_actions, Action, CallData, | ||
DynamicPayload, Function, OnlineClient, Pallet, Param, Payload, SubstrateConfig, | ||
}; | ||
use url::Url; | ||
|
||
|
@@ -40,6 +43,9 @@ | |
/// - with a password "//Alice///SECRET_PASSWORD" | ||
#[arg(short, long)] | ||
suri: Option<String>, | ||
/// Use your browser wallet to sign the extrinsic. | ||
#[arg(name = "use-wallet", short('w'), long, default_value = "false", conflicts_with = "suri")] | ||
use_wallet: bool, | ||
/// SCALE encoded bytes representing the call data of the extrinsic. | ||
#[arg(name = "call", short, long, conflicts_with_all = ["pallet", "function", "args"])] | ||
call_data: Option<String>, | ||
|
@@ -95,7 +101,14 @@ | |
}; | ||
|
||
// Sign and submit the extrinsic. | ||
if let Err(e) = call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await { | ||
let result = if self.use_wallet { | ||
let call_data = xt.encode_call_data(&chain.client.metadata())?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realize it's not specific to this PR, but |
||
submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli).await | ||
} else { | ||
call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await | ||
}; | ||
|
||
if let Err(e) = result { | ||
display_message(&e.to_string(), false, &mut cli)?; | ||
break; | ||
} | ||
|
@@ -197,11 +210,25 @@ | |
// sudo. | ||
self.configure_sudo(chain, cli)?; | ||
|
||
// Resolve who is signing the extrinsic. | ||
// Resolve who is signing the extrinsic. If a `suri` was provided via the command line, | ||
// skip the prompt. | ||
let suri = match self.suri.as_ref() { | ||
Some(suri) => suri.clone(), | ||
None => | ||
cli.input("Signer of the extrinsic:").default_input(DEFAULT_URI).interact()?, | ||
None => { | ||
if !self.use_wallet { | ||
if cli.confirm("Do you want to use your browser wallet to sign the transaction? (Selecting 'No' will prompt you to manually enter the secret key URI for signing, e.g., '//Alice')") | ||
.initial_value(true) | ||
.interact()? { | ||
self.use_wallet = true; | ||
DEFAULT_URI.to_string() // Default value because the user is using the browser wallet. | ||
} | ||
else { | ||
cli.input("Signer of the extrinsic:").default_input(DEFAULT_URI).interact()? | ||
} | ||
} else { | ||
DEFAULT_URI.to_string() // Default value because the user is using the browser wallet. | ||
} | ||
}, | ||
}; | ||
|
||
return Ok(Call { | ||
|
@@ -210,6 +237,7 @@ | |
suri, | ||
skip_confirm: self.skip_confirm, | ||
sudo: self.sudo, | ||
use_wallet: self.use_wallet, | ||
}); | ||
} | ||
} | ||
|
@@ -222,11 +250,36 @@ | |
call_data: &str, | ||
cli: &mut impl Cli, | ||
) -> Result<()> { | ||
// Resolve who is signing the extrinsic. | ||
// Resolve who is signing the extrinsic. If a `suri` was provided via the command line, | ||
// skip the prompt. | ||
let mut use_wallet = self.use_wallet; | ||
let suri = match self.suri.as_ref() { | ||
Some(suri) => suri, | ||
None => &cli.input("Signer of the extrinsic:").default_input(DEFAULT_URI).interact()?, | ||
Some(suri) => suri.clone(), | ||
None => | ||
if !self.use_wallet { | ||
if cli.confirm("Do you want to use your browser wallet to sign the transaction? (Selecting 'No' will prompt you to manually enter the secret key URI for signing, e.g., '//Alice')") | ||
.initial_value(true) | ||
.interact()? { | ||
use_wallet = true; | ||
DEFAULT_URI.to_string() | ||
} | ||
else { | ||
cli.input("Signer of the extrinsic:").default_input(DEFAULT_URI).interact()? | ||
} | ||
} else { | ||
DEFAULT_URI.to_string() | ||
}, | ||
}; | ||
// Perform signing steps with wallet integration and return early. | ||
if use_wallet { | ||
let call_data_bytes = | ||
decode_call_data(call_data).map_err(|err| anyhow!("{}", format!("{err:?}")))?; | ||
submit_extrinsic_with_wallet(client, &url, call_data_bytes, cli) | ||
Check warning on line 277 in crates/pop-cli/src/commands/call/chain.rs GitHub Actions / clippythis expression creates a reference which is immediately dereferenced by the compiler
|
||
.await | ||
.map_err(|err| anyhow!("{}", format!("{err:?}")))?; | ||
display_message("Call complete.", true, cli)?; | ||
return Ok(()); | ||
} | ||
cli.info(format!("Encoded call data: {}", call_data))?; | ||
if !self.skip_confirm && | ||
!cli.confirm("Do you want to submit the extrinsic?") | ||
|
@@ -244,7 +297,7 @@ | |
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(client, url, CallData::new(call_data_bytes), suri) | ||
let result = sign_and_submit_extrinsic(client, url, CallData::new(call_data_bytes), &suri) | ||
.await | ||
.map_err(|err| anyhow!("{}", format!("{err:?}")))?; | ||
|
||
|
@@ -283,6 +336,7 @@ | |
self.function = None; | ||
self.args.clear(); | ||
self.sudo = false; | ||
self.use_wallet = false; | ||
} | ||
|
||
// Function to check if all required fields are specified. | ||
|
@@ -334,6 +388,8 @@ | |
/// - for a dev account "//Alice" | ||
/// - with a password "//Alice///SECRET_PASSWORD" | ||
suri: String, | ||
/// Whether to use your browser wallet to sign the extrinsic. | ||
use_wallet: bool, | ||
/// Whether to automatically sign and submit the extrinsic without prompting for confirmation. | ||
skip_confirm: bool, | ||
/// Whether to dispatch the function call with `Root` origin. | ||
|
@@ -411,14 +467,45 @@ | |
.collect(); | ||
full_message.push_str(&format!(" --args {}", args.join(" "))); | ||
} | ||
full_message.push_str(&format!(" --url {} --suri {}", chain.url, self.suri)); | ||
full_message.push_str(&format!(" --url {}", chain.url)); | ||
if self.use_wallet { | ||
full_message.push_str(" --use-wallet"); | ||
} else { | ||
full_message.push_str(&format!(" --suri {}", self.suri)); | ||
} | ||
if self.sudo { | ||
full_message.push_str(" --sudo"); | ||
} | ||
full_message | ||
} | ||
} | ||
|
||
// Sign and submit an extrinsic using wallet integration. | ||
async fn submit_extrinsic_with_wallet( | ||
client: &OnlineClient<SubstrateConfig>, | ||
url: &Url, | ||
call_data: Vec<u8>, | ||
cli: &mut impl Cli, | ||
) -> Result<()> { | ||
let maybe_payload = wait_for_signature(call_data, url.to_string()).await?; | ||
if let Some(payload) = maybe_payload { | ||
cli.success("Signed payload received.")?; | ||
let spinner = cliclack::spinner(); | ||
spinner.start( | ||
"Submitting the extrinsic and then waiting for finalization, please be patient...", | ||
); | ||
|
||
let result = submit_signed_extrinsic(client.clone(), payload) | ||
.await | ||
.map_err(|err| anyhow!("{}", format!("{err:?}")))?; | ||
|
||
spinner.stop(format!("Extrinsic submitted with hash: {:?}", result)); | ||
} else { | ||
display_message("No signed payload received.", false, cli)?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
// Displays a message to the user, with formatting based on the success status. | ||
fn display_message(message: &str, success: bool, cli: &mut impl Cli) -> Result<()> { | ||
if success { | ||
|
@@ -642,7 +729,7 @@ | |
) | ||
.expect_input("The value for `remark` might be too large to enter. You may enter the path to a file instead.", "0x11".into()) | ||
.expect_confirm("Would you like to dispatch this function call with `Root` origin?", true) | ||
.expect_input("Signer of the extrinsic:", "//Bob".into()); | ||
.expect_confirm("Do you want to use your browser wallet to sign the transaction? (Selecting 'No' will prompt you to manually enter the secret key URI for signing, e.g., '//Alice')", true); | ||
|
||
let chain = call_config.configure_chain(&mut cli).await?; | ||
assert_eq!(chain.url, Url::parse(POP_NETWORK_TESTNET_URL)?); | ||
|
@@ -651,9 +738,10 @@ | |
assert_eq!(call_chain.function.pallet, "System"); | ||
assert_eq!(call_chain.function.name, "remark"); | ||
assert_eq!(call_chain.args, ["0x11".to_string()].to_vec()); | ||
assert_eq!(call_chain.suri, "//Bob"); | ||
assert_eq!(call_chain.suri, "//Alice"); // Default value | ||
assert!(call_chain.use_wallet); | ||
assert!(call_chain.sudo); | ||
assert_eq!(call_chain.display(&chain), "pop call chain --pallet System --function remark --args \"0x11\" --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Bob --sudo"); | ||
assert_eq!(call_chain.display(&chain), "pop call chain --pallet System --function remark --args \"0x11\" --url wss://rpc1.paseo.popnetwork.xyz/ --use-wallet --sudo"); | ||
cli.verify() | ||
} | ||
|
||
|
@@ -714,6 +802,7 @@ | |
}, | ||
args: vec!["0x11".to_string()].to_vec(), | ||
suri: DEFAULT_URI.to_string(), | ||
use_wallet: false, | ||
skip_confirm: false, | ||
sudo: false, | ||
}; | ||
|
@@ -753,6 +842,7 @@ | |
function: find_dispatchable_by_name(&pallets, "System", "remark")?.clone(), | ||
args: vec!["0x11".to_string()].to_vec(), | ||
suri: DEFAULT_URI.to_string(), | ||
use_wallet: false, | ||
skip_confirm: false, | ||
sudo: false, | ||
}; | ||
|
@@ -776,11 +866,13 @@ | |
args: vec![].to_vec(), | ||
url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), | ||
suri: None, | ||
use_wallet: false, | ||
skip_confirm: false, | ||
call_data: Some("0x00000411".to_string()), | ||
sudo: false, | ||
}; | ||
let mut cli = MockCli::new() | ||
.expect_confirm("Do you want to use your browser wallet to sign the transaction? (Selecting 'No' will prompt you to manually enter the secret key URI for signing, e.g., '//Alice')", false) | ||
.expect_input("Signer of the extrinsic:", "//Bob".into()) | ||
.expect_confirm("Do you want to submit the extrinsic?", false) | ||
.expect_outro_cancel("Extrinsic with call data 0x00000411 was not submitted."); | ||
|
@@ -803,8 +895,9 @@ | |
pallet: None, | ||
function: None, | ||
args: vec![].to_vec(), | ||
url: Some(Url::parse("wss://polkadot-rpc.publicnode.com")?), | ||
url: Some(Url::parse(POLKADOT_NETWORK_URL)?), | ||
suri: Some("//Alice".to_string()), | ||
use_wallet: false, | ||
skip_confirm: false, | ||
call_data: Some("0x00000411".to_string()), | ||
sudo: true, | ||
|
@@ -836,6 +929,7 @@ | |
function: Some("remark".to_string()), | ||
args: vec!["0x11".to_string()].to_vec(), | ||
url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), | ||
use_wallet: true, | ||
suri: Some(DEFAULT_URI.to_string()), | ||
skip_confirm: false, | ||
call_data: None, | ||
|
@@ -846,6 +940,7 @@ | |
assert_eq!(call_config.function, None); | ||
assert_eq!(call_config.args.len(), 0); | ||
assert!(!call_config.sudo); | ||
assert!(!call_config.use_wallet); | ||
Ok(()) | ||
} | ||
|
||
|
@@ -857,6 +952,7 @@ | |
args: vec!["0x11".to_string()].to_vec(), | ||
url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), | ||
suri: Some(DEFAULT_URI.to_string()), | ||
use_wallet: false, | ||
skip_confirm: false, | ||
call_data: None, | ||
sudo: false, | ||
|
@@ -875,6 +971,7 @@ | |
args: vec!["2000".to_string(), "0x1".to_string(), "0x12".to_string()].to_vec(), | ||
url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), | ||
suri: Some(DEFAULT_URI.to_string()), | ||
use_wallet: false, | ||
call_data: None, | ||
skip_confirm: false, | ||
sudo: false, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed for consistency. In the main feat PR we are using other libraries, so refactored to use the same and remove this dependency.