Project Templates
XSWD Minimal Core

XSWD Minimal Core

A lightweight XSWD implementation under 18KB for basic TELA applications that need simple DERO connectivity without advanced features.

When to Use Minimal vs Super Core

  • Use Minimal (this page): Simple apps, tight size constraints, basic connectivity
  • Use Super Core: Production apps, complex features, robust error handling

Features

Under 18KB - Fits TELA size constraints without compression
Multi-endpoint fallback - Tries 44326, 10103, 40403 ports
Basic reconnection - Simple retry logic
Essential API methods - Core DERO RPC calls
Development mode - Test without wallet
Proven patterns - Based on tela_tests code

xswd-minimal.js (15KB)

// XSWD Minimal Core - Lightweight DERO connectivity for TELA
// Based on proven patterns from tela_tests/app1
 
(function() {
  'use strict';
 
  const XSWDMinimal = {
    // Connection state
    socket: null,
    isConnected: false,
    connectionStatus: 'disconnected',
    currentEndpointIndex: 0,
    reconnectAttempts: 0,
    maxReconnectAttempts: 5,
    
    // Configuration
    endpoints: [
      'ws://127.0.0.1:44326/xswd',  // XSWD standard
      'ws://127.0.0.1:10103/xswd',  // DERO mainnet wallet
      'ws://127.0.0.1:40403/xswd'   // DERO testnet wallet
    ],
    
    // Application identity
    applicationData: {
      id: null,
      name: 'TELA Application',
      description: 'TELA application with DERO connectivity',
      url: typeof window !== 'undefined' ? window.location.origin : ''
    },
    
    // Request management
    pendingRequests: new Map(),
    requestId: 0,
    developmentMode: false,
    
    // Public API
    async initialize(options = {}) {
      try {
        // Generate unique application ID
        this.applicationData.id = this.generateAppId();
        
        // Update app info if provided
        if (options.name) this.applicationData.name = options.name;
        if (options.description) this.applicationData.description = options.description;
        
        // Try connecting to available endpoints
        const connected = await this.tryConnect();
        
        if (connected) {
          this.updateStatus('connected');
          return true;
        }
        
        // Enable development mode if allowed
        if (options.allowDevelopmentMode) {
          this.developmentMode = true;
          this.isConnected = true;
          this.updateStatus('dev-mode');
          console.log('⚠️ Running in development mode (no wallet required)');
          return true;
        }
        
        this.updateStatus('failed');
        return false;
        
      } catch (error) {
        console.error('XSWD initialization failed:', error);
        
        if (options.allowDevelopmentMode) {
          this.developmentMode = true;
          this.isConnected = true;
          this.updateStatus('dev-mode');
          return true;
        }
        
        this.updateStatus('failed');
        return false;
      }
    },
    
    async tryConnect() {
      for (let i = 0; i < this.endpoints.length; i++) {
        try {
          const endpoint = this.endpoints[i];
          console.log(`🔗 Trying to connect to ${endpoint}...`);
          
          const connected = await this.connectToEndpoint(endpoint);
          if (connected) {
            console.log(`✅ Connected to ${endpoint}`);
            this.currentEndpointIndex = i;
            return true;
          }
        } catch (error) {
          console.log(`❌ Failed to connect to ${this.endpoints[i]}: ${error.message}`);
        }
      }
      return false;
    },
    
    connectToEndpoint(endpoint) {
      return new Promise((resolve) => {
        try {
          this.updateStatus('connecting');
          const socket = new WebSocket(endpoint);
          
          const timeout = setTimeout(() => {
            socket.close();
            resolve(false);
          }, 5000);
          
          socket.onopen = () => {
            clearTimeout(timeout);
            // Send application data for approval (tela_tests pattern)
            socket.send(JSON.stringify(this.applicationData));
          };
          
          socket.onmessage = (event) => {
            try {
              const response = JSON.parse(event.data);
              
              // Handle handshake acceptance (tela_tests pattern)
              if (response.accepted) {
                clearTimeout(timeout);
                this.socket = socket;
                this.isConnected = true;
                this.setupSocketHandlers();
                resolve(true);
                return;
              }
              
              // Handle RPC responses
              this.handleResponse(response);
              
            } catch (error) {
              console.error('Message parsing error:', error);
            }
          };
          
          socket.onclose = () => {
            clearTimeout(timeout);
            this.handleDisconnection();
            resolve(false);
          };
          
          socket.onerror = () => {
            clearTimeout(timeout);
            resolve(false);
          };
          
        } catch (error) {
          resolve(false);
        }
      });
    },
    
    setupSocketHandlers() {
      if (!this.socket) return;
      
      this.socket.onmessage = (event) => {
        try {
          const response = JSON.parse(event.data);
          this.handleResponse(response);
        } catch (error) {
          console.error('Response parsing error:', error);
        }
      };
      
      this.socket.onclose = () => {
        this.handleDisconnection();
      };
      
      this.socket.onerror = (error) => {
        console.error('WebSocket error:', error);
        this.handleDisconnection();
      };
    },
    
    handleResponse(response) {
      if (response.id && this.pendingRequests.has(response.id)) {
        const { resolve, reject } = this.pendingRequests.get(response.id);
        this.pendingRequests.delete(response.id);
        
        if (response.error) {
          reject(new Error(response.error.message || 'RPC error'));
        } else {
          resolve(response.result);
        }
      }
    },
    
    handleDisconnection() {
      this.isConnected = false;
      this.socket = null;
      this.updateStatus('disconnected');
      
      // Auto-reconnect if we had a connection
      if (this.reconnectAttempts < this.maxReconnectAttempts) {
        this.reconnectAttempts++;
        console.log(`🔄 Reconnecting... (attempt ${this.reconnectAttempts})`);
        setTimeout(() => this.tryConnect(), 2000 * this.reconnectAttempts);
      }
    },
    
    async call(method, params = {}) {
      if (this.developmentMode) {
        return this.simulateCall(method, params);
      }
      
      if (!this.isConnected || !this.socket) {
        throw new Error('Not connected to DERO network');
      }
      
      return new Promise((resolve, reject) => {
        const id = (++this.requestId).toString();
        const request = {
          jsonrpc: '2.0',
          id: id,
          method: method
        };
        
        if (params && Object.keys(params).length > 0) {
          request.params = params;
        }
        
        this.pendingRequests.set(id, { resolve, reject });
        
        try {
          this.socket.send(JSON.stringify(request));
        } catch (error) {
          this.pendingRequests.delete(id);
          reject(new Error(`Send failed: ${error.message}`));
          return;
        }
        
        // Timeout handling
        const timeoutMs = method.includes('SC') ? 30000 : 10000;
        setTimeout(() => {
          if (this.pendingRequests.has(id)) {
            this.pendingRequests.delete(id);
            reject(new Error(`Request timeout: ${method}`));
          }
        }, timeoutMs);
      });
    },
    
    // High-level helper methods
    async getNetworkInfo() {
      try {
        const info = await this.call('DERO.GetInfo');
        return {
          height: info?.height || 0,
          difficulty: info?.difficulty || 0,
          peer_count: info?.peer_count || 0,
          tx_pool_size: info?.tx_pool_size || 0,
          version: info?.version || 'Unknown'
        };
      } catch (error) {
        console.error('GetNetworkInfo failed:', error);
        return null;
      }
    },
    
    async getBlock(height) {
      try {
        return await this.call('DERO.GetBlock', { height: parseInt(height) });
      } catch (error) {
        console.error(`GetBlock ${height} failed:`, error);
        return null;
      }
    },
    
    async getTransaction(txid) {
      try {
        const result = await this.call('DERO.GetTransaction', { txs_hashes: [txid] });
        return result?.txs?.[0] || null;
      } catch (error) {
        console.error(`GetTransaction ${txid} failed:`, error);
        return null;
      }
    },
    
    async getBalance() {
      try {
        return await this.call('GetBalance');
      } catch (error) {
        console.error('GetBalance failed:', error);
        return null;
      }
    },
    
    async getAddress() {
      try {
        return await this.call('GetAddress');
      } catch (error) {
        console.error('GetAddress failed:', error);
        return null;
      }
    },
    
    // Development mode simulation
    simulateCall(method, params) {
      console.log(`🧪 Dev mode: ${method}`, params);
      
      switch (method) {
        case 'DERO.GetInfo':
          return Promise.resolve({
            height: 1234567,
            difficulty: 15000000000,
            peer_count: 8,
            tx_pool_size: 12,
            version: 'DERO HE v4.2.0-dev'
          });
          
        case 'DERO.GetBlock':
          return Promise.resolve({
            height: params?.height || 1234567,
            hash: '0123456789abcdef' + String(params?.height || 0).padStart(48, '0'),
            timestamp: Math.floor(Date.now() / 1000),
            tx_hashes: ['tx1', 'tx2']
          });
          
        case 'GetAddress':
          return Promise.resolve({
            address: 'dero1qy0ehnqjpr0wxqnknyc66du2fsxyktppkr8m8e6jvplp954klfjz2qqdzcd8p'
          });
          
        case 'GetBalance':
          return Promise.resolve({
            unlocked_balance: 12345600000  // 123.456 DERO
          });
          
        default:
          return Promise.resolve(null);
      }
    },
    
    // Utility functions
    generateAppId() {
      return Array.from({ length: 64 }, () => 
        Math.floor(Math.random() * 16).toString(16)
      ).join('');
    },
    
    updateStatus(status) {
      this.connectionStatus = status;
      
      // Update UI if elements exist
      try {
        const dot = document.querySelector('#connection-status .status-dot');
        if (dot) {
          dot.className = 'status-dot';
          switch (status) {
            case 'connected':
              dot.classList.add('dot-green');
              break;
            case 'connecting':
              dot.classList.add('dot-yellow');
              break;
            case 'dev-mode':
              dot.classList.add('dot-blue');
              break;
            default:
              dot.classList.add('dot-red');
          }
        }
      } catch (_) {}
    },
    
    getConnectionStatus() {
      return {
        connected: this.isConnected,
        status: this.connectionStatus,
        developmentMode: this.developmentMode,
        endpoint: this.socket?.url || this.endpoints[this.currentEndpointIndex]
      };
    }
  };
  
  // Export to global scope
  if (typeof window !== 'undefined') {
    window.xswd = window.xswd || XSWDMinimal;
  }
})();

