use aptos_sdk::{
Aptos, AptosConfig,
account::MultiEd25519Account,
crypto::Ed25519PrivateKey,
transaction::{EntryFunction, TransactionBuilder, TransactionPayload},
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
println!("=== Multi-Signature Account Example ===\n");
let aptos = Aptos::new(AptosConfig::testnet())?;
println!("Connected to testnet (chain_id: {})", aptos.chain_id().id());
println!("\n--- Part 1: Create 2-of-3 Multi-Sig Account ---");
let key1 = Ed25519PrivateKey::generate();
let key2 = Ed25519PrivateKey::generate();
let key3 = Ed25519PrivateKey::generate();
println!("Generated 3 Ed25519 keys:");
println!(" Key 1: {}", key1.public_key());
println!(" Key 2: {}", key2.public_key());
println!(" Key 3: {}", key3.public_key());
let multi_account = MultiEd25519Account::new(
vec![key1.clone(), key2.clone(), key3.clone()],
2, )?;
println!("\nMulti-sig account created:");
println!(" Address: {}", multi_account.address());
println!(
" Threshold: {}-of-{}",
multi_account.threshold(),
multi_account.num_keys()
);
println!(" Can sign: {}", multi_account.can_sign());
println!("\n--- Part 2: Fund Multi-Sig Account ---");
aptos
.fund_account(multi_account.address(), 100_000_000)
.await?;
println!("Funded multi-sig account with 1 APT");
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
let balance = aptos.get_balance(multi_account.address()).await?;
println!("Balance: {} APT", balance as f64 / 100_000_000.0);
println!("\n--- Part 3: Sign and Submit Transaction ---");
let recipient = Ed25519PrivateKey::generate().public_key().to_address();
let payload = EntryFunction::apt_transfer(recipient, 1_000_000)?;
let seq_num = aptos.get_sequence_number(multi_account.address()).await?;
let raw_txn = TransactionBuilder::new()
.sender(multi_account.address())
.sequence_number(seq_num)
.payload(TransactionPayload::EntryFunction(payload))
.chain_id(aptos.chain_id())
.build()?;
let signed = aptos_sdk::transaction::builder::sign_transaction(&raw_txn, &multi_account)?;
println!(
"Signed with threshold {}-of-{}",
multi_account.threshold(),
multi_account.num_keys()
);
let result = aptos.submit_and_wait(&signed, None).await?;
let success = result.data.get("success").and_then(|v| v.as_bool());
println!("Transaction success: {:?}", success);
println!("\n--- Part 4: Distributed Signing Scenario ---");
let pub_keys = vec![key1.public_key(), key2.public_key(), key3.public_key()];
let party1 = MultiEd25519Account::from_keys(pub_keys.clone(), vec![(0, key1.clone())], 2)?;
println!("Party 1: owns key index 0, can_sign={}", party1.can_sign());
let party2 = MultiEd25519Account::from_keys(pub_keys.clone(), vec![(1, key2.clone())], 2)?;
println!("Party 2: owns key index 1, can_sign={}", party2.can_sign());
let party3 = MultiEd25519Account::from_keys(pub_keys.clone(), vec![(2, key3.clone())], 2)?;
println!("Party 3: owns key index 2, can_sign={}", party3.can_sign());
let payload2 = EntryFunction::apt_transfer(recipient, 500_000)?;
let seq_num2 = aptos.get_sequence_number(multi_account.address()).await?;
let raw_txn2 = TransactionBuilder::new()
.sender(multi_account.address())
.sequence_number(seq_num2)
.payload(TransactionPayload::EntryFunction(payload2))
.chain_id(aptos.chain_id())
.build()?;
let message = raw_txn2.signing_message()?;
let contrib1 = party1.create_signature_contribution(&message, 0)?;
println!("Party 1 signed at index {}", contrib1.0);
let contrib3 = party3.create_signature_contribution(&message, 2)?;
println!("Party 3 signed at index {}", contrib3.0);
let aggregated = MultiEd25519Account::aggregate_signatures(vec![contrib1, contrib3])?;
println!("Aggregated {} signatures", aggregated.num_signatures());
let multi_pk = multi_account.public_key();
match multi_pk.verify(&message, &aggregated) {
Ok(_) => println!("✓ Aggregated signature verified!"),
Err(e) => println!("✗ Verification failed: {}", e),
}
println!("\n--- Part 5: View-Only Multi-Sig ---");
let view_only = MultiEd25519Account::view_only(pub_keys, 2)?;
println!("View-only account created:");
println!(" Address: {}", view_only.address());
println!(" Can sign: {}", view_only.can_sign());
println!(
" Same address as full account: {}",
view_only.address() == multi_account.address()
);
println!("\n✓ Multi-signature example completed!");
Ok(())
}