# Multi-Signature Account SDK Guide
This guide demonstrates how to use the OneMoney SDK for multi-signature operations.
## Table of Contents
1. [Creating a Multi-Sig Account](#creating-a-multi-sig-account)
2. [Sending Transactions from Multi-Sig Account](#sending-transactions-from-multi-sig-account)
3. [Offline Signing Workflow](#offline-signing-workflow)
4. [Weighted Thresholds Example](#weighted-thresholds-example)
5. [Security Best Practices](#security-best-practices)
6. [API Reference](#api-reference)
7. [Complete Example](#complete-example)
---
## Creating a Multi-Sig Account
### Step 1: Define Signers and Threshold
```rust
use onemoney_protocol::{Client, utils::{SignerConfig, ThresholdConfig}};
use onemoney_protocol::crypto::sign_multisig_transaction_payload;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::testnet()?;
// Define signers (33-byte SEC1 compressed public keys)
let alice_pubkey = vec![/* 33 bytes */];
let bob_pubkey = vec![/* 33 bytes */];
let charlie_pubkey = vec![/* 33 bytes */];
let alice = SignerConfig::new(alice_pubkey, 1)?; // weight = 1
let bob = SignerConfig::new(bob_pubkey, 1)?; // weight = 1
let charlie = SignerConfig::new(charlie_pubkey, 1)?; // weight = 1
// 2-of-3 multi-sig: any 2 signers can authorize transactions
let threshold = ThresholdConfig::new(2)?; // total weight needed = 2
Ok(())
}
```
### Step 2: Derive Multi-Sig Address
```rust
use onemoney_protocol::utils::derive_multisig_address;
// Derive the multi-sig account address (deterministic)
let multisig_address = derive_multisig_address(
&[alice.clone(), bob.clone(), charlie.clone()],
&threshold
)?;
println!("Multi-sig account address: {}", multisig_address);
```
### Step 3: Create and Submit Creation Transaction
```rust
// Get creator's nonce
let creator_address = /* your address */;
let creator_nonce = client.get_account_nonce(creator_address).await?.nonce;
// Create and submit in one step
let (multisig_address, tx_response) = client.submit_create_multisig_account(
&[alice, bob, charlie],
&threshold,
21210, // chain_id
creator_nonce, // nonce of the creator, not multi-sig account
creator_key, // creator private key
).await?;
println!("Transaction hash: {}", tx_response.hash);
```
---
## Sending Transactions from Multi-Sig Account
Once the multi-sig account is created, it can send transactions by collecting signatures from its signers.
### Step 1: Create Payment Payload
```rust
use alloy_primitives::{Address, U256};
// Get multi-sig account's nonce
let multisig_nonce = client.get_account_nonce(multisig_address).await?.nonce;
// Create payment transaction
let recipient = Address::from_str("0x...")?;
let payload = client.create_multisig_payment_payload(
recipient, // recipient
Address::ZERO, // token (ZERO = native token)
U256::from(1000), // amount
21210, // chain_id
multisig_nonce, // multi-sig account's nonce
);
```
### Step 2: Collect Signatures from Signers
```rust
use onemoney_protocol::utils::MultiSigSignatureCollector;
let mut collector = MultiSigSignatureCollector::new();
// Alice signs (can be offline)
let alice_sig = sign_multisig_transaction_payload(&payload, multisig_address, alice_private_key)?;
collector.add_signature(alice_pubkey.clone(), alice_sig);
// Bob signs (can be offline)
let bob_sig = sign_multisig_transaction_payload(&payload, multisig_address, bob_private_key)?;
collector.add_signature(bob_pubkey.clone(), bob_sig);
// Now we have 2 signatures (weight = 2), which meets the threshold
println!("Collected {} signatures", collector.signature_count());
```
### Step 3: Create Multi-Sig Transaction
```rust
// Get all signatures
let signatures = collector.signatures();
// Submit multi-sig transaction
let response = client
.submit_multisig_payment_transaction(payload, multisig_address, signatures)
.await?;
println!("Transaction hash: {}", response.hash);
```
---
## Offline Signing Workflow
Multi-sig accounts support **offline signing**, where signers don't need to be online simultaneously.
### Coordinator Side (Online)
```rust
// 1. Create transaction payload
let payload = client.create_multisig_payment_payload(
recipient,
Address::ZERO,
U256::from(1000),
21210,
multisig_nonce,
);
// 2. Serialize payload and share with signers
let payload_json = serde_json::to_string(&payload)?;
// Send payload_json to each signer via secure channel
```
### Signer Side (Offline)
```rust
use onemoney_protocol::crypto::sign_multisig_transaction_payload;
// 1. Receive payload from coordinator
let payload: PaymentPayload = serde_json::from_str(&payload_json)?;
let multisig_address = /* multi-sig account address from coordinator */;
// 2. Sign with private key
let my_private_key = /* load from secure storage */;
let signature = sign_multisig_transaction_payload(&payload, multisig_address, my_private_key)?;
// 3. Return signature to coordinator
let sig_json = serde_json::to_string(&signature)?;
// Send back to coordinator
```
### Coordinator Side (Aggregation)
```rust
use onemoney_protocol::utils::MultiSigSignatureCollector;
let mut collector = MultiSigSignatureCollector::new();
// Collect signatures from each signer
for (pubkey, sig_json) in received_signatures {
let signature = serde_json::from_str(&sig_json)?;
collector.add_signature(pubkey, signature);
}
// Create and submit multi-sig transaction
let signatures = collector.signatures();
let response = client
.submit_multisig_payment_transaction(payload, multisig_address, signatures)
.await?;
println!("Transaction hash: {}", response.hash);
```
---
## Weighted Thresholds Example
Multi-sig accounts support weighted voting:
```rust
// CEO has weight 3, CFO has weight 2, others have weight 1
let ceo = SignerConfig::new(ceo_pubkey, 3)?;
let cfo = SignerConfig::new(cfo_pubkey, 2)?;
let engineer1 = SignerConfig::new(eng1_pubkey, 1)?;
let engineer2 = SignerConfig::new(eng2_pubkey, 1)?;
// Require weight >= 4 to authorize
let threshold = ThresholdConfig::new(4)?;
// Valid combinations:
// - CEO (3) + CFO (2) = 5
// - CEO (3) + engineer1 (1) = 4
// - CFO (2) + engineer1 (1) + engineer2 (1) = 4
// - engineer1 (1) + engineer2 (1) = 2 (insufficient)
```
---
## Security Best Practices
1. **Store Private Keys Securely**: Use hardware wallets or secure key management systems
2. **Verify Payload**: Each signer should verify the transaction details before signing
3. **Threshold Design**: Choose thresholds that balance security and operational efficiency
4. **Public Key Format**: Ensure all public keys are 33-byte SEC1 compressed format
5. **Offline Signing**: Sign transactions offline to minimize key exposure
---
## API Reference
### Core Functions
- `derive_multisig_address(signers, threshold)` - Derive multi-sig address
- `client.create_multisig_account_payload(...)` - Create account creation payload
- `client.submit_create_multisig_account(...)` - Create and submit account creation
- `client.create_multisig_payment_payload(...)` - Create payment payload
- `client.submit_multisig_payment_transaction(...)` - Submit multi-sig payment
### Types
- `SignerConfig { public_key: Vec<u8>, weight: u8 }` - Signer configuration
- `ThresholdConfig { threshold: u16 }` - Threshold configuration
- `MultiSigSignatureCollector` - Signature aggregation helper
---
## Complete Example
```rust
use onemoney_protocol::{Client, utils::{SignerConfig, ThresholdConfig, MultiSigSignatureCollector}};
use onemoney_protocol::crypto::sign_multisig_transaction_payload;
use alloy_primitives::{Address, U256};
use om_primitives_types::transaction::Signed;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::testnet()?;
// 1. Create 2-of-3 multi-sig account
let alice = SignerConfig::new(alice_pubkey, 1)?;
let bob = SignerConfig::new(bob_pubkey, 1)?;
let charlie = SignerConfig::new(charlie_pubkey, 1)?;
let threshold = ThresholdConfig::new(2)?;
let (multisig_address, create_payload) = client.create_multisig_account_payload(
&[alice.clone(), bob.clone(), charlie.clone()],
&threshold,
21210,
0,
)?;
// ... create and submit account creation transaction ...
// 2. Send payment from multi-sig account
let multisig_nonce = client.get_account_nonce(multisig_address).await?.nonce;
let payment_payload = client.create_multisig_payment_payload(
recipient,
Address::ZERO,
U256::from(1000),
21210,
multisig_nonce,
);
// 3. Collect signatures (Alice + Bob)
let mut collector = MultiSigSignatureCollector::new();
let alice_sig = sign_multisig_transaction_payload(&payment_payload, multisig_address, alice_key)?;
collector.add_signature(alice_pubkey, alice_sig);
let bob_sig = sign_multisig_transaction_payload(&payment_payload, multisig_address, bob_key)?;
collector.add_signature(bob_pubkey, bob_sig);
// 4. Create and submit multi-sig transaction
let signatures = collector.signatures();
let response = client
.submit_multisig_payment_transaction(payment_payload, multisig_address, signatures)
.await?;
println!("Transaction hash: {}", response.hash);
Ok(())
}
```