Skip to main content

W3MSG Protocol Abstraction Layer - Troubleshooting Guide

This guide helps diagnose and resolve common issues when working with the W3MSG Protocol Abstraction Layer.

Table of Contents​


Connection Issues​

Wallet Connection Failures​

Symptom: SDK fails to initialize with wallet connection errors.

Common Causes & Solutions:

// Problem: Wallet not connected
if (!window.ethereum) {
console.error('No Web3 wallet detected');
// Solution: Guide user to install MetaMask or similar wallet
}

// Problem: Wrong network
const provider = new ethers.BrowserProvider(window.ethereum);
const network = await provider.getNetwork();
if (network.chainId !== expectedChainId) {
console.error(`Wrong network: ${network.chainId}, expected: ${expectedChainId}`);
// Solution: Request network switch
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${expectedChainId.toString(16)}` }]
});
}

// Problem: User rejected connection
try {
await window.ethereum.request({ method: 'eth_requestAccounts' });
} catch (error) {
if (error.code === 4001) {
console.error('User rejected the connection request');
// Solution: Show user-friendly message and retry option
}
}

Protocol Connection Timeouts​

Symptom: Protocol initialization hangs or times out.

Debugging Steps:

  1. Check network connectivity:
async function checkNetworkConnectivity() {
try {
const response = await fetch('https://api.github.com', {
method: 'HEAD',
mode: 'no-cors'
});
console.log('Network connectivity: OK');
} catch (error) {
console.error('Network connectivity: FAILED', error);
}
}
  1. Verify endpoint URLs:
const config = {
endpoints: {
'xmtp': 'wss://production.xmtp.network', // Correct
'push': 'https://backend.push.org' // Correct
}
};

// Test endpoint connectivity
async function testEndpoint(url: string) {
try {
const response = await fetch(url.replace('wss://', 'https://'));
console.log(`Endpoint ${url}: ${response.status}`);
} catch (error) {
console.error(`Endpoint ${url}: FAILED`, error);
}
}
  1. Check firewall/proxy settings:
// If behind corporate firewall, WebSocket connections might be blocked
const wsTest = new WebSocket('wss://production.xmtp.network');
wsTest.onopen = () => console.log('WebSocket: OK');
wsTest.onerror = (error) => console.error('WebSocket: BLOCKED', error);
wsTest.onclose = () => wsTest.close();

Message Sending Failures​

"Recipient Not Found" Errors​

Symptom: Messages fail with recipient-related errors.

Solutions:

// 1. Validate address format
import { ethers } from 'ethers';

function validateRecipient(address: string): boolean {
try {
return ethers.isAddress(address);
} catch {
return false;
}
}

// 2. Check if recipient can receive messages (XMTP)
async function canReceiveMessages(protocol: XMTPProtocol, address: string) {
try {
const canMessage = await protocol.canMessage(address);
if (!canMessage) {
console.warn(`Recipient ${address} cannot receive XMTP messages`);
return false;
}
return true;
} catch (error) {
console.error('Failed to check message capability:', error);
return false;
}
}

// 3. Use address normalization
function normalizeAddress(address: string): string {
return ethers.getAddress(address.toLowerCase());
}

Rate Limiting Issues​

Symptom: Messages fail with rate limit errors.

Solutions:

// 1. Implement exponential backoff
async function sendWithBackoff(w3msg: W3MSGSDK, params: SendMessageParams) {
const maxRetries = 5;
let delay = 1000; // Start with 1 second

for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await w3msg.send(params);
} catch (error) {
if (error instanceof ProtocolError && error.category === ErrorCategory.RATE_LIMIT) {
console.log(`Rate limited, waiting ${delay}ms before retry ${attempt + 1}`);
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // Exponential backoff
continue;
}
throw error;
}
}

throw new Error('Max retries exceeded due to rate limiting');
}

// 2. Implement message queuing
class MessageQueue {
private queue: SendMessageParams[] = [];
private processing = false;
private rateLimitDelay = 1000; // 1 message per second

async enqueue(params: SendMessageParams) {
this.queue.push(params);
if (!this.processing) {
await this.processQueue();
}
}

private async processQueue() {
this.processing = true;

while (this.queue.length > 0) {
const params = this.queue.shift()!;

try {
await w3msg.send(params);
console.log('Message sent successfully');
} catch (error) {
console.error('Failed to send message:', error);
// Optionally re-queue failed messages
}

// Rate limiting delay
await new Promise(resolve => setTimeout(resolve, this.rateLimitDelay));
}

this.processing = false;
}
}

Content Validation Errors​

Symptom: Messages fail due to invalid content format.

Solutions:

// Content validation utility
function validateMessageContent(content: any, type: MessageType): boolean {
if (type === MessageType.CHAT) {
// Chat messages can be string or rich content
if (typeof content === 'string') {
return content.length > 0 && content.length <= 1000; // Example limit
}
return false;
}

if (type === MessageType.NOTIFICATION) {
// Notifications require structured content
if (typeof content === 'object' && content !== null) {
return !!(content.title && content.body);
}
return false;
}

return false;
}

// Example usage
const messageParams = {
to: '0x742d35Cc6634C0532925a3b8D900b5dF5D7F3E8a',
content: {
title: 'Test Notification',
body: 'This is a test message'
},
type: MessageType.NOTIFICATION
};

if (!validateMessageContent(messageParams.content, messageParams.type)) {
throw new Error('Invalid message content for the specified type');
}

Protocol Switching Problems​

Protocol Initialization Failures​

Symptom: Protocol switching fails during initialization.

Debugging & Solutions:

// 1. Check protocol availability
async function checkProtocolAvailability(protocolName: string) {
const availableProtocols = w3msg.getAvailableProtocols();
if (!availableProtocols.includes(protocolName)) {
console.error(`Protocol ${protocolName} not available`);
return false;
}
return true;
}

// 2. Verify protocol health before switching
async function switchProtocolSafely(protocolName: string) {
const health = healthMonitor.getProtocolHealth(protocolName);

if (!health?.isHealthy) {
console.warn(`Protocol ${protocolName} is unhealthy, switching may fail`);

// Try to perform health check
await healthMonitor.checkProtocolHealth(protocolName);
const updatedHealth = healthMonitor.getProtocolHealth(protocolName);

if (!updatedHealth?.isHealthy) {
throw new Error(`Cannot switch to unhealthy protocol: ${protocolName}`);
}
}

await w3msg.switchProtocol(protocolName);
}

// 3. Implement fallback switching
async function switchWithFallback(preferredProtocol: string, fallbackProtocols: string[]) {
try {
await switchProtocolSafely(preferredProtocol);
console.log(`Successfully switched to ${preferredProtocol}`);
} catch (error) {
console.warn(`Failed to switch to ${preferredProtocol}, trying fallbacks`);

for (const fallback of fallbackProtocols) {
try {
await switchProtocolSafely(fallback);
console.log(`Successfully switched to fallback ${fallback}`);
return;
} catch (fallbackError) {
console.warn(`Fallback ${fallback} also failed:`, fallbackError);
}
}

throw new Error('All protocol switches failed');
}
}

Inconsistent Protocol States​

Symptom: Protocol switching appears successful but messages still fail.

Solutions:

// 1. Verify protocol state consistency
async function verifyProtocolState() {
const currentProtocol = w3msg.getCurrentProtocol();
const protocolInstance = protocolManager.getProtocol(currentProtocol);

if (!protocolInstance) {
console.error('Protocol instance not found');
return false;
}

const connectionStatus = protocolInstance.getConnectionStatus();
if (connectionStatus !== 'connected') {
console.warn(`Protocol ${currentProtocol} status: ${connectionStatus}`);

// Attempt reconnection
try {
await protocolInstance.connect();
console.log(`Reconnected to ${currentProtocol}`);
} catch (error) {
console.error(`Failed to reconnect to ${currentProtocol}:`, error);
return false;
}
}

return true;
}

// 2. Force protocol re-initialization
async function forceProtocolReinitialization(protocolName: string) {
console.log(`Force reinitializing ${protocolName}`);

// Disconnect current protocol
const currentProtocol = protocolManager.getProtocol(protocolName);
if (currentProtocol) {
await currentProtocol.disconnect();
}

// Wait a moment for cleanup
await new Promise(resolve => setTimeout(resolve, 1000));

// Reconnect
if (currentProtocol) {
await currentProtocol.connect();
}

console.log(`${protocolName} reinitialized successfully`);
}

Browser Compatibility Issues​

Service Worker Registration Failures​

Symptom: Push notifications don't work due to Service Worker issues.

Solutions:

// 1. Check Service Worker support
if (!('serviceWorker' in navigator)) {
console.error('Service Workers not supported in this browser');
// Fallback: Use polling for notifications
setupPollingNotifications();
} else {
// Register Service Worker with error handling
try {
const registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/'
});
console.log('Service Worker registered:', registration);
} catch (error) {
console.error('Service Worker registration failed:', error);
}
}

// 2. Handle HTTPS requirement
function checkSecureContext() {
if (!window.isSecureContext) {
console.warn('Application not running in secure context (HTTPS)');
console.warn('Push notifications and other features may not work');
return false;
}
return true;
}

// 3. Fallback for unsupported browsers
function setupPollingNotifications() {
console.log('Setting up polling-based notifications');

setInterval(async () => {
try {
// Poll for new messages
const messages = await fetchNewMessages();
messages.forEach(message => {
showBrowserNotification(message);
});
} catch (error) {
console.error('Polling failed:', error);
}
}, 30000); // Poll every 30 seconds
}

WebCrypto API Issues​

Symptom: Encryption/decryption operations fail.

Solutions:

// 1. Check WebCrypto support
function checkWebCryptoSupport(): boolean {
if (!window.crypto || !window.crypto.subtle) {
console.error('WebCrypto API not supported');
return false;
}

// Test basic functionality
try {
// This should not throw in supported browsers
window.crypto.getRandomValues(new Uint8Array(1));
return true;
} catch (error) {
console.error('WebCrypto API not functional:', error);
return false;
}
}

// 2. Implement fallback for older browsers
async function generateKeyWithFallback(): Promise<CryptoKey> {
if (checkWebCryptoSupport()) {
return await window.crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);
} else {
// Fallback: Load crypto polyfill
await import('crypto-js');
console.warn('Using crypto-js fallback for encryption');
// Implement fallback encryption logic
throw new Error('Fallback encryption not implemented');
}
}

Browser Feature Detection​

import { BrowserCompatibility } from '@w3msg/sdk';

async function setupBrowserCompatibility() {
const compatibility = BrowserCompatibility.getInstance();
const report = compatibility.getW3MSGCompatibilityReport();

console.log('Browser Compatibility Report:');
console.log('Overall Compatibility:', compatibility.isW3MSGCompatible());
console.log('Issues:', report.issues);
console.log('Recommendations:', report.recommendations);

// Handle specific compatibility issues
if (report.issues.length > 0) {
for (const issue of report.issues) {
switch (issue.category) {
case 'missing_web_api':
console.warn(`Missing Web API: ${issue.feature}`);
await loadPolyfillIfAvailable(issue.feature);
break;

case 'browser_version':
console.warn(`Browser version issue: ${issue.description}`);
showBrowserUpdateNotification();
break;

case 'security_context':
console.error(`Security context issue: ${issue.description}`);
showSecurityContextWarning();
break;
}
}
}
}

async function loadPolyfillIfAvailable(feature: string) {
const polyfills: Record<string, () => Promise<void>> = {
'fetch': () => import('whatwg-fetch'),
'AbortController': () => import('abortcontroller-polyfill/dist/abortcontroller-polyfill-only'),
'IntersectionObserver': () => import('intersection-observer')
};

if (polyfills[feature]) {
try {
await polyfills[feature]();
console.log(`Loaded polyfill for ${feature}`);
} catch (error) {
console.error(`Failed to load polyfill for ${feature}:`, error);
}
}
}

Performance Problems​

High Memory Usage​

Symptom: Application memory usage increases over time.

Solutions:

// 1. Monitor memory usage
function monitorMemoryUsage() {
if ('memory' in performance) {
const memory = (performance as any).memory;
console.log('Memory Usage:', {
used: `${Math.round(memory.usedJSHeapSize / 1024 / 1024)} MB`,
total: `${Math.round(memory.totalJSHeapSize / 1024 / 1024)} MB`,
limit: `${Math.round(memory.jsHeapSizeLimit / 1024 / 1024)} MB`
});
}
}

// 2. Implement proper cleanup
class W3MSGManager {
private w3msg: W3MSGSDK | null = null;
private eventListeners: Array<() => void> = [];
private intervals: Array<NodeJS.Timeout> = [];

async initialize() {
this.w3msg = new W3MSGSDK({ /* config */ });

// Track event listeners for cleanup
const healthListener = (event: any) => console.log('Health:', event);
healthMonitor.on('health:protocol_checked', healthListener);
this.eventListeners.push(() => healthMonitor.off('health:protocol_checked', healthListener));

// Track intervals for cleanup
const memoryInterval = setInterval(monitorMemoryUsage, 60000);
this.intervals.push(memoryInterval);

await this.w3msg.initialize();
}

async cleanup() {
// Clean up event listeners
this.eventListeners.forEach(cleanup => cleanup());
this.eventListeners = [];

// Clear intervals
this.intervals.forEach(interval => clearInterval(interval));
this.intervals = [];

// Disconnect W3MSG
if (this.w3msg) {
await this.w3msg.disconnect();
this.w3msg = null;
}

// Clear caches
healthMonitor.clearAllData();
retryManager.clearStatistics();

console.log('W3MSG cleanup completed');
}
}

// 3. Implement message history limits
class MessageCache {
private messages: Message[] = [];
private readonly maxMessages = 1000; // Limit cache size

addMessage(message: Message) {
this.messages.push(message);

// Remove old messages if cache is too large
if (this.messages.length > this.maxMessages) {
this.messages = this.messages.slice(-this.maxMessages);
}
}

getMessages(limit: number = 50): Message[] {
return this.messages.slice(-limit);
}

clearOldMessages(olderThanMs: number = 24 * 60 * 60 * 1000) { // 24 hours
const cutoff = Date.now() - olderThanMs;
this.messages = this.messages.filter(msg => msg.timestamp > cutoff);
}
}

Slow Protocol Switching​

Symptom: Protocol switching takes too long.

Solutions:

// 1. Pre-initialize protocols
class OptimizedProtocolManager {
private protocols: Map<string, MessageProtocol> = new Map();
private preInitialized = false;

async preInitializeProtocols(protocolNames: string[]) {
if (this.preInitialized) return;

const initPromises = protocolNames.map(async (name) => {
try {
const protocol = createProtocol(name); // Factory function
await protocol.initialize(config);
this.protocols.set(name, protocol);
console.log(`Pre-initialized ${name} protocol`);
} catch (error) {
console.warn(`Failed to pre-initialize ${name}:`, error);
}
});

await Promise.allSettled(initPromises);
this.preInitialized = true;
console.log('Protocol pre-initialization completed');
}

async switchProtocolFast(protocolName: string) {
const protocol = this.protocols.get(protocolName);
if (!protocol) {
throw new Error(`Protocol ${protocolName} not pre-initialized`);
}

// Protocol is already initialized, just activate it
this.activeProtocol = protocol;
console.log(`Fast switch to ${protocolName} completed`);
}
}

// 2. Implement connection pooling
class ConnectionPool {
private connections: Map<string, any> = new Map();

async getConnection(protocolName: string) {
let connection = this.connections.get(protocolName);

if (!connection || !this.isConnectionHealthy(connection)) {
console.log(`Creating new connection for ${protocolName}`);
connection = await this.createConnection(protocolName);
this.connections.set(protocolName, connection);
}

return connection;
}

private isConnectionHealthy(connection: any): boolean {
// Implement connection health check
return connection && connection.readyState === 1; // WebSocket OPEN state
}

private async createConnection(protocolName: string) {
// Implementation depends on protocol
switch (protocolName) {
case 'xmtp':
return new WebSocket('wss://production.xmtp.network');
case 'push':
return { /* HTTP client setup */ };
default:
throw new Error(`Unknown protocol: ${protocolName}`);
}
}
}

Configuration Issues​

Invalid Configuration Parameters​

Symptom: SDK initialization fails with configuration errors.

Solutions:

// 1. Configuration validation utility
interface ConfigValidationResult {
valid: boolean;
errors: string[];
warnings: string[];
}

function validateW3MSGConfig(config: W3MSGSDKConfig): ConfigValidationResult {
const result: ConfigValidationResult = {
valid: true,
errors: [],
warnings: []
};

// Validate signer
if (!config.signer) {
result.errors.push('Signer is required');
result.valid = false;
}

// Validate environment
const validEnvironments = ['development', 'staging', 'production'];
if (config.environment && !validEnvironments.includes(config.environment)) {
result.errors.push(`Invalid environment: ${config.environment}`);
result.valid = false;
}

// Validate protocols
if (config.protocols) {
const validProtocols = ['xmtp', 'push'];
const invalidProtocols = config.protocols.filter(p => !validProtocols.includes(p));
if (invalidProtocols.length > 0) {
result.errors.push(`Invalid protocols: ${invalidProtocols.join(', ')}`);
result.valid = false;
}
}

// Validate endpoints
if (config.endpoints) {
for (const [protocol, endpoint] of Object.entries(config.endpoints)) {
if (!isValidUrl(endpoint)) {
result.errors.push(`Invalid endpoint URL for ${protocol}: ${endpoint}`);
result.valid = false;
}
}
}

// Validate retry policy
if (config.retryPolicy && typeof config.retryPolicy === 'object') {
if (config.retryPolicy.maxRetries < 0) {
result.errors.push('maxRetries must be non-negative');
result.valid = false;
}
if (config.retryPolicy.baseDelay < 0) {
result.errors.push('baseDelay must be non-negative');
result.valid = false;
}
}

return result;
}

function isValidUrl(url: string): boolean {
try {
new URL(url);
return true;
} catch {
return false;
}
}

// 2. Safe configuration with defaults
function createSafeConfig(userConfig: Partial<W3MSGSDKConfig>): W3MSGSDKConfig {
const defaultConfig: W3MSGSDKConfig = {
signer: userConfig.signer!, // Required, will be validated
protocols: ['xmtp', 'push'],
environment: 'production',
retryPolicy: 'moderate',
healthMonitoring: true,
circuitBreaker: true,
migration: true,
browserCompatibility: true
};

const config = { ...defaultConfig, ...userConfig };

const validation = validateW3MSGConfig(config);
if (!validation.valid) {
throw new Error(`Configuration validation failed: ${validation.errors.join(', ')}`);
}

if (validation.warnings.length > 0) {
console.warn('Configuration warnings:', validation.warnings);
}

return config;
}

Environment Variable Issues​

Symptom: API keys or endpoints not being loaded correctly.

Solutions:

// 1. Environment validation utility
function validateEnvironmentVariables(): { missing: string[], invalid: string[] } {
const required = [
'PUSH_API_KEY',
'RPC_URL'
];

const optional = [
'XMTP_ENV',
'PUSH_ENV'
];

const missing = required.filter(key => !process.env[key]);
const invalid: string[] = [];

// Validate API key format
if (process.env.PUSH_API_KEY && !process.env.PUSH_API_KEY.startsWith('PK_')) {
invalid.push('PUSH_API_KEY must start with "PK_"');
}

// Validate URL format
if (process.env.RPC_URL && !isValidUrl(process.env.RPC_URL)) {
invalid.push('RPC_URL must be a valid URL');
}

return { missing, invalid };
}

// 2. Configuration loader with fallbacks
function loadEnvironmentConfig(): Partial<W3MSGSDKConfig> {
const { missing, invalid } = validateEnvironmentVariables();

if (missing.length > 0) {
console.error('Missing required environment variables:', missing);
}

if (invalid.length > 0) {
console.error('Invalid environment variables:', invalid);
}

return {
apiKeys: {
'push': process.env.PUSH_API_KEY || ''
},
endpoints: {
'xmtp': process.env.XMTP_ENDPOINT || 'wss://production.xmtp.network',
'push': process.env.PUSH_ENDPOINT || 'https://backend.push.org'
},
environment: (process.env.NODE_ENV as any) || 'production'
};
}

// 3. Runtime configuration validation
async function validateRuntimeConfig(config: W3MSGSDKConfig) {
console.log('Validating runtime configuration...');

// Test signer
try {
const address = await config.signer.getAddress();
console.log(`āœ… Signer address: ${address}`);
} catch (error) {
console.error('āŒ Signer validation failed:', error);
throw error;
}

// Test API keys
if (config.apiKeys?.push) {
try {
// Test Push Protocol API key
const response = await fetch('https://backend.push.org/apis/v1/channels', {
headers: {
'Authorization': `Bearer ${config.apiKeys.push}`
}
});

if (response.ok) {
console.log('āœ… Push Protocol API key valid');
} else {
console.warn(`āš ļø Push Protocol API key may be invalid: ${response.status}`);
}
} catch (error) {
console.warn('āš ļø Could not validate Push Protocol API key:', error);
}
}

// Test endpoints
if (config.endpoints) {
for (const [protocol, endpoint] of Object.entries(config.endpoints)) {
try {
const url = endpoint.replace('wss://', 'https://');
const response = await fetch(url, { method: 'HEAD', mode: 'no-cors' });
console.log(`āœ… Endpoint reachable: ${protocol} (${endpoint})`);
} catch (error) {
console.warn(`āš ļø Endpoint unreachable: ${protocol} (${endpoint})`);
}
}
}

console.log('Configuration validation completed');
}

Debugging Tools​

Enable Debug Logging​

// 1. Enable comprehensive logging
function enableDebugLogging() {
// SDK-level logging
const w3msg = new W3MSGSDK({
// ... config
debug: true // Enable debug mode
});

// Protocol-level logging
w3msg.on('debug', (event) => {
console.log(`[DEBUG ${event.timestamp}] ${event.component}: ${event.message}`, event.data);
});

// Health monitoring logging
healthMonitor.on('health:protocol_checked', (event) => {
console.log(`[HEALTH] ${event.protocolName}: ${event.isHealthy ? 'OK' : 'FAIL'} (${event.latency}ms)`);
});

// Retry logging
retryManager.on('retry:attempt', (event) => {
console.log(`[RETRY] Attempt ${event.attempt}/${event.maxRetries} for ${event.operationId}`);
});

// Error logging
w3msg.on('error', (error) => {
if (error instanceof ProtocolError) {
console.error(`[ERROR] ${error.protocol}:${error.method} - ${error.category}/${error.severity}:`, error.message);
}
});
}

// 2. Protocol state inspector
function inspectProtocolStates() {
const protocols = w3msg.getAvailableProtocols();

console.log('=== Protocol State Inspection ===');

protocols.forEach(protocolName => {
const protocol = protocolManager.getProtocol(protocolName);
if (protocol) {
console.log(`${protocolName.toUpperCase()}:`);
console.log(` Status: ${protocol.getConnectionStatus()}`);
console.log(` Config: ${JSON.stringify(protocol.getConfig(), null, 2)}`);

const health = healthMonitor.getProtocolHealth(protocolName);
if (health) {
console.log(` Health: ${health.isHealthy ? 'OK' : 'FAIL'} (${health.latency}ms)`);
console.log(` Errors: ${health.errorCount}, Uptime: ${health.uptime.toFixed(1)}%`);
}
}
});
}

Performance Profiling​

// 1. Message sending performance profiler
class MessagePerformanceProfiler {
private metrics: Array<{
timestamp: number;
protocol: string;
operation: string;
duration: number;
success: boolean;
}> = [];

async profileSendMessage(w3msg: W3MSGSDK, params: SendMessageParams) {
const startTime = performance.now();
const protocol = w3msg.getCurrentProtocol() || 'unknown';

try {
const result = await w3msg.send(params);
const duration = performance.now() - startTime;

this.metrics.push({
timestamp: Date.now(),
protocol,
operation: 'send_message',
duration,
success: true
});

console.log(`āœ… Message sent in ${duration.toFixed(2)}ms via ${protocol}`);
return result;
} catch (error) {
const duration = performance.now() - startTime;

this.metrics.push({
timestamp: Date.now(),
protocol,
operation: 'send_message',
duration,
success: false
});

console.log(`āŒ Message failed after ${duration.toFixed(2)}ms via ${protocol}`);
throw error;
}
}

getPerformanceReport() {
const totalMessages = this.metrics.length;
const successfulMessages = this.metrics.filter(m => m.success).length;
const averageDuration = this.metrics.reduce((sum, m) => sum + m.duration, 0) / totalMessages;

const protocolBreakdown = this.metrics.reduce((acc, metric) => {
if (!acc[metric.protocol]) {
acc[metric.protocol] = { count: 0, totalDuration: 0, successes: 0 };
}
acc[metric.protocol].count++;
acc[metric.protocol].totalDuration += metric.duration;
if (metric.success) acc[metric.protocol].successes++;
return acc;
}, {} as Record<string, any>);

return {
totalMessages,
successRate: (successfulMessages / totalMessages * 100).toFixed(1) + '%',
averageDuration: averageDuration.toFixed(2) + 'ms',
protocolBreakdown: Object.entries(protocolBreakdown).map(([protocol, stats]) => ({
protocol,
count: stats.count,
avgDuration: (stats.totalDuration / stats.count).toFixed(2) + 'ms',
successRate: (stats.successes / stats.count * 100).toFixed(1) + '%'
}))
};
}
}

Network Diagnostic Tool​

// Network connectivity and latency testing
class NetworkDiagnostics {
async runFullDiagnostics() {
console.log('šŸ” Running Network Diagnostics...');

const results = {
connectivity: await this.testConnectivity(),
latency: await this.testLatency(),
protocols: await this.testProtocolEndpoints(),
websockets: await this.testWebSocketConnections()
};

this.printDiagnosticReport(results);
return results;
}

private async testConnectivity() {
const testUrls = [
'https://www.google.com',
'https://api.github.com',
'https://httpbin.org/get'
];

const results = await Promise.allSettled(
testUrls.map(async (url) => {
const start = performance.now();
await fetch(url, { method: 'HEAD', mode: 'no-cors' });
return { url, latency: performance.now() - start };
})
);

return results.map((result, index) => ({
url: testUrls[index],
success: result.status === 'fulfilled',
latency: result.status === 'fulfilled' ? result.value.latency : null
}));
}

private async testLatency() {
const measurements = [];

for (let i = 0; i < 5; i++) {
const start = performance.now();
await fetch('https://httpbin.org/get', { method: 'HEAD', mode: 'no-cors' });
measurements.push(performance.now() - start);

await new Promise(resolve => setTimeout(resolve, 100));
}

return {
min: Math.min(...measurements),
max: Math.max(...measurements),
avg: measurements.reduce((sum, m) => sum + m, 0) / measurements.length,
measurements
};
}

private async testProtocolEndpoints() {
const endpoints = {
xmtp: 'https://production.xmtp.network',
push: 'https://backend.push.org'
};

const results = {};

for (const [protocol, endpoint] of Object.entries(endpoints)) {
try {
const start = performance.now();
const response = await fetch(endpoint, { method: 'HEAD' });
results[protocol] = {
success: response.ok,
status: response.status,
latency: performance.now() - start
};
} catch (error) {
results[protocol] = {
success: false,
error: error.message
};
}
}

return results;
}

private async testWebSocketConnections() {
const wsEndpoints = {
xmtp: 'wss://production.xmtp.network'
};

const results = {};

for (const [protocol, endpoint] of Object.entries(wsEndpoints)) {
try {
const ws = new WebSocket(endpoint);
const success = await new Promise<boolean>((resolve) => {
const timeout = setTimeout(() => resolve(false), 5000);

ws.onopen = () => {
clearTimeout(timeout);
ws.close();
resolve(true);
};

ws.onerror = () => {
clearTimeout(timeout);
resolve(false);
};
});

results[protocol] = { success };
} catch (error) {
results[protocol] = { success: false, error: error.message };
}
}

return results;
}

private printDiagnosticReport(results: any) {
console.log('\nšŸ“Š Network Diagnostic Report');
console.log('============================');

console.log('\n🌐 Connectivity:');
results.connectivity.forEach(test => {
console.log(` ${test.success ? 'āœ…' : 'āŒ'} ${test.url} ${test.latency ? `(${test.latency.toFixed(2)}ms)` : ''}`);
});

console.log('\n⚔ Latency:');
console.log(` Min: ${results.latency.min.toFixed(2)}ms`);
console.log(` Max: ${results.latency.max.toFixed(2)}ms`);
console.log(` Avg: ${results.latency.avg.toFixed(2)}ms`);

console.log('\nšŸ”Œ Protocol Endpoints:');
Object.entries(results.protocols).forEach(([protocol, result]: [string, any]) => {
console.log(` ${result.success ? 'āœ…' : 'āŒ'} ${protocol}: ${result.success ? `${result.status} (${result.latency?.toFixed(2)}ms)` : result.error}`);
});

console.log('\nšŸ”— WebSocket Connections:');
Object.entries(results.websockets).forEach(([protocol, result]: [string, any]) => {
console.log(` ${result.success ? 'āœ…' : 'āŒ'} ${protocol}: ${result.success ? 'Connected' : result.error || 'Failed'}`);
});
}
}

// Usage
const diagnostics = new NetworkDiagnostics();
await diagnostics.runFullDiagnostics();

FAQ​

Q: Why am I getting "Protocol not available" errors?​

A: This usually happens when:

  1. The protocol wasn't included in the SDK configuration
  2. The protocol failed to initialize properly
  3. Browser compatibility issues

Solution: Check your configuration and ensure all required protocols are included and properly configured.

Q: Messages are being sent but recipients aren't receiving them. Why?​

A: Common causes:

  1. Recipient doesn't have the protocol client (XMTP app, Push notifications enabled)
  2. Wrong recipient address format
  3. Network connectivity issues
  4. Protocol-specific limitations

Solution: Verify recipient capabilities and address format.

Q: Protocol switching is slow. How can I improve it?​

A: Use protocol pre-initialization and connection pooling (see Performance Problems section above).

Q: How do I handle rate limiting effectively?​

A: Implement exponential backoff and message queuing (see Message Sending Failures section above).

Q: Browser notifications aren't working. What should I check?​

A: Verify:

  1. HTTPS is enabled
  2. User granted notification permissions
  3. Service Worker is registered
  4. Browser supports Push API

Q: How can I debug protocol health issues?​

A: Use the health monitoring system and enable debug logging to track protocol status and performance metrics.


For more specific issues not covered here, please check the GitHub Issues or create a new issue with detailed information about your problem.