@hsuite/multisig
🚧 Work in Progress - This application is currently under active development. Features and APIs may change.
🔐 Multi-Signature Wallet Platform - Enterprise-grade security and transaction management for the Hedera ecosystem.
A comprehensive multi-signature wallet solution designed specifically for the Hedera network, providing secure transaction management, customizable approval workflows, and enterprise-grade controls with maximum security for digital assets. Built with NestJS, MongoDB, and seamless Hedera integration.
📑 Table of Contents
🔍 Overview
The @hsuite/multisig
application provides enterprise-grade multi-signature wallet functionality that supports:
Multi-Signature Wallets: Create wallets requiring multiple signatures for transactions
Flexible Threshold Management: Configure custom signature requirements (M-of-N)
Comprehensive Transaction Support: HBAR transfers, token operations, staking, and account updates
Queue-based Processing: Asynchronous transaction handling with Bull Queue
Real-time Updates: WebSocket integration for live transaction status updates
Cross-Chain Support: Extensible architecture for multiple blockchain networks
✨ Features
🏦 Multi-Signature Wallet Management
Create multi-signature wallets with custom threshold requirements
Add/remove wallet owners with proper validation and authorization
Update wallet configurations including keys and thresholds
Track wallet history with comprehensive audit trails
💰 Transaction Operations
HBAR Transfers: Send and receive HBAR with multi-signature approval
Token Operations:
Fungible token transfers (HTS tokens)
Non-fungible token (NFT) transfers
Token association/dissociation
Account Management:
Account updates (keys, auto-renew, etc.)
Staking operations to consensus nodes
Transaction Batching: Group multiple operations for efficiency
🔒 Security & Approval Workflows
Threshold Signatures: Configurable M-of-N signature requirements
Transaction Validation: Comprehensive checks before execution
Owner Management: Secure addition/removal of wallet owners
Audit Logging: Complete transaction and signature history
Queue Safety: Time-limited transaction windows for security
🔄 Real-time Features
WebSocket Gateway: Live updates for transaction status changes
Event-driven Architecture: Pub/sub system for real-time notifications
Background Processing: Asynchronous transaction handling
Status Monitoring: Real-time wallet and transaction status tracking
🏗️ Technical Architecture
Core Components
/**
* Main application components:
* - MultisigService: Core wallet and transaction management
* - MultisigConsumer: Background job processing with Bull Queue
* - MultisigGateway: WebSocket real-time communication
* - MultisigController: REST API endpoints
* - Model Services: Database operations and business logic
*/
Data Models
Multisig Wallet Entity
interface MultisigWallet {
wallet: string; // Hedera Account ID
smartApp: string; // Smart application identifier
name: string; // Wallet display name
description: string; // Wallet description
settings: {
owners: Array<{
wallet: string; // Owner's Hedera Account ID
name: string; // Owner display name
}>;
threshold: number; // Required signatures (M-of-N)
};
history: MultisigHistory[]; // Transaction history
}
Transaction History Entity
interface MultisigHistory {
type: 'create' | 'update' | 'withdraw' | 'associate' | 'dissociate';
payload: any; // Transaction-specific data
timestamp: string; // Transaction timestamp
threshold: number; // Required threshold at time of transaction
events: MultisigEvent[]; // Associated events and signatures
}
Transaction Event Entity
interface MultisigEvent {
status: 'pending' | 'success' | 'error';
action: 'signature' | 'execute' | 'request';
payload: any; // Event-specific data
timestamp: string; // Event timestamp
transaction?: Buffer; // Serialized transaction data
signature?: Buffer; // Signature data
}
Infrastructure Stack
Framework: NestJS with TypeScript
Database: MongoDB with Mongoose ODM
Queue System: Bull Queue with Redis
WebSockets: Socket.IO for real-time communication
Blockchain: Hedera Hashgraph SDK integration
Validation: class-validator and custom business rules
Background Jobs: Automatic retry and error handling
🔌 API Endpoints
Wallet Management
Get Wallet Information
GET /multisig/wallet/{walletId}
Authorization: Bearer {token}
Response:
{
"wallet": "0.0.123456",
"name": "Treasury Wallet",
"description": "Main treasury multi-sig wallet",
"settings": {
"owners": [
{ "wallet": "0.0.111111", "name": "Alice" },
{ "wallet": "0.0.222222", "name": "Bob" },
{ "wallet": "0.0.333333", "name": "Charlie" }
],
"threshold": 2
},
"assets": [...],
"chain": "HEDERA"
}
Wallet Creation
Create Wallet Request
POST /multisig/wallet/create/request
Content-Type: application/json
Authorization: Bearer {token}
{
"name": "Development Team Wallet",
"description": "Multi-sig wallet for development expenses",
"balance": 100.0,
"settings": {
"owners": [
{ "wallet": "0.0.111111", "name": "Lead Developer" },
{ "wallet": "0.0.222222", "name": "Project Manager" },
{ "wallet": "0.0.333333", "name": "Technical Director" }
],
"threshold": 2
},
"maxAutomaticTokenAssociations": 1000,
"isReceiverSignatureRequired": false
}
Execute Wallet Creation
POST /multisig/wallet/create/execute
Content-Type: application/json
Authorization: Bearer {token}
{
"bytes": "0x...", // Signed transaction bytes
"jobId": "12345"
}
Transaction Operations
Create Withdrawal Request
POST /multisig/wallet/withdraw/request
Content-Type: application/json
Authorization: Bearer {token}
{
"walletId": "0.0.123456",
"receiverId": "0.0.789012",
"tokenId": "HBAR", // or token ID like "0.0.445566"
"type": "FUNGIBLE_COMMON", // or "NON_FUNGIBLE_UNIQUE"
"amountOrSerial": 100.0 // or NFT serial number
}
Create Update Request
POST /multisig/wallet/update/request
Content-Type: application/json
Authorization: Bearer {token}
{
"walletId": "0.0.123456",
"action": "add", // or "remove"
"owner": {
"wallet": "0.0.444444",
"name": "New Team Member"
},
"threshold": 3
}
Create Staking Request
POST /multisig/wallet/stake/request
Content-Type: application/json
Authorization: Bearer {token}
{
"walletId": "0.0.123456",
"nodeId": 3 // Hedera consensus node ID, omit to unstake
}
Signature Management
Sign Transaction
POST /multisig/transaction/sign/{jobId}
Content-Type: application/json
Authorization: Bearer {token}
{
"walletId": "0.0.123456",
"senderId": "0.0.111111",
"historyId": "6507f1f77bcf86cd79943901",
"signature": "0x..." // Signature bytes
}
Get Transaction Status
GET /multisig/transaction/status/{jobId}
Authorization: Bearer {token}
Token Operations
Associate Token Request
POST /multisig/wallet/associate/request
Content-Type: application/json
Authorization: Bearer {token}
{
"walletId": "0.0.123456",
"tokenId": "0.0.445566"
}
Dissociate Token Request
POST /multisig/wallet/dissociate/request
Content-Type: application/json
Authorization: Bearer {token}
{
"walletId": "0.0.123456",
"tokenId": "0.0.445566"
}
🚀 Installation & Setup
Prerequisites
Node.js 18+ and npm/yarn
MongoDB 5.0+
Redis 6.0+
Hedera testnet/mainnet account
Environment Configuration
Create .multisig.env
file:
# Database Configuration
DATABASE_URL=mongodb://localhost:27017/hsuite_multisig
# Redis Configuration (for Bull Queue)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=optional_password
# Hedera Network Configuration
HEDERA_NETWORK=testnet
HEDERA_ACCOUNT_ID=0.0.YOUR_ACCOUNT
HEDERA_PRIVATE_KEY=your_private_key_hex
# Application Configuration
PORT=3003
APP_NAME=@hsuite/multisig
LOG_LEVEL=info
# WebSocket Configuration
WS_PORT=3004
WS_NAMESPACE=multisig_events
# Queue Configuration
QUEUE_REDIS_URL=redis://localhost:6379
QUEUE_ATTEMPTS=3
QUEUE_BACKOFF_DELAY=2000
# Security Configuration
JWT_SECRET=your_jwt_secret
TRANSACTION_TIMEOUT=120000 # 2 minutes in milliseconds
# Chain Configuration
SUPPORTED_CHAINS=HEDERA,RIPPLE
DEFAULT_CHAIN=HEDERA
Installation Steps
Install dependencies:
npm install
# or
yarn install
Start required services:
# Start MongoDB
mongod --dbpath /path/to/db
# Start Redis
redis-server
Initialize database (if required):
npm run migration:run multisig
Start the application:
# Development mode
npm run start:dev multisig
# Production mode
npm run start:prod multisig
# Watch mode for development
npm run start:debug multisig
Verify installation:
# Health check
curl http://localhost:3003/health
# WebSocket test
curl -i -N -H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
-H "Sec-WebSocket-Version: 13" \
http://localhost:3004/socket.io/
📚 Usage Examples
Complete Multi-Signature Workflow
import axios from 'axios';
import { PrivateKey, Client } from '@hashgraph/sdk';
const API_BASE = 'http://localhost:3003';
const client = Client.forTestnet();
// 1. Create a multi-signature wallet
const createMultisigWallet = async () => {
// Request wallet creation
const createResponse = await axios.post(`${API_BASE}/multisig/wallet/create/request`, {
name: 'Team Treasury',
description: 'Multi-signature wallet for team expenses',
balance: 1000.0,
settings: {
owners: [
{ wallet: '0.0.111111', name: 'Alice (CEO)' },
{ wallet: '0.0.222222', name: 'Bob (CTO)' },
{ wallet: '0.0.333333', name: 'Charlie (CFO)' }
],
threshold: 2
},
maxAutomaticTokenAssociations: 1000
}, {
headers: { Authorization: `Bearer ${authToken}` }
});
// Sign the transaction
const transactionBytes = Buffer.from(createResponse.data.bytes, 'base64');
const privateKey = PrivateKey.fromString(process.env.PRIVATE_KEY);
const signature = privateKey.sign(transactionBytes);
// Execute the creation
const executeResponse = await axios.post(`${API_BASE}/multisig/wallet/create/execute`, {
bytes: Array.from(signature),
jobId: createResponse.data.jobId
}, {
headers: { Authorization: `Bearer ${authToken}` }
});
return executeResponse.data;
};
// 2. Create a withdrawal request
const createWithdrawal = async (walletId: string) => {
const response = await axios.post(`${API_BASE}/multisig/wallet/withdraw/request`, {
walletId,
receiverId: '0.0.789012',
tokenId: 'HBAR',
type: 'FUNGIBLE_COMMON',
amountOrSerial: 100.0
}, {
headers: { Authorization: `Bearer ${authToken}` }
});
return response.data;
};
// 3. Sign a transaction (multiple owners need to do this)
const signTransaction = async (jobId: string, walletId: string, signerId: string) => {
// Get transaction details first
const statusResponse = await axios.get(`${API_BASE}/multisig/transaction/status/${jobId}`, {
headers: { Authorization: `Bearer ${authToken}` }
});
// Sign the transaction bytes
const transactionBytes = Buffer.from(statusResponse.data.bytes, 'base64');
const privateKey = PrivateKey.fromString(process.env.SIGNER_PRIVATE_KEY);
const signature = privateKey.sign(transactionBytes);
// Submit signature
const response = await axios.post(`${API_BASE}/multisig/transaction/sign/${jobId}`, {
walletId,
senderId: signerId,
historyId: statusResponse.data.historyId,
signature: Array.from(signature)
}, {
headers: { Authorization: `Bearer ${authToken}` }
});
return response.data;
};
// 4. Monitor wallet status
const getWalletInfo = async (walletId: string) => {
const response = await axios.get(`${API_BASE}/multisig/wallet/${walletId}`, {
headers: { Authorization: `Bearer ${authToken}` }
});
console.log('Wallet Info:', {
balance: response.data.assets,
pendingTransactions: response.data.history.filter(h => h.status === 'pending'),
owners: response.data.settings.owners,
threshold: response.data.settings.threshold
});
return response.data;
};
WebSocket Integration for Real-time Updates
import { io, Socket } from 'socket.io-client';
class MultisigWebSocketClient {
private socket: Socket;
constructor(authToken: string) {
this.socket = io('http://localhost:3004/multisig', {
auth: {
token: authToken
},
transports: ['websocket']
});
this.setupEventListeners();
}
private setupEventListeners() {
// Connection events
this.socket.on('connect', () => {
console.log('Connected to multisig WebSocket');
});
this.socket.on('disconnect', () => {
console.log('Disconnected from multisig WebSocket');
});
// Wallet events
this.socket.on('wallet_created', (data) => {
console.log('New wallet created:', data);
// Update UI to show new wallet
});
this.socket.on('wallet_updated', (data) => {
console.log('Wallet updated:', data);
// Refresh wallet information
});
// Transaction events
this.socket.on('transaction_pending', (data) => {
console.log('Transaction pending signature:', data);
// Show notification for pending transaction
});
this.socket.on('signature_added', (data) => {
console.log('New signature added:', data);
// Update signature count display
});
this.socket.on('transaction_executed', (data) => {
console.log('Transaction executed:', data);
// Update transaction history and balances
});
this.socket.on('transaction_failed', (data) => {
console.error('Transaction failed:', data);
// Show error notification
});
}
// Subscribe to specific wallet events
subscribeToWallet(walletId: string) {
this.socket.emit('subscribe_wallet', { walletId });
}
// Unsubscribe from wallet events
unsubscribeFromWallet(walletId: string) {
this.socket.emit('unsubscribe_wallet', { walletId });
}
disconnect() {
this.socket.disconnect();
}
}
// Usage
const wsClient = new MultisigWebSocketClient(authToken);
wsClient.subscribeToWallet('0.0.123456');
Token Operations Example
// Associate a token with the multisig wallet
const associateToken = async (walletId: string, tokenId: string) => {
// Step 1: Create association request
const requestResponse = await axios.post(`${API_BASE}/multisig/wallet/associate/request`, {
walletId,
tokenId
}, {
headers: { Authorization: `Bearer ${authToken}` }
});
// Step 2: Get required signatures (repeat for each required signer)
const signatures = [];
for (const signer of requiredSigners) {
const signature = await signTransaction(
requestResponse.data.jobId,
walletId,
signer.accountId
);
signatures.push(signature);
}
// Step 3: Execute when threshold is met
const executeResponse = await axios.post(`${API_BASE}/multisig/wallet/associate/execute`, {
bytes: requestResponse.data.bytes,
jobId: requestResponse.data.jobId
}, {
headers: { Authorization: `Bearer ${authToken}` }
});
return executeResponse.data;
};
// Transfer tokens from multisig wallet
const transferTokens = async (walletId: string, tokenId: string, amount: number, recipient: string) => {
const response = await axios.post(`${API_BASE}/multisig/wallet/withdraw/request`, {
walletId,
receiverId: recipient,
tokenId,
type: 'FUNGIBLE_COMMON',
amountOrSerial: amount
}, {
headers: { Authorization: `Bearer ${authToken}` }
});
return response.data;
};
🗃️ Database Schema
Collections Structure
multisig_wallets
{
_id: ObjectId,
wallet: String, // Hedera Account ID
smartApp: String, // Smart application identifier
name: String, // Wallet display name
description: String, // Wallet description
settings: {
owners: [{
wallet: String, // Owner Hedera Account ID
name: String // Owner display name
}],
threshold: Number // Required signature count
},
history: [ObjectId], // refs to multisig_histories
createdAt: Date,
updatedAt: Date
}
multisig_histories
{
_id: ObjectId,
type: String, // 'create'|'update'|'withdraw'|'associate'|'dissociate'
payload: Object, // Transaction-specific data
timestamp: String, // Transaction timestamp
threshold: Number, // Required threshold
events: [ObjectId], // refs to multisig_events
createdAt: Date,
updatedAt: Date
}
multisig_events
{
_id: ObjectId,
status: String, // 'pending'|'success'|'error'
action: String, // 'signature'|'execute'|'request'
payload: Object, // Event-specific data
timestamp: String, // Event timestamp
transaction: Buffer, // Serialized transaction (optional)
signature: Buffer, // Signature data (optional)
createdAt: Date,
updatedAt: Date
}
🔒 Security Features
Transaction Security
Time-limited requests: Transactions expire after 2 minutes to prevent replay attacks
Signature validation: All signatures verified against Hedera public keys
Threshold enforcement: Transactions only execute when signature threshold is met
Owner validation: Only wallet owners can sign transactions
Access Control
JWT Authentication: All API endpoints require valid authentication tokens
Wallet ownership: Users can only access wallets they own or are members of
Role-based permissions: Different access levels for wallet operations
Data Protection
Encrypted storage: Sensitive data encrypted at rest
Audit logging: Complete audit trail for all operations
Input validation: Comprehensive validation of all inputs
SQL injection prevention: MongoDB queries properly parameterized
⚙️ Queue System
The application uses Bull Queue for reliable background processing:
Queue Types
create.request: Wallet creation requests with 2-minute delay
create.execute: Wallet creation execution
withdraw.request: Withdrawal requests
withdraw.execute: Withdrawal execution
update.request: Wallet update requests
update.execute: Wallet update execution
stake.request: Staking requests
stake.execute: Staking execution
transaction.sign: Transaction signature processing
Queue Configuration
Retry Policy: 3 attempts with exponential backoff
Timeout: 2-minute timeout for transaction execution
Concurrency: Configurable concurrent job processing
Error Handling: Comprehensive error logging and recovery
Monitoring Queues
// Get queue statistics
const getQueueStats = async () => {
const stats = await multisigQueue.getJobCounts();
console.log('Queue Statistics:', {
active: stats.active,
waiting: stats.waiting,
completed: stats.completed,
failed: stats.failed,
delayed: stats.delayed
});
};
// Monitor failed jobs
const getFailedJobs = async () => {
const failedJobs = await multisigQueue.getFailed();
return failedJobs.map(job => ({
id: job.id,
name: job.name,
data: job.data,
error: job.failedReason,
timestamp: job.timestamp
}));
};
🔧 Development
Project Structure
apps/multisig/
├── src/
│ ├── main.ts # Application entry point
│ ├── multisig.module.ts # Root module configuration
│ ├── multisig.service.ts # Core multisig business logic
│ ├── multisig.controller.ts # REST API endpoints
│ ├── multisig.consumer.ts # Bull Queue job processing
│ ├── multisig.gateway.ts # WebSocket gateway
│ ├── multisig.events.ts # Event definitions
│ ├── entities/ # Database entity definitions
│ │ ├── wallets/ # Wallet entities
│ │ ├── histories/ # History entities
│ │ └── events/ # Event entities
│ ├── models/ # Database model services
│ │ ├── wallets/ # Wallet model operations
│ │ ├── histories/ # History model operations
│ │ └── events/ # Event model operations
│ └── interfaces/ # TypeScript interface definitions
│ └── namespaces/ # Organized interface namespaces
├── test/ # Test files
├── config/ # Configuration files
├── tsconfig.app.json # TypeScript configuration
└── README.md # This file
Running Tests
# Unit tests
npm run test multisig
# E2E tests
npm run test:e2e multisig
# Test coverage
npm run test:cov multisig
# Watch mode for TDD
npm run test:watch multisig
# Debug tests
npm run test:debug multisig
Development Commands
# Start in development mode with hot reload
npm run start:dev multisig
# Start in debug mode
npm run start:debug multisig
# Build for production
npm run build multisig
# Start production build
npm run start:prod multisig
# Lint code
npm run lint
# Format code
npm run format
Adding New Features
Define interfaces in
src/interfaces/
Create/update entities in
src/entities/
Implement business logic in
src/multisig.service.ts
Add API endpoints in
src/multisig.controller.ts
Add queue processing in
src/multisig.consumer.ts
Update WebSocket events in
src/multisig.gateway.ts
Write tests for all new functionality
🧪 Testing
Test Categories
Unit Tests
Service method testing
Controller endpoint testing
Model operation testing
Utility function testing
Integration Tests
Database integration
Queue system integration
WebSocket communication
Hedera SDK integration
E2E Tests
Complete wallet creation workflow
Multi-signature transaction flow
Error handling scenarios
Performance testing
Running Specific Tests
# Test specific file
npm test -- multisig.service.spec.ts
# Test specific pattern
npm test -- --testNamePattern="wallet creation"
# Test with coverage
npm run test:cov -- --testPathPattern=multisig
🤝 Contributing
We welcome contributions to improve the multisig platform! Please read our Contributing Guide for details on:
Code Style: Follow ESLint and Prettier configurations
Testing Requirements: Maintain >90% test coverage
Documentation: Update documentation for new features
PR Process: Use conventional commits and proper PR templates
Development Workflow
Fork the repository
Create a feature branch:
git checkout -b feature/amazing-feature
Make changes and add tests
Run the test suite:
npm run test multisig
Commit changes:
git commit -m 'feat: add amazing feature'
Push to branch:
git push origin feature/amazing-feature
Open a Pull Request
📄 License
This package is part of the HbarSuite ecosystem and is covered by its license terms.
🆘 Support & Documentation
GitHub Issues: Report bugs and request features
API Documentation: Swagger UI at
/api/docs
when runningCommunity: Join our Discord for real-time support
Enterprise Support: Contact us for enterprise deployment assistance
Built with ❤️ by the HbarSuite Team Securing digital assets on Hedera with enterprise-grade multi-signature solutions Copyright © 2024 HbarSuite. All rights reserved.
Last updated