Quick Start Guide

TELA Development Guide

Essential guide for building production-ready TELA applications with size constraints, performance optimization, and DERO blockchain integration.

⚠️

Critical Size Constraints TELA has strict size limitations that are absolute requirements. Never exceed these limits or deployment will fail.

TELA Size Limitations (CRITICAL)

File Size Constraints

// ABSOLUTE LIMITS - NEVER EXCEED
TELA-DOC-1 files:  Maximum 18KB code size, 19.2KB total including headers
TELA-INDEX-1 files: Recommended 9KB limit for updatable contracts
Shard size limit:   17,500 bytes for automatic file splitting
Total contract:     Maximum 20KB per TELA-DOC-1 contract

Size Planning Matrix

File SizeAction RequiredStrategy
< 10KB✅ Deploy directlyOptimal size
10-15KB⚠️ Consider compressionUse .gz compression
15-18KB⚠️ Compression recommendedRequired for safety margin
18KB+❌ Too largeUse DocShards system
INDEX > 9KB⚠️ May not be updatableReduce MODs or split

Code Style and Architecture

HTML-First Development Philosophy

TELA Principle: HTML-First, JavaScript-Minimal Write fast, zippy static HTML-based applications using proven patterns from working tela_tests code.

Proven JavaScript Patterns

Based on successful tela_tests/app1 application:

// ✅ PROVEN PATTERN - Global variables and traditional functions
let socket;
const applicationData = {
    "id": "your-app-" + Date.now(),
    "name": "Your TELA Application",
    "description": "App description",
    "url": "http://localhost:" + location.port
};
 
// ✅ PROVEN PATTERN - Traditional function declarations
function connectWebSocket() {
    socket = new WebSocket("ws://localhost:44326/xswd");
    
    socket.addEventListener("open", function(event) {
        sendData(applicationData);
    });
}
 
function sendData(data) {
    if (socket && socket.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify(data));
    }
}
<!-- ✅ GOOD: HTML-first approach -->
<div class="block-info" data-height="12345">
    <h3>Block #12345</h3>
    <p>Hash: <span class="hash">a1b2c3...</span></p>
    <p>Timestamp: <time datetime="2024-01-15">Jan 15, 2024</time></p>
</div>
 
<!-- ❌ AVOID: JavaScript-heavy approach -->
<div id="block-container"></div>
<script>
// Lots of DOM manipulation code...
</script>

Size Optimization Techniques

