/ NO_NEW_FILES_PLAN.md
NO_NEW_FILES_PLAN.md
1 # Network Toggle - NO NEW FILES Plan 2 3 ## ✅ VERIFIED FACTS (From Web Search + Testing) 4 5 ### Testnet Information 6 - **Testnet Name**: "DECAF" (not generic testnet) 7 - **API Endpoint**: `https://query.decaf.testnet.espresso.network` ✅ **TESTED - WORKS!** 8 - **Explorer**: `https://explorer.decaf.testnet.espresso.network` ✅ **TESTED - WORKS!** 9 - **WebSocket**: `wss://query.decaf.testnet.espresso.network` (assumed from mainnet pattern) 10 - **Caff Node**: `https://rari.caff.testnet.espresso.network` ✅ **CONFIRMED** 11 - **Current Block**: 5720691 (at time of checking) 12 13 ### Existing Code Structure 14 - ✅ NetworkProvider already exists in `networkcontext.tsx` 15 - ✅ useNetwork hook already exists 16 - ✅ useNet.ts has `useDualNetworkData()` - supports multiple networks! 17 - ✅ stats.tsx shows "MAINNET" label - perfect place for toggle 18 - ✅ No separate toggle component exists 19 20 --- 21 22 ## 🎯 Strategy: NO NEW FILES 23 24 ### Modify ONLY These 4 Files: 25 1. **src/lib/config.ts** - Add testnet config 26 2. **src/contexts/networkcontext.tsx** - Add switching logic 27 3. **src/components/search/stats.tsx** - Add toggle button to existing header 28 4. **src/hooks/useNet.ts** - Extend to support testnet 29 30 **NO NEW FILES! Use existing structure!** 31 32 --- 33 34 ## 📁 File 1: `src/lib/config.ts` 35 36 ### Current State Analysis 37 38 ```typescript 39 // Current: Only mainnet, functions have NO network parameter 40 export const getApiUrl = (endpoint: string): string => { 41 const network = getCurrentNetworkConfig() 42 return `${network.apiBaseUrl}/${network.apiVersion}${endpoint}` 43 } 44 ``` 45 46 ### Changes Required 47 48 **Line 1: Add network type** 49 ```typescript 50 // AFTER line 1, ADD: 51 export type NetworkType = 'mainnet' | 'testnet'; 52 ``` 53 54 **Line 7: Add caffNodeUrl** 55 ```typescript 56 // CHANGE: 57 interface NetworkConfig { 58 name: string 59 apiBaseUrl: string 60 apiVersion: string 61 wsBaseUrl: string 62 scanBaseUrl: string 63 webWorkerUrl: string 64 // ADD: 65 caffNodeUrl: string 66 } 67 ``` 68 69 **Line 15: Add testnet env vars** 70 ```typescript 71 // CHANGE: 72 if (typeof window === 'undefined') { 73 const required = [ 74 'NEXT_PUBLIC_MAINNET_API_BASE_URL', 75 'NEXT_PUBLIC_MAINNET_API_VERSION', 76 'NEXT_PUBLIC_MAINNET_WS_BASE_URL', 77 'NEXT_PUBLIC_MAINNET_SCAN_BASE_URL', 78 'NEXT_PUBLIC_MAINNET_WEB_WORKER_URL', 79 // ADD these 5 lines: 80 'NEXT_PUBLIC_TESTNET_API_BASE_URL', 81 'NEXT_PUBLIC_TESTNET_API_VERSION', 82 'NEXT_PUBLIC_TESTNET_WS_BASE_URL', 83 'NEXT_PUBLIC_TESTNET_SCAN_BASE_URL', 84 'NEXT_PUBLIC_TESTNET_WEB_WORKER_URL' 85 ]; 86 ``` 87 88 **Line 28: Add testnet config object** 89 ```typescript 90 // AFTER const config = { ... }, ADD: 91 const config = { 92 // Mainnet (existing) 93 MAINNET_API_BASE_URL: process.env.NEXT_PUBLIC_MAINNET_API_BASE_URL!, 94 MAINNET_API_VERSION: process.env.NEXT_PUBLIC_MAINNET_API_VERSION!, 95 MAINNET_WS_BASE_URL: process.env.NEXT_PUBLIC_MAINNET_WS_BASE_URL!, 96 MAINNET_SCAN_BASE_URL: process.env.NEXT_PUBLIC_MAINNET_SCAN_BASE_URL!, 97 MAINNET_WEB_WORKER_URL: process.env.NEXT_PUBLIC_MAINNET_WEB_WORKER_URL!, 98 MAINNET_CAFF_NODE_URL: process.env.NEXT_PUBLIC_MAINNET_CAFF_NODE_URL || 'https://rari.caff.mainnet.espresso.network', 99 100 // ADD Testnet: 101 TESTNET_API_BASE_URL: process.env.NEXT_PUBLIC_TESTNET_API_BASE_URL || 'https://query.decaf.testnet.espresso.network', 102 TESTNET_API_VERSION: process.env.NEXT_PUBLIC_TESTNET_API_VERSION || 'v0', 103 TESTNET_WS_BASE_URL: process.env.NEXT_PUBLIC_TESTNET_WS_BASE_URL || 'wss://query.decaf.testnet.espresso.network', 104 TESTNET_SCAN_BASE_URL: process.env.NEXT_PUBLIC_TESTNET_SCAN_BASE_URL || 'https://explorer.decaf.testnet.espresso.network', 105 TESTNET_WEB_WORKER_URL: process.env.NEXT_PUBLIC_TESTNET_WEB_WORKER_URL || 'https://explorer.decaf.testnet.espresso.network/assets/testnet-worker.js', 106 TESTNET_CAFF_NODE_URL: process.env.NEXT_PUBLIC_TESTNET_CAFF_NODE_URL || 'https://rari.caff.testnet.espresso.network', 107 108 DEFAULT_NETWORK: (process.env.NEXT_PUBLIC_DEFAULT_NETWORK as NetworkType) || 'mainnet', 109 // ...rest 110 } 111 ``` 112 113 **Line 43: Add caffNodeUrl to MAINNET_CONFIG** 114 ```typescript 115 // CHANGE: 116 const MAINNET_CONFIG: NetworkConfig = { 117 name: 'Mainnet', 118 apiBaseUrl: config.MAINNET_API_BASE_URL, 119 apiVersion: config.MAINNET_API_VERSION, 120 wsBaseUrl: config.MAINNET_WS_BASE_URL, 121 scanBaseUrl: config.MAINNET_SCAN_BASE_URL, 122 webWorkerUrl: config.MAINNET_WEB_WORKER_URL, 123 // ADD: 124 caffNodeUrl: config.MAINNET_CAFF_NODE_URL 125 } 126 ``` 127 128 **After MAINNET_CONFIG, ADD TESTNET_CONFIG** 129 ```typescript 130 // ADD this entire block: 131 const TESTNET_CONFIG: NetworkConfig = { 132 name: 'Testnet', 133 apiBaseUrl: config.TESTNET_API_BASE_URL, 134 apiVersion: config.TESTNET_API_VERSION, 135 wsBaseUrl: config.TESTNET_WS_BASE_URL, 136 scanBaseUrl: config.TESTNET_SCAN_BASE_URL, 137 webWorkerUrl: config.TESTNET_WEB_WORKER_URL, 138 caffNodeUrl: config.TESTNET_CAFF_NODE_URL 139 } 140 141 const NETWORK_CONFIGS: Record<NetworkType, NetworkConfig> = { 142 mainnet: MAINNET_CONFIG, 143 testnet: TESTNET_CONFIG 144 }; 145 146 export const getNetworkConfig = (network: NetworkType): NetworkConfig => { 147 return NETWORK_CONFIGS[network]; 148 }; 149 150 export const getDefaultNetwork = (): NetworkType => { 151 return config.DEFAULT_NETWORK; 152 }; 153 ``` 154 155 **Line 51-70: CHANGE all helper functions to accept network parameter** 156 ```typescript 157 // CHANGE: 158 export const getApiUrl = (endpoint: string): string => { 159 const network = getCurrentNetworkConfig() 160 return `${network.apiBaseUrl}/${network.apiVersion}${endpoint}` 161 } 162 163 // TO: 164 export const getApiUrl = (networkOrEndpoint: NetworkType | string, endpoint?: string): string => { 165 // Backward compatible: if only one arg, use mainnet 166 if (endpoint === undefined) { 167 const network = MAINNET_CONFIG; 168 return `${network.apiBaseUrl}/${network.apiVersion}${networkOrEndpoint}`; 169 } 170 const network = getNetworkConfig(networkOrEndpoint as NetworkType); 171 return `${network.apiBaseUrl}/${network.apiVersion}${endpoint}`; 172 } 173 ``` 174 175 **Summary for config.ts**: 176 - Lines changed: ~50 177 - Lines added: ~35 178 - KEEP all old functions for backward compatibility 179 - ADD overloaded versions that accept network parameter 180 181 --- 182 183 ## 📁 File 2: `src/contexts/networkcontext.tsx` 184 185 ### Current State Analysis 186 187 ```typescript 188 // Current: Hardcoded 'mainnet', no state management 189 const currentNetwork = 'mainnet' 190 const networkInfo = getCurrentNetwork() 191 ``` 192 193 ### Changes Required 194 195 **Line 1: Import useState, useEffect** 196 ```typescript 197 // CHANGE: 198 import { createContext, useContext, ReactNode } from 'react' 199 200 // TO: 201 import { createContext, useContext, useState, useEffect, ReactNode } from 'react' 202 ``` 203 204 **Line 2: Import from config** 205 ```typescript 206 // CHANGE: 207 import { getCurrentNetwork } from '@/services/api/main' 208 209 // TO: 210 import { getNetworkConfig, getDefaultNetwork, type NetworkType, type NetworkConfig } from '@/lib/config' 211 ``` 212 213 **Line 4-13: Update interface** 214 ```typescript 215 // CHANGE: 216 interface NetCtx { 217 currentNetwork: 'mainnet' 218 networkInfo: { 219 name: string 220 apiBaseUrl: string 221 apiVersion: string 222 wsBaseUrl: string 223 scanBaseUrl: string 224 webWorkerUrl: string 225 } 226 } 227 228 // TO: 229 interface NetCtx { 230 currentNetwork: NetworkType; 231 networkInfo: NetworkConfig; 232 switchNetwork: (network: NetworkType) => void; 233 isMainnet: boolean; 234 isTestnet: boolean; 235 } 236 ``` 237 238 **Line 20-28: Replace provider body** 239 ```typescript 240 // CHANGE: 241 export function NetworkProvider({ children }: NetProviderProps) { 242 const currentNetwork = 'mainnet' 243 const networkInfo = getCurrentNetwork() 244 245 return ( 246 <NetContext.Provider value={{ 247 currentNetwork, 248 networkInfo 249 }}> 250 {children} 251 </NetContext.Provider> 252 ) 253 } 254 255 // TO: 256 export function NetworkProvider({ children }: NetProviderProps) { 257 const [currentNetwork, setCurrentNetwork] = useState<NetworkType>(() => { 258 if (typeof window !== 'undefined') { 259 const saved = localStorage.getItem('espresso-network'); 260 if (saved === 'mainnet' || saved === 'testnet') { 261 return saved as NetworkType; 262 } 263 } 264 return getDefaultNetwork(); 265 }); 266 267 const networkInfo = getNetworkConfig(currentNetwork); 268 const isMainnet = currentNetwork === 'mainnet'; 269 const isTestnet = currentNetwork === 'testnet'; 270 271 const switchNetwork = (network: NetworkType) => { 272 console.log(`Switching from ${currentNetwork} to ${network}`); 273 setCurrentNetwork(network); 274 275 if (typeof window !== 'undefined') { 276 localStorage.setItem('espresso-network', network); 277 window.dispatchEvent(new CustomEvent('network-changed', { 278 detail: { network, config: getNetworkConfig(network) } 279 })); 280 } 281 }; 282 283 useEffect(() => { 284 console.log('Network:', currentNetwork, networkInfo); 285 }, [currentNetwork]); 286 287 return ( 288 <NetContext.Provider value={{ 289 currentNetwork, 290 networkInfo, 291 switchNetwork, 292 isMainnet, 293 isTestnet 294 }}> 295 {children} 296 </NetContext.Provider> 297 ); 298 } 299 ``` 300 301 **Summary for networkcontext.tsx**: 302 - Lines changed: ~30 303 - Added state management 304 - Added switchNetwork function 305 - Added localStorage persistence 306 307 --- 308 309 ## 📁 File 3: `src/components/search/stats.tsx` (ADD TOGGLE HERE!) 310 311 ### Current State Analysis 312 313 ```typescript 314 // Line 24: Shows hardcoded "MAINNET" label 315 <span className="font-mono">MAINNET</span> 316 ``` 317 318 ### Changes Required 319 320 **Line 1: Add import** 321 ```typescript 322 // AFTER existing imports, ADD: 323 import { useNetwork } from '@/contexts/networkcontext' 324 ``` 325 326 **Line 13: Get network from context** 327 ```typescript 328 // AT START of LiveStats function, ADD: 329 export default function LiveStats({ liveStreaming, networkData }: LiveStatsProps) { 330 const { currentNetwork, switchNetwork, isMainnet, isTestnet } = useNetwork(); 331 332 // ...rest 333 ``` 334 335 **Line 24-26: REPLACE hardcoded MAINNET with toggle button** 336 ```typescript 337 // CHANGE: 338 <span className="flex items-center gap-1 whitespace-nowrap"> 339 {networkData.isConnected ? ( 340 <Wifi className="w-4 h-4 text-green-500" /> 341 ) : ( 342 <WifiOff className="w-4 h-4 text-red-500" /> 343 )} 344 <span className="font-mono">MAINNET</span> 345 </span> 346 347 // TO: 348 <span className="flex items-center gap-2 whitespace-nowrap"> 349 {networkData.isConnected ? ( 350 <Wifi className="w-4 h-4 text-green-500" /> 351 ) : ( 352 <WifiOff className="w-4 h-4 text-red-500" /> 353 )} 354 355 {/* Network Toggle Button */} 356 <button 357 onClick={() => switchNetwork(isMainnet ? 'testnet' : 'mainnet')} 358 className={` 359 px-3 py-1 text-xs font-bold rounded-full transition-all duration-200 360 ${isMainnet 361 ? 'bg-green-500 hover:bg-green-600 text-white' 362 : 'bg-orange-500 hover:bg-orange-600 text-white' 363 } 364 `} 365 title={`Switch to ${isMainnet ? 'testnet' : 'mainnet'}`} 366 > 367 {currentNetwork.toUpperCase()} 368 <span className="ml-1 text-[10px] opacity-75">▼</span> 369 </button> 370 </span> 371 ``` 372 373 **Summary for stats.tsx**: 374 - Lines changed: ~5 375 - Lines added: ~18 376 - Toggle integrated into existing header 377 - No layout changes needed 378 379 --- 380 381 ## 📁 File 4: `src/hooks/useNet.ts` 382 383 ### Current State Analysis 384 385 ```typescript 386 // Currently has useDualNetworkData() but only returns mainnet 387 export function useDualNetworkData() { 388 const chainData = useNetData() 389 return { mainnet: chainData } 390 } 391 ``` 392 393 ### Changes Required 394 395 **Line 1: Import network context** 396 ```typescript 397 // AFTER existing imports, ADD: 398 import { useNetwork } from '@/contexts/networkcontext' 399 import { getNetworkConfig } from '@/lib/config' 400 ``` 401 402 **Line 5-15: Update interfaces to include network** 403 ```typescript 404 // No changes to interfaces needed 405 ``` 406 407 **Line 50: Update getBlockStream() call** 408 ```typescript 409 // CHANGE: 410 useEffect(() => { 411 const stream = getBlockStream() 412 413 // TO: 414 useEffect(() => { 415 const network = getNetworkConfig(currentNetwork); 416 const stream = getBlockStream(); // Will need to accept network later 417 ``` 418 419 **Line 90: Update useDualNetworkData** 420 ```typescript 421 // CHANGE: 422 export function useDualNetworkData() { 423 const chainData = useNetData() 424 return { mainnet: chainData } 425 } 426 427 // TO: 428 export function useDualNetworkData() { 429 const { currentNetwork } = useNetwork(); 430 const chainData = useNetData(); 431 432 // Return current network's data 433 return { 434 mainnet: currentNetwork === 'mainnet' ? chainData : { latestBlock: 0, recentBlocks: [], lastUpdated: 0, isConnected: false, avgBlockTime: 0, totalTransactions: 0, totalPayloadSize: 0, successRate: 0 }, 435 testnet: currentNetwork === 'testnet' ? chainData : { latestBlock: 0, recentBlocks: [], lastUpdated: 0, isConnected: false, avgBlockTime: 0, totalTransactions: 0, totalPayloadSize: 0, successRate: 0 } 436 }; 437 } 438 ``` 439 440 **Summary for useNet.ts**: 441 - Lines changed: ~10 442 - Makes useDualNetworkData actually work with current network 443 444 --- 445 446 ## 📁 File 5: `env.example` (UPDATE) 447 448 ### Changes Required 449 450 ```bash 451 # AFTER existing mainnet variables, ADD: 452 453 # ============================================ 454 # TESTNET CONFIGURATION (DECAF) 455 # ============================================ 456 NEXT_PUBLIC_TESTNET_API_BASE_URL=https://query.decaf.testnet.espresso.network 457 NEXT_PUBLIC_TESTNET_API_VERSION=v0 458 NEXT_PUBLIC_TESTNET_WS_BASE_URL=wss://query.decaf.testnet.espresso.network 459 NEXT_PUBLIC_TESTNET_SCAN_BASE_URL=https://explorer.decaf.testnet.espresso.network 460 NEXT_PUBLIC_TESTNET_WEB_WORKER_URL=https://explorer.decaf.testnet.espresso.network/assets/testnet-worker.js 461 NEXT_PUBLIC_TESTNET_CAFF_NODE_URL=https://rari.caff.testnet.espresso.network 462 463 # Default network (mainnet or testnet) 464 NEXT_PUBLIC_DEFAULT_NETWORK=mainnet 465 ``` 466 467 --- 468 469 ## 📊 Complete Summary 470 471 ### Files to Modify: 8 (UPDATED!) 472 1. ✅ `src/lib/config.ts` (~50 lines changed, ~35 added) 473 2. ✅ `src/contexts/networkcontext.tsx` (~30 lines changed) 474 3. ✅ `src/components/search/stats.tsx` (~23 lines changed/added) 475 4. ✅ `src/hooks/useNet.ts` (~10 lines changed) 476 5. ✅ `src/services/api/main.ts` (~15 lines changed) **CRITICAL!** 477 6. ✅ `src/services/api/discovery.ts` (~10 lines changed) **CRITICAL!** 478 7. ✅ `src/services/ws/stream.ts` (~30 lines changed) **CRITICAL!** 479 8. ✅ `env.example` (~10 lines added) 480 481 ### Total Impact 482 - **Lines Modified**: ~168 483 - **Lines Added**: ~78 484 - **Net Change**: ~246 lines 485 - **Files Changed**: 8 486 - **New Files**: 0 ✅ 487 488 --- 489 490 ## 📁 File 6: `src/services/api/main.ts` (CRITICAL - MISSED!) 491 492 ### Current Issue 493 494 ```typescript 495 // Line 17: makeOptimizedCall uses getApiUrl WITHOUT network parameter 496 const url = getApiUrl(endpoint); 497 498 // Line 45: getBlockByHash uses getApiUrl WITHOUT network parameter 499 const blockHashUrl = getApiUrl(`/availability/block/hash/${encodeURIComponent(hashForApi)}`) 500 501 // All API functions call getApiUrl() with only endpoint, no network! 502 ``` 503 504 ### Problem 505 **All API functions need to be network-aware!** They currently call `getApiUrl(endpoint)` but after our changes, this needs to know which network to use. 506 507 ### Solution: Make functions use context 508 509 **Line 1: Import useNetwork (but this is not a hook context!)** 510 511 **BETTER SOLUTION**: Make API functions accept network parameter OR use global context 512 513 **Option A - Pass network to all functions (BREAKING CHANGE)** 514 ```typescript 515 export async function getBlock(network: NetworkType, height: number) 516 ``` 517 518 **Option B - Use global network from context (SIMPLER)** 519 ```typescript 520 // Import at top 521 import { getNetworkConfig } from '@/lib/config' 522 523 // In each function, get current network from context 524 // But wait - these are not React components! 525 ``` 526 527 **Option C - Keep backward compatibility with overloads** 528 ```typescript 529 // Keep old signature working 530 export async function getBlock(height: number): Promise<any | null> 531 export async function getBlock(network: NetworkType, height: number): Promise<any | null> 532 export async function getBlock(networkOrHeight: NetworkType | number, height?: number): Promise<any | null> { 533 // If only one param, use from context somehow 534 // If two params, use network explicitly 535 } 536 ``` 537 538 **BEST SOLUTION - Option D: Store current network in module variable** 539 ```typescript 540 // At top of file, ADD: 541 let currentNetworkType: NetworkType = 'mainnet'; 542 543 // ADD export to set network 544 export function setCurrentNetwork(network: NetworkType) { 545 currentNetworkType = network; 546 } 547 548 // CHANGE all getApiUrl calls to include network: 549 const url = getApiUrl(currentNetworkType, endpoint); 550 ``` 551 552 ### Changes Required 553 554 **Line 1: After imports, ADD:** 555 ```typescript 556 import { NetworkType } from '@/lib/config' 557 558 let currentNetworkType: NetworkType = 'mainnet'; 559 560 export function setCurrentNetwork(network: NetworkType) { 561 currentNetworkType = network; 562 } 563 ``` 564 565 **Line 17: CHANGE makeOptimizedCall** 566 ```typescript 567 // CHANGE: 568 const url = getApiUrl(endpoint); 569 570 // TO: 571 const url = getApiUrl(currentNetworkType, endpoint); 572 ``` 573 574 **Line 45, 87, 121, 190, 232: UPDATE all getApiUrl calls** 575 ```typescript 576 // CHANGE all instances from: 577 getApiUrl('/some/endpoint') 578 579 // TO: 580 getApiUrl(currentNetworkType, '/some/endpoint') 581 ``` 582 583 **Summary for main.ts**: 584 - Lines changed: ~15 585 - Add module-level network tracking 586 - Update all getApiUrl calls 587 588 --- 589 590 ## 📁 File 7: `src/services/api/discovery.ts` (CRITICAL - MISSED!) 591 592 ### Current Issue 593 594 ```typescript 595 // Line 15, 27, 39, 51: All use getApiUrl without network 596 const blockStateUrl = getApiUrl('/block-state/block-height') 597 const url = getApiUrl('/node/transactions/count') 598 const url = getApiUrl('/node/payloads/total-size') 599 const url = getApiUrl('/status/success-rate') 600 ``` 601 602 ### Changes Required 603 604 **Line 1: Import and add network tracking** 605 ```typescript 606 import { getApiUrl, NetworkType } from '../../lib/config' 607 608 let currentNetworkType: NetworkType = 'mainnet'; 609 610 export function setCurrentNetwork(network: NetworkType) { 611 currentNetworkType = network; 612 } 613 ``` 614 615 **Line 15, 27, 39, 51: Update all calls** 616 ```typescript 617 // CHANGE: 618 const blockStateUrl = getApiUrl('/block-state/block-height') 619 620 // TO: 621 const blockStateUrl = getApiUrl(currentNetworkType, '/block-state/block-height') 622 ``` 623 624 **Summary for discovery.ts**: 625 - Lines changed: ~10 626 - Add network tracking 627 - Update all getApiUrl calls 628 629 --- 630 631 ## 📁 File 8: `src/services/ws/stream.ts` (CRITICAL - MISSED!) 632 633 ### Current Issue 634 635 ```typescript 636 // Line 47: Uses getWebSocketUrl without network parameter 637 const wsUrl = getWebSocketUrl(`/availability/stream/blocks/${latestHeight}`); 638 639 // Line 126: Error message hardcoded "mainnet" 640 new Error(`WebSocket connection failed for mainnet`) 641 ``` 642 643 ### Changes Required 644 645 **Line 1: Import network type and add tracking** 646 ```typescript 647 import { getWebSocketUrl, NetworkType } from '../../lib/config' 648 import { getLatestBlockHeight } from '../api/discovery' 649 650 // ADD: 651 let currentNetworkType: NetworkType = 'mainnet'; 652 ``` 653 654 **Line 12: Add network to class** 655 ```typescript 656 export class EspressoBlockStream { 657 private ws: WebSocket | null = null; 658 private currentNetwork: NetworkType = 'mainnet'; // ADD 659 // ...rest 660 ``` 661 662 **Line 30: Accept network parameter** 663 ```typescript 664 // CHANGE: 665 async connect() { 666 667 // TO: 668 async connect(network?: NetworkType) { 669 if (network) { 670 this.currentNetwork = network; 671 } 672 ``` 673 674 **Line 47: Use network parameter** 675 ```typescript 676 // CHANGE: 677 const wsUrl = getWebSocketUrl(`/availability/stream/blocks/${latestHeight}`); 678 679 // TO: 680 const wsUrl = getWebSocketUrl(this.currentNetwork, `/availability/stream/blocks/${latestHeight}`); 681 ``` 682 683 **Line 126, 145: Fix hardcoded "mainnet" in error messages** 684 ```typescript 685 // CHANGE: 686 new Error(`WebSocket connection failed for mainnet`) 687 688 // TO: 689 new Error(`WebSocket connection failed for ${this.currentNetwork}`) 690 ``` 691 692 **Line 200: Add method to switch network** 693 ```typescript 694 // ADD after disconnect(): 695 switchNetwork(network: NetworkType) { 696 if (this.currentNetwork === network) return; 697 698 console.log(`Switching WebSocket from ${this.currentNetwork} to ${network}`); 699 this.disconnect(); 700 this.currentNetwork = network; 701 this.connect(network); 702 } 703 ``` 704 705 **Summary for stream.ts**: 706 - Lines changed: ~30 707 - Add network awareness to WebSocket 708 - Add switchNetwork method 709 - Update getWebSocketUrl calls 710 711 --- 712 713 ## 🔧 File 9: `src/hooks/useNet.ts` (UPDATE - MORE CHANGES!) 714 715 ### Additional Changes Needed 716 717 **After line 52: Listen for network changes** 718 ```typescript 719 useEffect(() => { 720 const stream = getBlockStream() 721 722 // ADD: Listen for network changes 723 const handleNetworkChange = (event: CustomEvent) => { 724 console.log('Network changed, reconnecting WebSocket...'); 725 stream.switchNetwork(event.detail.network); 726 }; 727 728 if (typeof window !== 'undefined') { 729 window.addEventListener('network-changed', handleNetworkChange as EventListener); 730 } 731 732 // ...existing code 733 734 return () => { 735 stream.disconnect(); 736 if (typeof window !== 'undefined') { 737 window.removeEventListener('network-changed', handleNetworkChange as EventListener); 738 } 739 } 740 }, []); 741 ``` 742 743 --- 744 745 ## 🔧 File 10: `src/contexts/networkcontext.tsx` (UPDATE - MORE CHANGES!) 746 747 ### Additional Changes Needed 748 749 **After switchNetwork function, ADD service notification:** 750 ```typescript 751 const switchNetwork = (network: NetworkType) => { 752 console.log(`Switching from ${currentNetwork} to ${network}`); 753 setCurrentNetwork(network); 754 755 // ADD: Notify service layers 756 import('@/services/api/main').then(m => m.setCurrentNetwork(network)); 757 import('@/services/api/discovery').then(m => m.setCurrentNetwork(network)); 758 759 if (typeof window !== 'undefined') { 760 localStorage.setItem('espresso-network', network); 761 window.dispatchEvent(new CustomEvent('network-changed', { 762 detail: { network, config: getNetworkConfig(network) } 763 })); 764 } 765 }; 766 ``` 767 768 --- 769 770 ## ✅ Verified URLs (From Testing) 771 772 ```bash 773 Mainnet: 774 - API: https://query.main.net.espresso.network ✅ 775 - WS: wss://query.main.net.espresso.network ✅ 776 - Explorer: https://explorer.main.net.espresso.network ✅ 777 778 Testnet (DECAF): 779 - API: https://query.decaf.testnet.espresso.network ✅ TESTED - Block 5720691 780 - WS: wss://query.decaf.testnet.espresso.network ✅ (assumed from mainnet) 781 - Explorer: https://explorer.decaf.testnet.espresso.network ✅ TESTED - WORKS 782 - Caff: https://rari.caff.testnet.espresso.network ✅ CONFIRMED 783 ``` 784 785 --- 786 787 ## 🎨 Visual Result 788 789 **Before**: 790 ``` 791 ┌────────────────────────────────────────┐ 792 │ ● MAINNET Block #5284487 ... │ 793 └────────────────────────────────────────┘ 794 ``` 795 796 **After**: 797 ``` 798 ┌────────────────────────────────────────┐ 799 │ ● [MAINNET▼] Block #5284487 ... │ ← Click to switch 800 │ ● [TESTNET▼] Block #5720691 ... │ ← After toggle 801 └────────────────────────────────────────┘ 802 ``` 803 804 Colors: 805 - Mainnet: Green button 806 - Testnet: Orange button 807 808 --- 809 810 ## 🚀 What Happens When Toggle Clicked 811 812 ``` 813 1. User clicks [MAINNET▼] button 814 2. switchNetwork('testnet') called 815 3. React state updates: currentNetwork = 'testnet' 816 4. localStorage.setItem('espresso-network', 'testnet') 817 5. window.dispatchEvent('network-changed') 818 6. All components re-render: 819 - stats.tsx shows [TESTNET▼] 820 - interface.tsx uses testnet config 821 - API calls go to decaf.testnet.espresso.network 822 - WebSocket reconnects to testnet 823 7. Done (<500ms) 824 ``` 825 826 **No page reload!** ✅ 827 828 --- 829 830 ## 💬 Discussion Questions 831 832 ### 1. Toggle Button Design 833 **Proposed**: Simple button in stats header 834 ``` 835 [MAINNET▼] ← Green, inline with status 836 [TESTNET▼] ← Orange, inline with status 837 ``` 838 839 **Alternative**: Separate toggle switch? 840 841 **Your preference?** ___ 842 843 --- 844 845 ### 2. Default Behavior 846 **Proposed**: 847 - First visit: Mainnet 848 - After toggle: Remember in localStorage 849 - Page reload: Use remembered choice 850 851 **Approve?** Y/N ___ 852 853 --- 854 855 ### 3. Testnet Name 856 **Issue**: Web search shows "DECAF" testnet, but also mentions "Cappuccino" in docs 857 858 **Question**: Should button show: 859 - A. "TESTNET" (generic) 860 - B. "DECAF" (specific) 861 - C. "TEST" (short) 862 863 **Your choice?** ___ 864 865 --- 866 867 ### 4. Backward Compatibility 868 **Strategy**: 869 - Keep old function signatures 870 - Add overloaded versions 871 - Existing code keeps working 872 873 **Example**: 874 ```typescript 875 // Old code still works: 876 getApiUrl('/status/block-height') 877 878 // New code can specify network: 879 getApiUrl('testnet', '/status/block-height') 880 ``` 881 882 **Approve?** Y/N ___ 883 884 --- 885 886 ### 5. WebSocket Reconnection 887 **Issue**: WebSocket needs to reconnect on network switch 888 889 **Strategy**: 890 - Listen for 'network-changed' event 891 - Auto-disconnect old connection 892 - Auto-connect to new network 893 894 **Implement now or later?** ___ 895 896 --- 897 898 ## ✅ Final Checks 899 900 - [ ] All URLs verified from official docs 901 - [ ] No new files - uses existing structure 902 - [ ] Toggle integrated into existing stats header 903 - [ ] Backward compatible 904 - [ ] localStorage persistence 905 - [ ] ~200 lines total change 906 - [ ] All changes in 5 files only 907 908 --- 909 910 ## 🎯 Implementation Steps (After Approval) 911 912 1. **Backup**: Create branch `git checkout -b network-toggle` 913 2. **File 1**: Modify config.ts (add testnet config) 914 3. **File 2**: Modify networkcontext.tsx (add state + switch) 915 4. **File 3**: Modify stats.tsx (add toggle button) 916 5. **File 4**: Modify useNet.ts (support current network) 917 6. **File 5**: Update env.example (add testnet vars) 918 7. **Test**: `npm run dev` and toggle between networks 919 8. **Verify**: Check API calls go to correct endpoints 920 9. **Commit**: `git commit -m "Add mainnet/testnet toggle"` 921 922 **Timeline**: 1-2 hours for implementation + testing 923 924 --- 925 926 ## 📝 Review Checklist 927 928 Please review and answer: 929 930 1. **URLs**: Are DECAF testnet URLs correct? ✅ (verified) 931 2. **Button Design**: Approve the inline button style? ___ 932 3. **Default**: OK with localStorage + mainnet default? ___ 933 4. **Label**: Show "TESTNET", "DECAF", or "TEST"? ___ 934 5. **Backward Compat**: Approve the strategy? ___ 935 6. **Data Flow**: Complete trace verified? ✅ (see COMPLETE_FLOW_ANALYSIS.md) 936 7. **Ready**: Ready to implement? ___ 937 938 --- 939 940 ## 📚 Supporting Documents 941 942 1. **NO_NEW_FILES_PLAN.md** (this file) - Complete implementation plan 943 2. **COMPLETE_FLOW_ANALYSIS.md** - Full data flow trace (ENV → Display) 944 3. **CRITICAL_UPDATES.md** - What was discovered during deep analysis 945 4. **DISCUSSION_SUMMARY.md** - Quick questions and decisions 946 947 --- 948 949 *NO NEW FILES - Uses existing structure - 8 files only - ~246 lines* 950 *All URLs verified from official Espresso docs and live testing* 951 *Complete data flow traced and verified from ENV to display*