Lyra Web3 Playground is built with a modern, scalable architecture designed for extensibility and maintainability. This document provides a comprehensive overview of the system architecture.
- React 18: UI framework with concurrent features
- TypeScript: Type safety and developer experience
- Vite: Fast build tool and dev server
- Tailwind CSS: Utility-first CSS framework
- React Router: Client-side routing
- ethers.js v6: Ethereum interaction library
- viem: Type-safe Ethereum library
- @solana/web3.js: Solana blockchain interaction
- MetaMask: Primary wallet provider
- Zustand: Lightweight state management
- React Context: For theme and auth
- Vitest: Unit testing framework
- ESLint: Code linting
- TypeScript: Static type checking
┌─────────────────────────────────────────────────────────────┐
│ Browser / Client │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ React App │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Pages (Routes) │ │ │
│ │ │ • Homepage • ExamplePage │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Components │ │ │
│ │ │ • NavBar • WalletConnect │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Examples │ │ │
│ │ │ • Web3 Examples • AI Examples │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ State Management (Zustand) │ │ │
│ │ │ • Theme Store • Wallet Store │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Browser APIs & Extensions │ │
│ │ • MetaMask • LocalStorage • IndexedDB │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ External Services │
├─────────────────────────────────────────────────────────────┤
│ • Ethereum Networks (Infura/Alchemy) │
│ • Polygon Networks │
│ • IPFS (for NFT storage) │
│ • AI APIs (OpenAI, Anthropic) │
│ • Supabase (Auth, Database, Storage) │
└─────────────────────────────────────────────────────────────┘
App
├── LiveAnnouncerProvider (accessibility)
├── VisualFeedbackProvider (accessibility)
├── Router
│ ├── SkipLink (accessibility)
│ ├── NavBar (persistent, hidden on /ide routes)
│ ├── MobileBottomNav (mobile only)
│ ├── Homepage
│ │ └── Example Cards
│ ├── SandboxPage (/ide)
│ │ ├── WebSandbox (Web development IDE)
│ │ └── SoliditySandbox (Smart contract IDE)
│ └── ExamplePage
│ ├── UnifiedSandbox (default view)
│ │ ├── Contract.sol editor
│ │ ├── Frontend.jsx (auto-generated)
│ │ ├── Live Preview
│ │ └── Deployed Contracts panel
│ └── ExampleWithPlayground (tutorial view)
│ ├── WalletConnectExample
│ ├── SmartContractExample
│ └── NFTMinterExample
The application includes a comprehensive, cutting-edge accessibility system. See ACCESSIBILITY.md for full documentation.
src/components/Accessibility/
├── index.ts # Exports
├── AccessibilityButton.tsx # Draggable floating button + quick menu
├── AccessibilityPanel.tsx # Full settings panel (6 tabs)
├── SkipLinks.tsx # Skip navigation links
├── Announcer.tsx # Screen reader announcements
├── DwellClick.tsx # Click-by-hovering for motor impairments
├── ReadingGuide.tsx # Line highlighter for reading
├── ColorBlindFilters.tsx # SVG filters for color blindness
└── (legacy files) # Original simple accessibility
src/stores/accessibilityStore.ts # 40+ settings with profiles
src/styles/accessibility.css # 580+ lines of a11y CSS
Key Features (Beyond WCAG 2.1 AAA):
- One-click profiles: Low Vision, Blind, Deaf, Motor, Cognitive
- Dwell Click: Click by hovering (no mouse button needed)
- Reading Guide: Line highlighter follows cursor
- Color Blind Filters: Protanopia, Deuteranopia, Tritanopia, etc.
- Text-to-Speech: Read content aloud with adjustable rate/pitch
- Code-to-Natural-Language: Translates code to plain English!
- OpenDyslexic Font: Dyslexia-friendly typography
- Large Click Targets: Up to 64px minimum touch areas
- Full keyboard navigation with
Alt+Aquick access - Settings export/import for device portability
// Container component (logic)
function WalletConnectExample() {
const [state, setState] = useState();
const handleConnect = async () => { /* logic */ };
return <WalletView state={state} onConnect={handleConnect} />;
}
// Presenter component (UI)
function WalletView({ state, onConnect }: Props) {
return <div>{/* Pure UI */}</div>;
}// Custom hook for wallet logic
function useWallet() {
const [address, setAddress] = useState<string | null>(null);
const connect = async () => { /* logic */ };
return { address, connect };
}
// Used in component
function Component() {
const { address, connect } = useWallet();
}- Manages light/dark mode
- Persists to localStorage
- Updates document class
interface ThemeState {
mode: 'light' | 'dark';
toggleTheme: () => void;
setTheme: (mode: 'light' | 'dark') => void;
}- Stores wallet connection state
- Manages address, balance, chainId
- Provides connect/disconnect actions
interface WalletState {
address: string | null;
chainId: number | null;
balance: string | null;
isConnected: boolean;
provider: any | null;
}- Manages internationalization (10 languages)
- Persists language preference
- Supports RTL (Arabic)
interface I18nState {
language: Language;
setLanguage: (lang: Language) => void;
t: (key: string) => string; // Translation function
}
// Supported: en, es, zh, fr, de, ja, ko, pt, ru, ar- 40+ accessibility settings
- One-click profiles
- Text-to-speech with code translation
- Persists all preferences
interface AccessibilitySettings {
// Vision
highContrast: boolean;
textSize: number; // 100-200%
colorBlindMode: 'none' | 'protanopia' | 'deuteranopia' | ...;
readingGuide: boolean;
dyslexicFont: boolean;
// Motor
dwellClick: boolean;
dwellTime: number; // ms
largeClickTargets: 'normal' | 'large' | 'extra-large';
// Cognitive
simplifiedUI: boolean;
focusMode: boolean;
reducedMotion: boolean;
// Audio
textToSpeech: boolean;
speechRate: number;
codeToNaturalLanguage: boolean;
}User Action
↓
Component Handler
↓
Store Action
↓
State Update
↓
Component Re-render
1. User clicks "Connect Wallet"
2. Component calls connectWallet()
3. Request sent to window.ethereum (MetaMask)
4. User approves in MetaMask
5. Receive accounts and network info
6. Update wallet store
7. Components re-render with wallet data
8. Fetch balance from provider
9. Update UI with balance
1. User selects different network
2. Call wallet_switchEthereumChain
3. If network not added, call wallet_addEthereumChain
4. MetaMask switches network
5. chainChanged event fired
6. Update store with new chainId
7. Re-fetch balance for new network
8. Update UI
-
Never expose private keys
- Use MetaMask for signing
- Never store private keys in code or localStorage
-
Validate all inputs
- Check addresses are valid
- Validate amounts and parameters
-
Handle errors gracefully
- Don't expose internal errors to users
- Log errors for debugging
-
RPC endpoint security
- Use environment variables for API keys
- Implement rate limiting
- Use fallback providers
-
Content Security Policy
- Restrict script sources
- Prevent XSS attacks
-
Input sanitization
- Sanitize user inputs
- Validate on both client and server
-
Dependency security
- Regular security audits
- Keep dependencies updated
// Route-based splitting
const HomePage = lazy(() => import('./pages/Homepage'));
const ExamplePage = lazy(() => import('./pages/ExamplePage'));
// Component-based splitting
const MonacoEditor = lazy(() => import('@monaco-editor/react'));Vite configuration splits bundles:
- React vendor bundle
- Web3 vendor bundle
- Editor bundle (Monaco)
// Lazy load heavy components
const Editor = lazy(() => import('./components/Editor'));
<Suspense fallback={<Loading />}>
<Editor />
</Suspense>- Create component in
src/examples/[category]/ - Add to example registry in Homepage
- Add route in ExamplePage
- Update documentation
- Add network config to
src/utils/networks.ts - Test wallet connection
- Test transactions
- Update UI to show new network
- Create AI service in
src/services/ai/ - Add API key to environment
- Create example component
- Add to example registry
- Test utility functions
- Test custom hooks
- Test state management
- Test rendering
- Test user interactions
- Test error states
- Test wallet connection flow
- Test network switching
- Test example interactions
- Test complete user journeys
- Test across browsers
- Test mobile responsiveness
npm run build
# Produces optimized production build in dist/-
Vercel (Recommended)
- Automatic deployments from Git
- Edge network CDN
- Free for hobby projects
-
Netlify
- Similar to Vercel
- Good CI/CD integration
-
Self-hosted
- Nginx/Apache
- Docker container
- Kubernetes cluster
Production requires:
VITE_INFURA_API_KEY (optional - falls back to public RPC)
VITE_SUPABASE_URL (for auth features)
VITE_SUPABASE_ANON_KEY (for auth features)
- Lighthouse CI
- Web Vitals tracking
- Bundle size monitoring
- Console error monitoring
- Failed transaction tracking
- API error logging
- Plausible Analytics (planned)
- No personal data collection
- GDPR compliant
The sandbox system consists of three main components:
A CodePen-like environment for web development.
Key Features:
- Multi-file project support with file tree
- Monaco Editor with syntax highlighting
- Live preview with iframe sandboxing
- Device presets (Desktop/Tablet/Mobile)
- Console capture via postMessage
- Settings panel with 10+ options
State Management:
interface SandboxFile {
id: string;
name: string;
content: string;
language: 'html' | 'css' | 'javascript' | 'typescript' | 'jsx' | 'vue' | 'python';
}
interface EditorSettings {
theme: 'vs-dark' | 'light';
fontSize: number;
tabSize: number;
wordWrap: boolean;
minimap: boolean;
lineNumbers: boolean;
formatOnSave: boolean;
vimMode: boolean;
autoSave: boolean;
ligatures: boolean;
}A Remix-quality smart contract IDE.
Key Features:
- Multiple Solidity versions (0.6.12 - 0.8.24)
- Real-time compilation with error display
- Contract deployment to multiple networks
- Interactive contract function panel
- Transaction history tracking
- ABI/Bytecode export
State Management:
interface CompiledContract {
name: string;
abi: any[];
bytecode: string;
gasEstimates: GasEstimate;
}
interface DeployedContract {
id: string;
name: string;
address: string;
abi: any[];
network: string;
}Generic sandbox that auto-generates from contract templates.
Key Features:
- Auto-generates React frontend from contract ABI
- Three-tab layout: Contract.sol / Frontend.jsx / Preview
- Deployed contracts interaction panel
- Works with any
templateIdfrom contractTemplates
User edits code
↓
Monaco Editor onChange
↓
Update file state
↓
Trigger runCode (debounced)
↓
Build HTML document
↓
Write to iframe srcDoc
↓
Capture console via postMessage
↓
Display in Console panel
-
Backend Services
- Serverless functions for AI proxy
- Database for user data
- Caching layer
-
Real-time Features
- WebSocket connections
- Live blockchain events
- Collaborative editing
-
Mobile Support
- Progressive Web App
- React Native app
- WalletConnect integration
-
Scalability
- Kubernetes deployment
- Load balancing
- CDN optimization
The community system allows users to share, discover, and collaborate on projects using wallet-based authentication.
User clicks "Share" or "Like"
↓
Check wallet connection
↓
If not connected → Show WalletConnect modal
↓
User connects MetaMask
↓
Wallet address used as user_id
↓
Proceed with action
Key Functions:
// Share a project publicly
shareProject({ title, description, files, category, tags, walletAddress })
→ Returns: { data, shareUrl, error }
// Get a shared project by token
getSharedProject(shareToken)
→ Returns: { data: SharedProject, error }
// Browse public projects
getPublicProjects({ category, sortBy, limit, offset })
→ Returns: { data: SharedProject[], total, error }
// Like/unlike a project
likeProject(projectId, walletAddress)
→ Returns: { liked: boolean, error }
// Add comment to project
addComment(projectId, content, walletAddress)
→ Returns: { data: ProjectComment, error }
// Fork a project
forkProject(shareToken, walletAddress)
→ Returns: { data: SharedProject, error }-- shared_projects table
id: uuid
user_id: text (wallet address)
title: text
description: text
files: jsonb[]
category: text
tags: text[]
is_public: boolean
share_token: text (unique)
likes_count: integer
views_count: integer
forks_count: integer
created_at: timestamp
updated_at: timestamp
-- project_likes table
id: uuid
project_id: uuid
user_id: text (wallet address)
created_at: timestamp
-- project_comments table
id: uuid
project_id: uuid
user_id: text (wallet address)
content: text
created_at: timestampReal-time cryptocurrency and DeFi market data from CoinGecko and DeFiLlama APIs.
class MarketDataService {
// CoinGecko API
getTopCoins(limit): Promise<TokenPrice[]>
getPrices(coinIds): Promise<PriceMap>
getTrending(): Promise<TrendingCoin[]>
getGlobalData(): Promise<GlobalMarketData>
getMarketChart(coinId, days): Promise<MarketChart>
// DeFiLlama API
getTopProtocols(limit): Promise<ProtocolTVL[]>
getTopChains(limit): Promise<ChainTVL[]>
getTopYields(limit): Promise<YieldPool[]>
getChainDeFiOverview(chain): Promise<DeFiOverview>
// Combined
getMarketOverview(): Promise<FullOverview>
}// Cache with TTL
private cache: Map<string, { data: any; timestamp: number }>
private cacheTTL = 60000; // 1 minute for prices
// 5 minutes for DeFi data
async fetchWithCache<T>(key, fetcher): Promise<T> {
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < cacheTTL) {
return cached.data;
}
const data = await fetcher();
cache.set(key, { data, timestamp: Date.now() });
return data;
}// Available hooks
useTopCoins(limit)
usePrices(coinIds)
useTrending()
useGlobalMarketData()
useMarketChart(coinId, days)
useTopProtocols(limit)
useTopChains(limit)
useTopYields(limit)
useChainDeFi(chain)
useMarketOverview()
useLivePrices(coinIds, refreshInterval) // Auto-refresh
// Return type
interface UseQueryResult<T> {
data: T | null;
loading: boolean;
error: Error | null;
refetch: () => Promise<void>;
}Component mounts
↓
Hook calls marketData service
↓
Check cache
↓
If stale → Fetch from API
↓
Update cache
↓
Return data to component
↓
(For live prices) Set interval for refresh
This architecture provides a solid foundation for Lyra Web3 Playground. The modular design allows for easy extension and maintenance while maintaining performance and security.
For questions or suggestions about the architecture, please open a discussion on GitHub.