@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

  1. Install dependencies:

npm install
# or
yarn install
  1. Start required services:

# Start MongoDB
mongod --dbpath /path/to/db

# Start Redis
redis-server
  1. Initialize database (if required):

npm run migration:run multisig
  1. 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
  1. 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

  1. Define interfaces in src/interfaces/

  2. Create/update entities in src/entities/

  3. Implement business logic in src/multisig.service.ts

  4. Add API endpoints in src/multisig.controller.ts

  5. Add queue processing in src/multisig.consumer.ts

  6. Update WebSocket events in src/multisig.gateway.ts

  7. 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

  1. Fork the repository

  2. Create a feature branch: git checkout -b feature/amazing-feature

  3. Make changes and add tests

  4. Run the test suite: npm run test multisig

  5. Commit changes: git commit -m 'feat: add amazing feature'

  6. Push to branch: git push origin feature/amazing-feature

  7. 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 running

  • Community: 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