/* ✅ GOOD: Concise, minified-ready CSS */
.b{background:#f5f5f5;border:1px solid #ddd;padding:10px;margin:5px}
.h{color:#333;font:16px/1.4 Arial;margin:0 0 10px}
.t{font:12px monospace;color:#666;word-break:break-all}
 
/* ❌ AVOID: Verbose, space-wasting CSS */
.block-container {
    background-color: #f5f5f5;
    border: 1px solid #dddddd;
    padding: 10px;
    margin: 5px;
}
// ✅ PROVEN PATTERN: Modern JavaScript (from working tela_tests)
let socket;
const applicationData = {
    "id": "app-" + Date.now(),
    "name": "Your Application",
    "url": "http://localhost:" + location.port
};
 
// ✅ PROVEN PATTERN: Traditional and arrow functions both work
function connectWebSocket() {
    socket = new WebSocket("ws://localhost:44326/xswd");
}
 
// ✅ PROVEN PATTERN: Arrow functions in event handlers work fine
document.addEventListener("mousemove", (event) => {
    // This pattern is used successfully in tela_tests
});

DERO Blockchain Integration

XSWD Protocol Requirements

🚫

MANDATORY: XSWD Protocol Only ALL blockchain data must come from DERO daemon via XSWD WebSocket protocol. External APIs are forbidden.

// ✅ CORRECT: XSWD data fetching
async function getBlockData(height) {
    try {
        const blockData = await xswd.call('DERO.GetBlock', { height: height });
        return blockData;
    } catch (error) {
        console.error('DERO node call failed:', error);
        return null;
    }
}
 
// ❌ FORBIDDEN: External API calls
// fetch('https://api.example.com/block/' + height)  // NEVER DO THIS
// axios.get('https://external-service.com/data')    // NEVER DO THIS

Real Data Only Policy

// ✅ CORRECT: Live blockchain data
async function displayRealBalance() {
    try {
        const balance = await xswd.call('DERO.GetBalance');
        const deroAmount = balance.balance / 100000;
        document.getElementById('balance').textContent = `${deroAmount.toFixed(5)} DERO`;
    } catch (error) {
        document.getElementById('balance').textContent = 'Unable to load balance';
    }
}
 
// ❌ FORBIDDEN: Demo or placeholder data
// const demoBalance = 123.45678;  // NEVER USE FAKE DATA
// const placeholderData = {...};  // NEVER USE PLACEHOLDERS

No External Dependencies

Library Management Rules

⚠️

Zero External Dependencies NEVER use external CDN libraries. ALL code must be self-contained within TELA size limits.

<!-- ❌ FORBIDDEN: External CDN libraries -->
<!-- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> -->
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"> -->
 
<!-- ✅ CORRECT: Self-contained code -->
<style>
/* Custom minimal CSS within size limits */
.chart{width:100%;height:200px;border:1px solid #ddd}
</style>
<script>
// Custom minimal chart implementation
function drawChart(canvas, data) {
    const ctx = canvas.getContext('2d');
    // Minimal chart drawing code...
}
</script>

Custom Lightweight Implementations

// ✅ GOOD: Custom lightweight chart implementation
const MiniChart = {
    draw(canvas, data) {
        const ctx = canvas.getContext('2d');
        const w = canvas.width, h = canvas.height;
        const max = Math.max(...data);
        
        ctx.clearRect(0, 0, w, h);
        ctx.strokeStyle = '#4CAF50';
        ctx.beginPath();
        
        data.forEach((val, i) => {
            const x = (i / (data.length - 1)) * w;
            const y = h - (val / max) * h;
            i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
        });
        
        ctx.stroke();
    }
};
 
// ❌ AVOID: Heavy external libraries
// import Chart from 'chart.js';  // Too large for TELA

📁 TELA File Structure

Optimal Project Organization

tela-block-explorer/
├── index.html (< 18KB)      # Main application entry
├── styles.css (< 18KB)      # All styling (compressed)
├── main.js (< 18KB)         # Core functionality
├── blocks.js (< 18KB)       # Block explorer module
├── transactions.js (< 18KB) # Transaction module
└── network.js (< 18KB)      # Network health module

Module Pattern for Size Management

// ✅ GOOD: Compact, self-contained module under 18KB
const BlockExplorer = (function() {
    let cache = {}, ws;
    
    return {
        async init() {
            ws = new WebSocket('ws://127.0.0.1:44326/xswd');
            ws.onopen = () => ws.send(JSON.stringify({
                id: 'block-explorer',
                name: 'DERO Block Explorer',
                description: 'Blockchain explorer on TELA',
                url: location.origin
            }));
        },
        
        async getBlock(h) {
            if (cache[h]) return cache[h];
            const b = await this.call('DERO.GetBlock', {height: h});
            cache[h] = b;
            return b;
        },
        
        call(method, params) {
            return new Promise((resolve, reject) => {
                const id = Date.now();
                const req = {jsonrpc: '2.0', id, method, params};
                ws.send(JSON.stringify(req));
                
                const handler = (event) => {
                    const res = JSON.parse(event.data);
                    if (res.id === id) {
                        ws.removeEventListener('message', handler);
                        res.error ? reject(res.error) : resolve(res.result);
                    }
                };
                ws.addEventListener('message', handler);
            });
        },
        
        renderBlock(b) {
            return `<div class="b"><h3>Block ${b.height}</h3><p>Hash: ${b.hash}</p><p>TXs: ${b.tx_hashes?.length || 0}</p></div>`;
        }
    };
})();

Performance Optimization

Critical Performance Requirements

Performance Targets

  • Applications must load and render in under 2 seconds
  • Smooth 60fps animations and interactions
  • Minimal memory footprint for long-running apps
// ✅ GOOD: Efficient DOM updates
function updateBlockList(blocks) {
    const html = blocks.map(b => 
        `<div class="b" data-h="${b.height}">
            <span class="h">#${b.height}</span>
            <span class="t">${new Date(b.timestamp * 1000).toLocaleTimeString()}</span>
            <span class="s">${b.tx_hashes?.length || 0} TXs</span>
        </div>`
    ).join('');
    
    document.getElementById('blocks').innerHTML = html;
}
 
// ❌ AVOID: Individual DOM manipulations
function updateBlockListSlow(blocks) {
    const container = document.getElementById('blocks');
    container.innerHTML = ''; // Clears everything
    
    blocks.forEach(block => {
        const div = document.createElement('div');
        div.className = 'b';
        // Multiple DOM operations...
        container.appendChild(div);
    });
}

Memory Management

// ✅ GOOD: Proper cleanup and caching
const DataManager = {
    cache: new Map(),
    timers: new Set(),
    
    setTimer(callback, delay) {
        const timer = setTimeout(callback, delay);
        this.timers.add(timer);
        return timer;
    },
    
    clearTimer(timer) {
        clearTimeout(timer);
        this.timers.delete(timer);
    },
    
    cleanup() {
        // Clean up all timers
        this.timers.forEach(timer => clearTimeout(timer));
        this.timers.clear();
        
        // Limit cache size
        if (this.cache.size > 100) {
            const entries = Array.from(this.cache.entries());
            entries.slice(0, 50).forEach(([key]) => this.cache.delete(key));
        }
    }
};
 
// Call cleanup periodically
setInterval(() => DataManager.cleanup(), 60000); // Every minute

🔐 Security and Privacy

TELA Security Model

// ✅ GOOD: Secure, privacy-preserving patterns
const SecureTELAApp = {
    // No external tracking
    analytics: null,
    
    // Local storage only for preferences
    savePreference(key, value) {
        try {
            localStorage.setItem(`tela-app-${key}`, JSON.stringify(value));
        } catch (error) {
            console.warn('Could not save preference:', error);
        }
    },
    
    // No sensitive data storage
    handleWalletData(walletInfo) {
        // Process immediately, don't store
        this.updateUI(walletInfo);
        // walletInfo goes out of scope and gets garbage collected
    },
    
    // Content Security Policy compliance
    executeSecurely(userInput) {
        // Sanitize all user inputs
        const sanitized = userInput.replace(/[<>'"&]/g, '');
        return sanitized;
    }
};

What Actually Works: tela_tests Proven Facts

Based on analysis of working tela_tests/app1 code:

Modern JavaScript Features That Work:

// ✅ PROVEN: ES6+ features from working tela_tests
let socket;                              // let variables work fine
const applicationData = {...};           // const objects work fine
const typeSpeed = 50;                   // const primitives work fine
"http://localhost:" + location.port     // string concatenation works
socket.addEventListener("open", function(event) {...});  // traditional functions work
document.addEventListener("mousemove", (event) => {...}); // arrow functions work in HTML

CSS Animations That Work:

/* ✅ PROVEN: Complex animations from working tela_tests */
@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}
 
@keyframes pulseGreen {
    0% { transform: scale(0.95); box-shadow: 0 0 0 0 var(--green); }
    50% { transform: scale(1); box-shadow: 0 0 0 10px rgba(0, 128, 0, 0); }
    100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(0, 128, 0, 0); }
}
 
:root {
    --green: #46b868;
    --background: #0d1117;
    --text: white;
}

Advanced UI Features That Work:

// ✅ PROVEN: Sophisticated features from tela_tests
// Custom cursor effects
document.addEventListener("mousemove", (event) => {
    svgCursor.style.left = event.clientX - 12 + "px";
    svgCursor.style.top = event.clientY - 15 + "px";
});
 
// Typing animation effects
function typeWriter(text) {
    // Sophisticated typing animation that works
}
 
// Visual status indicators
function toggleIndicators(color) {
    // Professional connection status system
}

Real-World Example: Block Explorer Implementation

Based on your DERO Block Explorer and tela_tests patterns, here's an optimized implementation:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DERO Block Explorer</title>
    <style>
        /* Compressed CSS for block explorer */
        *{box-sizing:border-box;margin:0;padding:0}
        body{font:14px/1.4 Arial,sans-serif;background:#f5f5f5;color:#333}
        .container{max-width:1200px;margin:0 auto;padding:20px}
        .header{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:#fff;padding:20px;border-radius:8px;margin-bottom:20px}
        .stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:15px;margin:20px 0}
        .stat{background:#fff;padding:15px;border-radius:6px;box-shadow:0 2px 4px rgba(0,0,0,0.1)}
        .stat h3{color:#667eea;margin-bottom:5px}
        .blocks{background:#fff;border-radius:6px;overflow:hidden}
        .block{padding:15px;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center}
        .block:hover{background:#f9f9f9}
        .block-height{font-weight:bold;color:#667eea}
        .block-hash{font-family:monospace;font-size:12px;color:#666}
        .block-time{color:#999;font-size:12px}
        .loading{text-align:center;padding:20px;color:#666}
        .error{background:#f8d7da;color:#721c24;padding:15px;border-radius:6px;margin:10px 0}
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🔍 DERO Block Explorer</h1>
            <p>Real-time blockchain data via TELA</p>
        </div>
        
        <div class="stats" id="stats">
            <div class="stat">
                <h3>Current Height</h3>
                <div id="height">Loading...</div>
            </div>
            <div class="stat">
                <h3>Network Difficulty</h3>
                <div id="difficulty">Loading...</div>
            </div>
            <div class="stat">
                <h3>Hash Rate</h3>
                <div id="hashrate">Loading...</div>
            </div>
        </div>
        
        <div class="blocks">
            <div class="loading" id="loading">🔄 Loading recent blocks...</div>
            <div id="block-list"></div>
        </div>
    </div>
 
    <script>
        // Compact TELA block explorer implementation
        const Explorer = (function() {
            let ws, cache = new Map(), timers = new Set();
            
            return {
                async init() {
                    try {
                        ws = new WebSocket('ws://127.0.0.1:44326/xswd');
                        ws.onopen = () => {
                            ws.send(JSON.stringify({
                                id: 'dero-explorer',
                                name: 'DERO Block Explorer',
                                description: 'Real-time blockchain explorer on TELA',
                                url: location.origin
                            }));
                        };
                        
                        ws.onmessage = this.handleMessage.bind(this);
                        ws.onerror = () => this.showError('Connection failed');
                        
                        await this.loadInitialData();
                        this.startPolling();
                    } catch (error) {
                        this.showError('Failed to initialize: ' + error.message);
                    }
                },
                
                async call(method, params = {}) {
                    return new Promise((resolve, reject) => {
                        const id = Date.now() + Math.random();
                        const req = {jsonrpc: '2.0', id, method, params};
                        
                        const timer = setTimeout(() => {
                            reject(new Error('Request timeout'));
                        }, 10000);
                        timers.add(timer);
                        
                        const handler = (event) => {
                            const res = JSON.parse(event.data);
                            if (res.id === id) {
                                clearTimeout(timer);
                                timers.delete(timer);
                                ws.removeEventListener('message', handler);
                                res.error ? reject(res.error) : resolve(res.result);
                            }
                        };
                        
                        ws.addEventListener('message', handler);
                        ws.send(JSON.stringify(req));
                    });
                },
                
                async loadInitialData() {
                    try {
                        const info = await this.call('DERO.GetInfo');
                        this.updateStats(info);
                        
                        const blocks = await this.loadRecentBlocks(info.height);
                        this.renderBlocks(blocks);
                        
                        document.getElementById('loading').style.display = 'none';
                    } catch (error) {
                        this.showError('Failed to load data: ' + error.message);
                    }
                },
                
                async loadRecentBlocks(currentHeight) {
                    const blocks = [];
                    const startHeight = Math.max(0, currentHeight - 20);
                    
                    for (let h = currentHeight; h > startHeight; h--) {
                        try {
                            const block = await this.call('DERO.GetBlock', {height: h});
                            blocks.push(block);
                        } catch (error) {
                            console.warn(`Failed to load block ${h}:`, error);
                        }
                    }
                    
                    return blocks;
                },
                
                updateStats(info) {
                    document.getElementById('height').textContent = info.height.toLocaleString();
                    document.getElementById('difficulty').textContent = info.difficulty.toLocaleString();
                    
                    // Calculate approximate hash rate
                    const hashRate = Math.round(info.difficulty / 16); // 16s block time
                    document.getElementById('hashrate').textContent = `${(hashRate / 1000000).toFixed(2)} MH/s`;
                },
                
                renderBlocks(blocks) {
                    const html = blocks.map(b => `
                        <div class="block" onclick="Explorer.showBlockDetails('${b.hash}')">
                            <div>
                                <div class="block-height">#${b.height.toLocaleString()}</div>
                                <div class="block-hash">${b.hash.substring(0, 16)}...</div>
                            </div>
                            <div>
                                <div class="block-time">${new Date(b.timestamp * 1000).toLocaleTimeString()}</div>
                                <div>${b.tx_hashes?.length || 0} TXs</div>
                            </div>
                        </div>
                    `).join('');
                    
                    document.getElementById('block-list').innerHTML = html;
                },
                
                startPolling() {
                    const timer = setInterval(async () => {
                        try {
                            const info = await this.call('DERO.GetInfo');
                            this.updateStats(info);
                            
                            // Check for new blocks
                            const lastHeight = parseInt(document.querySelector('.block-height')?.textContent.replace(/[#,]/g, '') || 0);
                            if (info.height > lastHeight) {
                                const newBlocks = await this.loadRecentBlocks(info.height);
                                this.renderBlocks(newBlocks);
                            }
                        } catch (error) {
                            console.warn('Polling error:', error);
                        }
                    }, 15000); // Every 15 seconds
                    
                    timers.add(timer);
                },
                
                showError(message) {
                    const errorDiv = document.createElement('div');
                    errorDiv.className = 'error';
                    errorDiv.textContent = '❌ ' + message;
                    document.querySelector('.container').insertBefore(errorDiv, document.querySelector('.stats'));
                },
                
                showBlockDetails(hash) {
                    // Implement block detail view
                    console.log('Show details for block:', hash);
                }
            };
        })();
        
        // Initialize when page loads
        document.addEventListener('DOMContentLoaded', () => {
            Explorer.init().catch(error => {
                console.error('Explorer initialization failed:', error);
            });
        });
    </script>
</body>
</html>

Development Workflow Best Practices

File Size Monitoring During Development

# Constantly monitor sizes during development
watch -n 1 'ls -la *.html *.css *.js | awk "{print \$9, \$5}" | while read name size; do echo "$name: $(echo "scale=2; $size/1024" | bc)KB"; done'
 
# Quick size check function
check_tela_sizes() {
    for file in *.html *.css *.js; do
        if [ -f "$file" ]; then
            size=$(wc -c < "$file")
            kb=$(echo "scale=2; $size/1024" | bc)
            if (( $(echo "$kb > 18" | bc -l) )); then
                echo "❌ $file: ${kb}KB (EXCEEDS LIMIT)"
            elif (( $(echo "$kb > 15" | bc -l) )); then
                echo "⚠️  $file: ${kb}KB (consider compression)"
            else
                echo "✅ $file: ${kb}KB"
            fi
        fi
    done
}

Testing with Real DERO Data

// ✅ GOOD: Test with real blockchain data
async function testWithRealData() {
    console.log('🧪 Testing with live DERO data...');
    
    try {
        // Test network info
        const info = await xswd.call('DERO.GetInfo');
        console.log(`✅ Network height: ${info.height}`);
        
        // Test block data
        const block = await xswd.call('DERO.GetBlock', {height: info.height});
        console.log(`✅ Latest block: ${block.hash}`);
        
        // Test transaction data
        if (block.tx_hashes && block.tx_hashes.length > 0) {
            const tx = await xswd.call('DERO.GetTransaction', {
                tx_hashes: [block.tx_hashes[0]]
            });
            console.log(`✅ Transaction data: ${tx.txs[0].txid}`);
        }
        
        console.log('🎉 All tests passed with real data!');
    } catch (error) {
        console.error('❌ Test failed:', error);
    }
}
 
// ❌ AVOID: Testing with fake data
// const fakeData = { height: 12345, hash: 'fake...' };  // NEVER DO THIS

Advanced Optimization Techniques

Intelligent Code Splitting

// ✅ GOOD: Smart module loading under size constraints
const ModuleLoader = {
    modules: new Map(),
    
    async loadModule(name) {
        if (this.modules.has(name)) {
            return this.modules.get(name);
        }
        
        // Load module code (each under 18KB)
        const moduleCode = await fetch(`/${name}.js`).then(r => r.text());
        const module = new Function('return ' + moduleCode)();
        
        this.modules.set(name, module);
        return module;
    },
    
    async loadOnDemand(moduleName, container) {
        const module = await this.loadModule(moduleName);
        const content = await module.render();
        container.innerHTML = content;
    }
};
 
// Usage: Load heavy features only when needed
document.getElementById('show-charts').onclick = () => {
    ModuleLoader.loadOnDemand('charts', document.getElementById('chart-container'));
};

Data Compression Techniques

// ✅ GOOD: Client-side data compression for caching
const DataCompressor = {
    compress(data) {
        // Simple compression for repeated data
        const str = JSON.stringify(data);
        const compressed = str.replace(/(\w+):/g, (match, key) => {
            // Replace common keys with single letters
            const keyMap = {
                'height': 'h',
                'timestamp': 't',
                'hash': 'x',
                'difficulty': 'd',
                'tx_hashes': 'txs'
            };
            return (keyMap[key] || key) + ':';
        });
        return compressed;
    },
    
    decompress(compressed) {
        // Reverse the compression
        const keyMap = {
            'h:': 'height:',
            't:': 'timestamp:',
            'x:': 'hash:',
            'd:': 'difficulty:',
            'txs:': 'tx_hashes:'
        };
        
        let decompressed = compressed;
        Object.entries(keyMap).forEach(([short, long]) => {
            decompressed = decompressed.replace(new RegExp(short, 'g'), long);
        });
        
        return JSON.parse(decompressed);
    }
};

📋 Development Checklist

TELA Development Checklist Use this checklist for every TELA application to ensure compliance and optimization.

Pre-Development

  • Plan file structure to stay under size limits
  • Identify which features need separate modules
  • Design for XSWD-only data access
  • Plan caching strategy for blockchain data

During Development

  • Monitor file sizes continuously (check_tela_sizes())
  • Test with real DERO daemon connections
  • Validate XSWD protocol integration
  • Implement proper error handling
  • Optimize for performance (< 2s load time)

Pre-Deployment

  • Final size check - all files under 18KB
  • Test compression ratios
  • Validate with TELA-CLI: serve local ./project
  • Test on multiple networks (simulator, testnet)
  • Verify no external dependencies

Post-Deployment

  • Test deployed application functionality
  • Monitor performance and user feedback
  • Update documentation if needed
  • Consider optimizations for future versions

This development guide captures the essential constraints and best practices for building production-ready TELA applications, based on real-world experience building blockchain explorers and other complex TELA applications.

Related Documentation