A secure multi-signature wallet requiring M-of-N signatures for transaction execution on Chert Coin blockchain.
- ✅ M-of-N Signatures - Require multiple approvals for transactions
- ✅ Flexible Configuration - Configurable signers and threshold
- ✅ Transaction Queue - Propose, approve, and execute transactions
- ✅ Owner Management - Add/remove signers with consensus
- ✅ Threshold Updates - Change required signature count
- ✅ Token Support - Manage CRC-20 tokens and native CHERT
- ✅ Transaction History - Track all proposals and executions
- ✅ Cancellation - Revoke pending transactions
- ✅ Time Locks - Optional execution delays for security
- 🏢 Corporate Treasury - Require multiple executives to approve spending
- 🤝 Joint Accounts - Shared wallets for partnerships or families
- 🏦 DAO Treasury - Multi-party control over community funds
- 🔐 Cold Storage - Distributed key security for large holdings
- 💼 Escrow Services - Third-party transaction mediation
- 🎯 Project Funds - Team-controlled project budgets
M-of-N Multisig:
- N = Total number of signers/owners
- M = Required signatures (threshold)
- M ≤ N
- Common configurations: 2/3, 3/5, 5/7
Example: 3-of-5 multisig
- 5 owners can propose transactions
- 3 signatures required to execute
- Any 3 of the 5 can approve
1. Propose → Any owner submits transaction
2. Approve → M owners sign the transaction
3. Execute → Anyone calls execute after M signatures
4. Complete → Transaction executes on blockchain
fn initialize(owners: Vec<String>, threshold: u64)Creates a new multisig wallet with initial configuration.
Parameters:
owners- Array of owner addresses (signers)threshold- Number of required signatures (M)
Requirements:
- Threshold > 0
- Threshold ≤ owners.length
- Owners must be unique addresses
- Minimum 1 owner
- Maximum 50 owners (gas limit protection)
Events:
WalletCreated { owners, threshold }
fn submit_transaction(
to: String,
value: u64,
data: Vec<u8>,
description: String
) -> u64Proposes a new transaction for approval.
Parameters:
to- Recipient addressvalue- Amount of CHERT to senddata- Contract call data (empty for simple transfers)description- Human-readable transaction description
Returns: Transaction ID
Requirements:
- Caller must be an owner
- Recipient address must be valid
Events:
TransactionSubmitted { tx_id, proposer, to, value }
Automatic Approval: Proposer automatically approves the transaction
fn approve_transaction(tx_id: u64)Adds caller's signature to a pending transaction.
Parameters:
tx_id- Transaction ID to approve
Requirements:
- Caller must be an owner
- Transaction must exist and be pending
- Caller must not have already approved
- Transaction must not be executed or cancelled
Events:
TransactionApproved { tx_id, approver }
fn revoke_approval(tx_id: u64)Removes caller's signature from a pending transaction.
Parameters:
tx_id- Transaction ID to revoke
Requirements:
- Caller must be an owner
- Transaction must be pending
- Caller must have previously approved
- Transaction must not be executed
Events:
ApprovalRevoked { tx_id, owner }
fn execute_transaction(tx_id: u64)Executes a transaction that has reached threshold signatures.
Parameters:
tx_id- Transaction ID to execute
Requirements:
- Transaction must exist and be pending
- Must have M or more approvals
- If time-locked, delay period must have passed
- Wallet must have sufficient balance (for value transfers)
Events:
TransactionExecuted { tx_id, executor }- On failure:
ExecutionFailed { tx_id, reason }
fn cancel_transaction(tx_id: u64)Cancels a pending transaction (requires M signatures).
Parameters:
tx_id- Transaction ID to cancel
Requirements:
- Transaction must be pending
- Must have M approvals for cancellation
- Uses same signature mechanism as execution
Events:
TransactionCancelled { tx_id }
fn add_owner(owner: String)Adds a new signer to the wallet (via multisig transaction).
Parameters:
owner- New owner address to add
Requirements:
- Must be called via
execute_transaction(requires M signatures) - Owner must not already exist
- Total owners must not exceed maximum (50)
Events:
OwnerAdded { owner }
fn remove_owner(owner: String)Removes a signer from the wallet (via multisig transaction).
Parameters:
owner- Owner address to remove
Requirements:
- Must be called via
execute_transaction(requires M signatures) - Owner must exist
- Remaining owners must be ≥ threshold
- Cannot remove last owner
Events:
OwnerRemoved { owner }
fn change_threshold(new_threshold: u64)Updates the required signature count (via multisig transaction).
Parameters:
new_threshold- New threshold value (M)
Requirements:
- Must be called via
execute_transaction(requires M signatures) - Threshold > 0
- Threshold ≤ total owners
Events:
ThresholdChanged { old_threshold, new_threshold }
fn set_time_lock(tx_id: u64, delay_seconds: u64)Adds an execution delay to a transaction for additional security.
Parameters:
tx_id- Transaction IDdelay_seconds- Seconds to wait after threshold reached
Requirements:
- Caller must be an owner
- Transaction must be pending
- Delay must be reasonable (< 30 days)
Events:
TimeLockSet { tx_id, unlock_time }
fn get_owners() -> Vec<String>Returns all current wallet owners.
Returns: Array of owner addresses
fn get_threshold() -> u64Returns the current signature threshold.
Returns: Required signature count (M)
fn get_transaction(tx_id: u64) -> TransactionReturns details of a specific transaction.
Returns: Transaction struct with all details
struct Transaction {
to: String,
value: u64,
data: Vec<u8>,
description: String,
proposer: String,
approvals: Vec<String>,
executed: bool,
cancelled: bool,
timestamp: u64,
time_lock: Option<u64>,
}fn get_transaction_count() -> u64Returns the total number of transactions (including executed/cancelled).
Returns: Transaction count
fn get_pending_transactions() -> Vec<u64>Returns IDs of all pending (not executed/cancelled) transactions.
Returns: Array of transaction IDs
fn has_approved(tx_id: u64, owner: String) -> boolChecks if an owner has approved a transaction.
Returns: True if approved, false otherwise
fn is_owner(address: String) -> boolChecks if an address is a wallet owner.
Returns: True if owner, false otherwise
fn get_approval_count(tx_id: u64) -> u64Returns the number of approvals for a transaction.
Returns: Approval count
fn can_execute(tx_id: u64) -> boolChecks if a transaction is ready to execute.
Returns: True if has threshold signatures and time lock expired
// Emitted when wallet is created
event WalletCreated {
owners: Vec<String>,
threshold: u64,
}
// Emitted when transaction is proposed
event TransactionSubmitted {
tx_id: u64,
proposer: String,
to: String,
value: u64,
description: String,
}
// Emitted when transaction is approved
event TransactionApproved {
tx_id: u64,
approver: String,
approval_count: u64,
}
// Emitted when approval is revoked
event ApprovalRevoked {
tx_id: u64,
owner: String,
}
// Emitted when transaction executes successfully
event TransactionExecuted {
tx_id: u64,
executor: String,
}
// Emitted when execution fails
event ExecutionFailed {
tx_id: u64,
reason: String,
}
// Emitted when transaction is cancelled
event TransactionCancelled {
tx_id: u64,
}
// Emitted when owner is added
event OwnerAdded {
owner: String,
}
// Emitted when owner is removed
event OwnerRemoved {
owner: String,
}
// Emitted when threshold changes
event ThresholdChanged {
old_threshold: u64,
new_threshold: u64,
}
// Emitted when time lock is set
event TimeLockSet {
tx_id: u64,
unlock_time: u64,
}// Owner management
Vector<String>: "owners"
Map<String, bool>: "is_owner"
u64: "threshold"
// Transaction storage
u64: "transaction_count"
Map<u64, Transaction>: "transactions"
Map<u64, Vec<String>>: "approvals" // tx_id -> [approvers]
Map<(u64, String), bool>: "has_approved" // (tx_id, owner) -> bool
Vector<u64>: "pending_txs"
// Time locks
Map<u64, u64>: "time_locks" // tx_id -> unlock_timestamp
// Configuration
u64: "max_owners" // 50
u64: "max_time_lock" // 30 days- Strict owner validation on all operations
- Prevent signature reuse across transactions
- Nonce tracking to prevent replay attacks
- Cannot execute without M signatures
- Threshold changes require existing threshold
- Cannot set threshold > owner count
- Owner changes require multisig approval
- Cannot remove owner if below threshold
- Prevent duplicate owner addresses
- Reentrancy protection on execution
- State changes before external calls
- Execution failure handling (doesn't revert entire transaction)
- Balance checks before transfers
- Optional delays for high-value transactions
- Prevents immediate execution after threshold
- Cancellation allowed during time lock period
- Overflow protection on all arithmetic
- Threshold bounds checking
- Transaction ID monotonic increase
let owners = vec![
"chert_1alice...".to_string(),
"chert_1bob...".to_string(),
"chert_1charlie...".to_string(),
];
multisig.initialize(owners, 2); // Requires 2 of 3 signatures// Alice proposes sending 1000 CHERT to Dave
let tx_id = multisig.submit_transaction(
"chert_1dave...".to_string(),
1000, // 1000 CHERT
vec![], // No contract call data
"Payment to contractor Dave".to_string()
);
// Alice's signature is automatically counted// Bob approves the transaction
multisig.approve_transaction(tx_id);
// Now has 2/3 signatures, ready to execute// Anyone can execute once threshold is reached
multisig.execute_transaction(tx_id);
// Transaction executes, Dave receives 1000 CHERT// First, propose the owner addition as a transaction
let tx_id = multisig.submit_transaction(
multisig_address, // Call self
0, // No value transfer
encode_call("add_owner", "chert_1eve..."),
"Add Eve as fourth owner".to_string()
);
// Get 2 approvals (including proposer)
multisig.approve_transaction(tx_id);
// Execute
multisig.execute_transaction(tx_id);
// Now it's a 2-of-4 multisig// Increase to 3-of-4
let tx_id = multisig.submit_transaction(
multisig_address,
0,
encode_call("change_threshold", 3),
"Increase security to 3 signatures".to_string()
);
// Get current threshold (2) approvals
multisig.approve_transaction(tx_id);
multisig.execute_transaction(tx_id);
// Now requires 3 signatures// Deploy multisig for DAO treasury
let treasury = deploy_multisig(council_members, 5); // 5-of-9 council
// Propose spending from treasury
let tx_id = treasury.submit_transaction(
grant_recipient,
50000, // 50k CHERT grant
vec![],
"Q1 2026 Development Grant"
);
// Council members vote
for member in council_members[0..5] {
treasury.approve_transaction(tx_id);
}
treasury.execute_transaction(tx_id);// Propose large transfer
let tx_id = multisig.submit_transaction(
recipient,
1_000_000, // 1M CHERT
vec![],
"Large partnership payment"
);
// Set 7-day time lock for review period
multisig.set_time_lock(tx_id, 7 * 24 * 60 * 60);
// Get approvals
multisig.approve_transaction(tx_id);
multisig.approve_transaction(tx_id);
// Must wait 7 days before execution
// ... 7 days later ...
multisig.execute_transaction(tx_id);// Propose contract upgrade
let tx_id = multisig.submit_transaction(
protocol_contract,
0,
encode_call("upgrade", new_implementation),
"Upgrade to v2.0"
);
// Security council reviews and approves
// Requires unanimous approval (5-of-5)
for member in security_council {
multisig.approve_transaction(tx_id);
}
multisig.execute_transaction(tx_id);- ✅ Native Integration - Built on Chert's consensus layer
- ✅ Lower Gas Costs - Optimized storage and execution
- ✅ Post-Quantum Ready - Compatible with Dilithium signatures
- ✅ Built-in Time Locks - No separate module needed
- ✅ Simpler Architecture - Focused on core multisig functionality
- Initialize with various owner/threshold combinations
- Submit transactions (transfers and contract calls)
- Approve transactions by multiple owners
- Revoke approvals before execution
- Execute transactions at threshold
- Fail execution below threshold
- Add owners via multisig
- Remove owners via multisig
- Change threshold via multisig
- Cancel pending transactions
- Set and enforce time locks
- Handle execution failures gracefully
- Prevent duplicate approvals
- Prevent non-owner interactions
- Test reentrancy protection
- Test with maximum owners (50)
MIT License - See LICENSE file for details
🚧 In Development - Implementation in progress
Estimated Completion: Q1 2026