onemoney-protocol 0.18.0

Official Rust SDK for OneMoney Protocol - L1 blockchain network client
Documentation
# 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(())
}
```