Usage

Basic Integration

<script src="xswd-minimal.js"></script>
<script>
// Initialize minimal XSWD
window.xswd.initialize({
  name: 'My TELA App',
  description: 'Simple TELA application',
  allowDevelopmentMode: true
}).then(connected => {
  if (connected) {
    loadAppData();
  }
});
 
async function loadAppData() {
  const networkInfo = await window.xswd.getNetworkInfo();
  const address = await window.xswd.getAddress();
  const balance = await window.xswd.getBalance();
  
  console.log('Network:', networkInfo);
  console.log('Address:', address);
  console.log('Balance:', balance);
}
</script>

File Size: 15KB ✅

This minimal version provides essential XSWD functionality while staying under the 18KB TELA limit.

Features Included:

  • ✅ Multi-endpoint connection
  • ✅ Basic reconnection logic
  • ✅ Essential RPC methods
  • ✅ Development mode
  • ✅ Proven connection patterns

Features Removed:

  • ❌ Advanced privacy monitoring
  • ❌ Request queuing system
  • ❌ Smart contract discovery
  • ❌ Complex error recovery
  • ❌ REST API compatibility

Recommendation

  • Use XSWD Minimal for simple apps with size constraints
  • Use XSWD Super Core (compressed) for production applications
  • Upgrade from Minimal to Super as your app grows in complexity

Perfect for: Basic TELA apps, learning projects, size-constrained deployments