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 contractSize Planning Matrix
| File Size | Action Required | Strategy |
|---|---|---|
| < 10KB | ✅ Deploy directly | Optimal size |
| 10-15KB | ⚠️ Consider compression | Use .gz compression |
| 15-18KB | ⚠️ Compression recommended | Required for safety margin |
| 18KB+ | ❌ Too large | Use DocShards system |
| INDEX > 9KB | ⚠️ May not be updatable | Reduce 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 THISReal 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 PLACEHOLDERSNo 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 moduleModule 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 HTMLCSS 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 THISAdvanced 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
- Advanced Features - DocShards and compression techniques
- TELA-CLI - Command-line development tools
- API Reference - Complete TELA Go package documentation
- XSWD Protocol - Wallet integration details