use quantus_cli::{
approve_proposal, create_multisig, get_multisig_info, get_proposal_info, list_proposals,
parse_multisig_amount, predict_multisig_address, propose_transfer,
wallet::{load_keypair_from_wallet, WalletManager},
QuantusClient, Result,
};
use sp_core::crypto::Ss58Codec;
#[tokio::main]
async fn main() -> Result<()> {
println!("🔐 Quantus Multisig Library Usage Example");
println!("==========================================\n");
let node_url = "ws://127.0.0.1:9944";
let quantus_client = QuantusClient::new(node_url).await?;
println!("📡 Connected to node: {}", node_url);
println!();
let wallet_manager = WalletManager::new()?;
println!("👥 Loading test wallets...");
let alice_keypair = load_keypair_from_wallet("crystal_alice", None, None)?;
let bob_keypair = load_keypair_from_wallet("crystal_bob", None, None)?;
let _charlie_keypair = load_keypair_from_wallet("crystal_charlie", None, None)?;
let alice_addr = wallet_manager
.find_wallet_address("crystal_alice")?
.expect("Alice wallet not found");
let bob_addr = wallet_manager
.find_wallet_address("crystal_bob")?
.expect("Bob wallet not found");
let charlie_addr = wallet_manager
.find_wallet_address("crystal_charlie")?
.expect("Charlie wallet not found");
println!(" Alice: {}", alice_addr);
println!(" Bob: {}", bob_addr);
println!(" Charlie: {}", charlie_addr);
println!();
let alice_account = parse_address(&alice_addr)?;
let bob_account = parse_address(&bob_addr)?;
let charlie_account = parse_address(&charlie_addr)?;
println!("🔐 Creating 2-of-3 multisig...");
let signers = vec![alice_account.clone(), bob_account.clone(), charlie_account.clone()];
let threshold = 2;
let nonce = 0;
let predicted_address = predict_multisig_address(signers.clone(), threshold, nonce);
println!("📍 Predicted address: {}", predicted_address);
let (tx_hash, multisig_address) =
create_multisig(&quantus_client, &alice_keypair, signers, threshold, nonce, true).await?;
println!("✅ Multisig created!");
println!(" Tx hash: 0x{}", hex::encode(tx_hash));
if let Some(addr) = &multisig_address {
println!(" Address: {}", addr);
}
println!();
if let Some(addr) = &multisig_address {
let multisig_account = parse_address(addr)?;
println!("📋 Querying multisig info...");
if let Some(info) = get_multisig_info(&quantus_client, multisig_account.clone()).await? {
println!(" Address: {}", info.address);
println!(" Balance: {} (raw units)", info.balance);
println!(" Threshold: {}", info.threshold);
println!(" Signers: {}", info.signers.len());
for (i, signer) in info.signers.iter().enumerate() {
println!(" {}. {}", i + 1, signer);
}
println!(" Proposal Nonce: {}", info.proposal_nonce);
println!();
}
println!("💰 Parsing amounts...");
let amount_1 = parse_multisig_amount("10")?; let amount_2 = parse_multisig_amount("10.5")?; let amount_3 = parse_multisig_amount("0.001")?;
println!(" 10 QUAN = {} (raw)", amount_1);
println!(" 10.5 QUAN = {} (raw)", amount_2);
println!(" 0.001 QUAN = {} (raw)", amount_3);
println!();
println!("📝 Creating transfer proposal...");
let expiry = 1000; let amount = parse_multisig_amount("10")?;
let propose_tx_hash = propose_transfer(
&quantus_client,
&alice_keypair,
multisig_account.clone(),
bob_account.clone(),
amount,
expiry,
)
.await?;
println!("✅ Proposal submitted!");
println!(" Tx hash: 0x{}", hex::encode(propose_tx_hash));
println!(" Check events for proposal ID");
println!();
println!("📋 Listing all proposals...");
let proposals = list_proposals(&quantus_client, multisig_account.clone()).await?;
println!(" Found {} proposal(s)", proposals.len());
for proposal in &proposals {
println!();
println!(" Proposal #{}:", proposal.id);
println!(" Proposer: {}", proposal.proposer);
println!(" Expiry: block {}", proposal.expiry);
println!(" Status: {:?}", proposal.status);
println!(" Approvals: {}", proposal.approvals.len());
println!(" Deposit: {} (raw)", proposal.deposit);
}
println!();
if !proposals.is_empty() {
let proposal_id = proposals[0].id;
println!("🔍 Querying proposal #{}...", proposal_id);
if let Some(proposal) =
get_proposal_info(&quantus_client, multisig_account.clone(), proposal_id).await?
{
println!(" Proposer: {}", proposal.proposer);
println!(" Call data size: {} bytes", proposal.call_data.len());
println!(" Expiry: block {}", proposal.expiry);
println!(" Approvals: {}", proposal.approvals.len());
}
println!();
println!("✅ Approving proposal #{}...", proposal_id);
let approve_tx_hash = approve_proposal(
&quantus_client,
&bob_keypair,
multisig_account.clone(),
proposal_id,
)
.await?;
println!("✅ Approval submitted!");
println!(" Tx hash: 0x{}", hex::encode(approve_tx_hash));
println!(" (Will auto-execute at threshold)");
println!();
}
}
println!("✨ Example complete!");
println!();
println!("📚 Available library functions:");
println!(" - predict_multisig_address() - Calculate address before creating");
println!(" - create_multisig() - Create with nonce for deterministic addresses");
println!(" - propose_transfer()");
println!(" - propose_custom()");
println!(" - approve_proposal()");
println!(" - cancel_proposal()");
println!(" - get_multisig_info()");
println!(" - get_proposal_info()");
println!(" - list_proposals()");
println!(" - approve_dissolve_multisig() - Requires threshold approvals");
println!(" - parse_multisig_amount()");
println!();
println!("💡 Note: Multisig deposits are RETURNED to creator upon dissolution");
Ok(())
}
fn parse_address(ss58: &str) -> Result<subxt::ext::subxt_core::utils::AccountId32> {
use sp_core::crypto::AccountId32;
let (account_id, _) = AccountId32::from_ss58check_with_version(ss58).map_err(|e| {
quantus_cli::error::QuantusError::Generic(format!("Invalid address: {:?}", e))
})?;
let bytes: [u8; 32] = *account_id.as_ref();
Ok(subxt::ext::subxt_core::utils::AccountId32::from(bytes))
}