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