Decentralized Messenger Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Decentralized Messenger Development
Complex
from 2 weeks to 3 months
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Decentralized Messenger Development

Key question when designing a decentralized messenger—what exactly is decentralized? Message storage? Routing? Identity? Encryption? Most "decentralized" messengers are centralized in one or more of these layers, just masked with blockchain wrapper. Honest architecture requires explicit trade-off choices at each level.

Protocol Stack: Component Selection

Transport Layer

XMTP (Extensible Message Transport Protocol)—de facto standard for Web3 messengers in 2024-2025. Built on Waku (libp2p-based messaging network). Messages stored on XMTP nodes (federated network), identity—Ethereum address, encryption—Double Ratchet (like Signal).

import { Client } from '@xmtp/xmtp-js';
import { Wallet } from 'ethers';

// Create XMTP identity (wallet signature)
const xmtpClient = await Client.create(signer, { env: 'production' });

// Check: is address registered in XMTP?
const isOnNetwork = await Client.canMessage(recipientAddress);

// Create or open conversation
const conversation = await xmtpClient.conversations.newConversation(recipientAddress);

// Send message
await conversation.send('Hello from Web3');

// Get history
const messages = await conversation.messages({ limit: 50 });

// Stream new messages
for await (const message of await conversation.streamMessages()) {
  console.log(`${message.senderAddress}: ${message.content}`);
}

Advantage of XMTP: ready E2E encryption, cross-app (messages work between different dApps built on XMTP: Coinbase Wallet, Converse, Lens), no need to build p2p infrastructure.

Waku (standalone)—if you need fully custom implementation without XMTP dependency. Light node in browser via @waku/sdk:

import { createLightNode, waitForRemotePeer } from '@waku/sdk';

const node = await createLightNode({ defaultBootstrap: true });
await waitForRemotePeer(node);

const topic = '/my-messenger/1/chat/proto';
const encoder = createEncoder({ contentTopic: topic });
const decoder = createDecoder(topic);

// Subscribe
await node.filter.subscribe([decoder], (message) => {
  // Process incoming message
});

// Publish
await node.lightPush.send(encoder, { payload: encryptedBytes });

Waku without XMTP gives more control, but requires implementing encryption, identity, private message delivery yourself.

Identity and Key Management

XMTP binds identifier to Ethereum address automatically. With standalone approach, need key derivation scheme.

Deterministic key derivation: encryption master key derives from deterministic message signature. User signs once, keys reproducible on any device:

async function deriveMessagingKeys(signer: ethers.Signer): Promise<{
  identityKey: Uint8Array;
  preKey: Uint8Array;
}> {
  const message = 'MyMessenger Identity Key v1\n\nThis key is used for encrypted messaging.\nSign to generate your keys.';
  const signature = await signer.signMessage(message);
  
  // HKDF from signature
  const keyMaterial = await crypto.subtle.importKey('raw', hexToBytes(signature), 'HKDF', false, ['deriveKey', 'deriveBits']);
  
  const identityKeyBits = await crypto.subtle.deriveBits(
    { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(32), info: new TextEncoder().encode('identity-key') },
    keyMaterial, 256
  );
  
  const preKeyBits = await crypto.subtle.deriveBits(
    { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(32), info: new TextEncoder().encode('pre-key') },
    keyMaterial, 256
  );
  
  return { identityKey: new Uint8Array(identityKeyBits), preKey: new Uint8Array(preKeyBits) };
}

Important: if user changes wallet—they lose keys. Backup mechanism critical.

Message Encryption

For private chats—X25519 ECDH for key agreement + AES-GCM for content encryption:

async function encryptMessage(
  plaintext: string,
  senderPrivateKey: Uint8Array,
  recipientPublicKey: Uint8Array
): Promise<{ ciphertext: Uint8Array; nonce: Uint8Array }> {
  // Shared secret via ECDH
  const sharedSecret = await performECDH(senderPrivateKey, recipientPublicKey);
  
  // Derive encryption key from shared secret
  const encryptionKey = await crypto.subtle.importKey(
    'raw', sharedSecret, { name: 'AES-GCM' }, false, ['encrypt']
  );
  
  const nonce = crypto.getRandomValues(new Uint8Array(12));
  const ciphertext = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv: nonce },
    encryptionKey,
    new TextEncoder().encode(plaintext)
  );
  
  return { ciphertext: new Uint8Array(ciphertext), nonce };
}

For group chat—shared group key encrypted with each participant's public key (sealed sender model).

Forward Secrecy via Double Ratchet

Static ECDH key—weakness: key compromise reveals entire chat history. Double Ratchet (Signal Protocol algorithm) solves this: each message encrypted with new ephemeral key derived from ratchet state. From-scratch implementation is complex; use @signalapp/libsignal-client (official WASM port).

XMTP implements Double Ratchet internally—one reason to choose it over custom implementation.

Message Storage

Problem: blockchain is expensive for message storage. Even 100 bytes of text on Ethereum = several dollars. Options:

Storage Decentralization Cost Speed
XMTP nodes Federated Free ~200ms
IPFS + Filecoin High ~$0.01/GB/month 1-5 sec
Ceramic/ComposeDB High Free (light) ~500ms
Arweave Maximum ~$0.005/MB once 2-30 sec
Own server None Cheap <50ms

For real UX—hybrid scheme: messages in XMTP/Waku (fast, p2p), archive messages older than N days—to IPFS with Filecoin pinning.

Push Notifications

Waku and XMTP have no native push. For mobile notifications need PUSH service. XMTP supports Push via @xmtp/react-native-sdk + XMTP push service, can self-host.

For web: Service Worker + Web Push API. Pattern: SW subscribes to XMTP streaming, on new message—shows push via showNotification.

Group Chats

XMTP v3 (MLS — Messaging Layer Security) adds native groups with E2E encryption and forward secrecy for entire group. Significantly more complex than 1-on-1: membership management (add/remove requires group key update), state consensus between clients.

// XMTP v3 Group API
const group = await xmtpClient.conversations.newGroup([member1, member2, member3]);
await group.send('Hello group');

// Add member
await group.addMembers([newMemberAddress]);

// Group regenerates keys automatically on membership change

On-Chain Component: What's Worth Storing

Most messenger data should NOT go on-chain. Reasonable to store on-chain only:

  • Public keys (identity registration)—once on first use
  • Group registry—list of groups user is member of (if public groups)
  • Token-gated access—check NFT/token ownership for group chat entry

ENS integration: resolve name.eth → address → XMTP check via canMessage. Show ENS names instead of addresses in UI.

Frontend Structure

src/
  components/
    ConversationList/     # Chat list
    MessageThread/        # Message display
    MessageInput/         # Input with attachment support
    ContactSearch/        # Search by ENS/address
  hooks/
    useXmtpClient         # XMTP initialization
    useConversations      # Conversation list with streaming
    useMessages           # Specific chat messages
  stores/                 # Zustand / Recoil state

React Query + Zustand—for caching data from XMTP nodes. Messages cached locally (IndexedDB), streaming adds new without reload.

Development Timeline

XMTP-based messenger (1-on-1 chats, ENS resolving, basic UI) — 2-3 weeks. Group chats (MLS v3), push notifications, token-gated rooms — another 2-3 weeks. Full product with file sharing, read receipts, mobile adaptation — 2-3 months.