/ CODE_PATCHES.md
CODE_PATCHES.md
1 # Network Toggle - Exact Code Patches (Before/After) 2 3 ## 🎯 Purpose 4 5 This document shows **EXACT line-by-line changes** for implementing the network toggle feature. 6 7 **Format**: 8 - ❌ Lines to REMOVE (or old version) 9 - ✅ Lines to ADD (or new version) 10 11 --- 12 13 ## 📁 File 1: `src/lib/config.ts` 14 15 ### BEFORE (Current - Mainnet Only) 16 17 ```typescript 18 interface NetworkConfig { 19 name: string 20 apiBaseUrl: string 21 apiVersion: string 22 wsBaseUrl: string 23 scanBaseUrl: string 24 webWorkerUrl: string 25 } 26 if (typeof window === 'undefined') { 27 const required = [ 28 'NEXT_PUBLIC_MAINNET_API_BASE_URL', 29 'NEXT_PUBLIC_MAINNET_API_VERSION', 30 'NEXT_PUBLIC_MAINNET_WS_BASE_URL', 31 'NEXT_PUBLIC_MAINNET_SCAN_BASE_URL', 32 'NEXT_PUBLIC_MAINNET_WEB_WORKER_URL' 33 ]; 34 35 const missing = required.filter(key => !process.env[key]); 36 } 37 38 const config = { 39 MAINNET_API_BASE_URL: process.env.NEXT_PUBLIC_MAINNET_API_BASE_URL!, 40 MAINNET_API_VERSION: process.env.NEXT_PUBLIC_MAINNET_API_VERSION!, 41 MAINNET_WS_BASE_URL: process.env.NEXT_PUBLIC_MAINNET_WS_BASE_URL!, 42 MAINNET_SCAN_BASE_URL: process.env.NEXT_PUBLIC_MAINNET_SCAN_BASE_URL!, 43 MAINNET_WEB_WORKER_URL: process.env.NEXT_PUBLIC_MAINNET_WEB_WORKER_URL!, 44 CURRENT_NETWORK: process.env.NEXT_PUBLIC_NETWORK || 'mainnet', 45 NETWORK_STATS_REFRESH_MS: parseInt(process.env.NEXT_PUBLIC_NETWORK_STATS_REFRESH_MS || '30000') 46 } 47 48 const MAINNET_CONFIG: NetworkConfig = { 49 name: 'Mainnet', 50 apiBaseUrl: config.MAINNET_API_BASE_URL, 51 apiVersion: config.MAINNET_API_VERSION, 52 wsBaseUrl: config.MAINNET_WS_BASE_URL, 53 scanBaseUrl: config.MAINNET_SCAN_BASE_URL, 54 webWorkerUrl: config.MAINNET_WEB_WORKER_URL 55 } 56 57 export const getCurrentNetworkConfig = (): NetworkConfig => { 58 return MAINNET_CONFIG 59 } 60 61 export const getApiUrl = (endpoint: string): string => { 62 const network = getCurrentNetworkConfig() 63 return `${network.apiBaseUrl}/${network.apiVersion}${endpoint}` 64 } 65 66 export const getWebSocketUrl = (endpoint: string): string => { 67 const network = getCurrentNetworkConfig() 68 return `${network.wsBaseUrl}/${network.apiVersion}${endpoint}` 69 } 70 71 export const getScanUrl = (path: string): string => { 72 const network = getCurrentNetworkConfig() 73 return `${network.scanBaseUrl}${path}` 74 } 75 76 export const getWebWorkerUrl = (): string => { 77 const network = getCurrentNetworkConfig() 78 return network.webWorkerUrl 79 } 80 81 export const getNetworkStatsRefreshMs = (): number => { 82 return config.NETWORK_STATS_REFRESH_MS 83 } 84 85 export { config } 86 ``` 87 88 ### AFTER (Multi-Network Support) 89 90 ```typescript 91 // ✅ ADD: Network type 92 export type NetworkType = 'mainnet' | 'testnet'; 93 94 interface NetworkConfig { 95 name: string 96 apiBaseUrl: string 97 apiVersion: string 98 wsBaseUrl: string 99 scanBaseUrl: string 100 webWorkerUrl: string 101 caffNodeUrl: string // ✅ ADD: Caff Node URL 102 } 103 104 if (typeof window === 'undefined') { 105 const required = [ 106 'NEXT_PUBLIC_MAINNET_API_BASE_URL', 107 'NEXT_PUBLIC_MAINNET_API_VERSION', 108 'NEXT_PUBLIC_MAINNET_WS_BASE_URL', 109 'NEXT_PUBLIC_MAINNET_SCAN_BASE_URL', 110 'NEXT_PUBLIC_MAINNET_WEB_WORKER_URL', 111 // ✅ ADD: Testnet env vars 112 'NEXT_PUBLIC_TESTNET_API_BASE_URL', 113 'NEXT_PUBLIC_TESTNET_API_VERSION', 114 'NEXT_PUBLIC_TESTNET_WS_BASE_URL', 115 'NEXT_PUBLIC_TESTNET_SCAN_BASE_URL', 116 'NEXT_PUBLIC_TESTNET_WEB_WORKER_URL' 117 ]; 118 119 const missing = required.filter(key => !process.env[key]); 120 // ✅ CHANGE: Warning instead of error 121 if (missing.length > 0) { 122 console.warn('Missing environment variables:', missing); 123 } 124 } 125 126 const config = { 127 // Mainnet 128 MAINNET_API_BASE_URL: process.env.NEXT_PUBLIC_MAINNET_API_BASE_URL!, 129 MAINNET_API_VERSION: process.env.NEXT_PUBLIC_MAINNET_API_VERSION!, 130 MAINNET_WS_BASE_URL: process.env.NEXT_PUBLIC_MAINNET_WS_BASE_URL!, 131 MAINNET_SCAN_BASE_URL: process.env.NEXT_PUBLIC_MAINNET_SCAN_BASE_URL!, 132 MAINNET_WEB_WORKER_URL: process.env.NEXT_PUBLIC_MAINNET_WEB_WORKER_URL!, 133 MAINNET_CAFF_NODE_URL: process.env.NEXT_PUBLIC_MAINNET_CAFF_NODE_URL || 'https://rari.caff.mainnet.espresso.network', 134 135 // ✅ ADD: Testnet config 136 TESTNET_API_BASE_URL: process.env.NEXT_PUBLIC_TESTNET_API_BASE_URL || 'https://query.testnet.espresso.network', 137 TESTNET_API_VERSION: process.env.NEXT_PUBLIC_TESTNET_API_VERSION || 'v0', 138 TESTNET_WS_BASE_URL: process.env.NEXT_PUBLIC_TESTNET_WS_BASE_URL || 'wss://query.testnet.espresso.network', 139 TESTNET_SCAN_BASE_URL: process.env.NEXT_PUBLIC_TESTNET_SCAN_BASE_URL || 'https://explorer.testnet.espresso.network', 140 TESTNET_WEB_WORKER_URL: process.env.NEXT_PUBLIC_TESTNET_WEB_WORKER_URL || 'https://explorer.testnet.espresso.network/assets/testnet-worker.js', 141 TESTNET_CAFF_NODE_URL: process.env.NEXT_PUBLIC_TESTNET_CAFF_NODE_URL || 'https://rari.caff.testnet.espresso.network', 142 143 // ✅ CHANGE: Default network 144 DEFAULT_NETWORK: (process.env.NEXT_PUBLIC_DEFAULT_NETWORK as NetworkType) || 'mainnet', 145 NETWORK_STATS_REFRESH_MS: parseInt(process.env.NEXT_PUBLIC_NETWORK_STATS_REFRESH_MS || '30000') 146 } 147 148 const MAINNET_CONFIG: NetworkConfig = { 149 name: 'Mainnet', 150 apiBaseUrl: config.MAINNET_API_BASE_URL, 151 apiVersion: config.MAINNET_API_VERSION, 152 wsBaseUrl: config.MAINNET_WS_BASE_URL, 153 scanBaseUrl: config.MAINNET_SCAN_BASE_URL, 154 webWorkerUrl: config.MAINNET_WEB_WORKER_URL, 155 caffNodeUrl: config.MAINNET_CAFF_NODE_URL // ✅ ADD 156 } 157 158 // ✅ ADD: Testnet config 159 const TESTNET_CONFIG: NetworkConfig = { 160 name: 'Testnet', 161 apiBaseUrl: config.TESTNET_API_BASE_URL, 162 apiVersion: config.TESTNET_API_VERSION, 163 wsBaseUrl: config.TESTNET_WS_BASE_URL, 164 scanBaseUrl: config.TESTNET_SCAN_BASE_URL, 165 webWorkerUrl: config.TESTNET_WEB_WORKER_URL, 166 caffNodeUrl: config.TESTNET_CAFF_NODE_URL 167 } 168 169 // ✅ ADD: Network registry 170 const NETWORK_CONFIGS: Record<NetworkType, NetworkConfig> = { 171 mainnet: MAINNET_CONFIG, 172 testnet: TESTNET_CONFIG 173 }; 174 175 // ✅ ADD: Get config by network 176 export const getNetworkConfig = (network: NetworkType): NetworkConfig => { 177 return NETWORK_CONFIGS[network]; 178 }; 179 180 // ✅ ADD: Get default network 181 export const getDefaultNetwork = (): NetworkType => { 182 return config.DEFAULT_NETWORK; 183 }; 184 185 // ❌ DEPRECATE: Old function (keep for compatibility) 186 export const getCurrentNetworkConfig = (): NetworkConfig => { 187 console.warn('getCurrentNetworkConfig is deprecated, use getNetworkConfig instead'); 188 return MAINNET_CONFIG; 189 } 190 191 // ✅ CHANGE: Add network parameter 192 export const getApiUrl = (network: NetworkType, endpoint: string): string => { 193 const networkConfig = getNetworkConfig(network); 194 return `${networkConfig.apiBaseUrl}/${networkConfig.apiVersion}${endpoint}`; 195 } 196 197 // ✅ CHANGE: Add network parameter 198 export const getWebSocketUrl = (network: NetworkType, endpoint: string): string => { 199 const networkConfig = getNetworkConfig(network); 200 return `${networkConfig.wsBaseUrl}/${networkConfig.apiVersion}${endpoint}`; 201 } 202 203 // ✅ CHANGE: Add network parameter 204 export const getScanUrl = (network: NetworkType, path: string): string => { 205 const networkConfig = getNetworkConfig(network); 206 return `${networkConfig.scanBaseUrl}${path}`; 207 } 208 209 // ✅ CHANGE: Add network parameter 210 export const getWebWorkerUrl = (network: NetworkType): string => { 211 const networkConfig = getNetworkConfig(network); 212 return networkConfig.webWorkerUrl; 213 } 214 215 // ✅ ADD: Get Caff Node URL 216 export const getCaffNodeUrl = (network: NetworkType): string => { 217 const networkConfig = getNetworkConfig(network); 218 return networkConfig.caffNodeUrl; 219 } 220 221 export const getNetworkStatsRefreshMs = (): number => { 222 return config.NETWORK_STATS_REFRESH_MS; 223 } 224 225 // ✅ ADD: Export types 226 export { config, type NetworkConfig, type NetworkType }; 227 ``` 228 229 **Summary of Changes**: 230 - ✅ Added `NetworkType` type 231 - ✅ Added `caffNodeUrl` to `NetworkConfig` 232 - ✅ Added testnet environment variables 233 - ✅ Added `TESTNET_CONFIG` object 234 - ✅ Added `getNetworkConfig(network)` function 235 - ✅ Changed all helper functions to accept `network` parameter 236 - ✅ Added `getCaffNodeUrl(network)` function 237 - ✅ Deprecated old `getCurrentNetworkConfig()` (backward compatible) 238 239 --- 240 241 ## 📁 File 2: `src/contexts/networkcontext.tsx` 242 243 ### BEFORE (Current - Static Mainnet) 244 245 ```typescript 246 "use client" 247 import { createContext, useContext, ReactNode } from 'react' 248 import { getCurrentNetwork } from '@/services/api/main' 249 250 interface NetCtx { 251 currentNetwork: 'mainnet' 252 networkInfo: { 253 name: string 254 apiBaseUrl: string 255 apiVersion: string 256 wsBaseUrl: string 257 scanBaseUrl: string 258 webWorkerUrl: string 259 } 260 } 261 262 const NetContext = createContext<NetCtx | undefined>(undefined) 263 264 interface NetProviderProps { 265 children: ReactNode 266 } 267 268 export function NetworkProvider({ children }: NetProviderProps) { 269 const currentNetwork = 'mainnet' 270 const networkInfo = getCurrentNetwork() 271 272 return ( 273 <NetContext.Provider value={{ 274 currentNetwork, 275 networkInfo 276 }}> 277 {children} 278 </NetContext.Provider> 279 ) 280 } 281 282 export function useNetwork() { 283 const context = useContext(NetContext) 284 if (context === undefined) { 285 return { 286 currentNetwork: 'mainnet' as const, 287 networkInfo: { 288 name: 'mainnet', 289 apiBaseUrl: '', 290 apiVersion: '', 291 wsBaseUrl: '', 292 scanBaseUrl: '', 293 webWorkerUrl: '' 294 } 295 } 296 } 297 return context 298 } 299 ``` 300 301 ### AFTER (Dynamic Multi-Network) 302 303 ```typescript 304 "use client" 305 // ✅ ADD: useState, useEffect 306 import { createContext, useContext, useState, useEffect, ReactNode } from 'react' 307 // ✅ CHANGE: Import from config instead 308 import { NetworkType, getNetworkConfig, getDefaultNetwork, type NetworkConfig } from '@/lib/config' 309 310 // ✅ CHANGE: Enhanced context type 311 interface NetworkContextType { 312 currentNetwork: NetworkType; 313 networkConfig: NetworkConfig; 314 switchNetwork: (network: NetworkType) => void; 315 isMainnet: boolean; 316 isTestnet: boolean; 317 } 318 319 // ✅ CHANGE: Update context 320 const NetContext = createContext<NetworkContextType | undefined>(undefined) 321 322 interface NetProviderProps { 323 children: ReactNode 324 } 325 326 export function NetworkProvider({ children }: NetProviderProps) { 327 // ✅ ADD: State management 328 const [currentNetwork, setCurrentNetwork] = useState<NetworkType>(() => { 329 // Initialize from localStorage or default 330 if (typeof window !== 'undefined') { 331 const saved = localStorage.getItem('espresso-network'); 332 if (saved === 'mainnet' || saved === 'testnet') { 333 return saved as NetworkType; 334 } 335 } 336 return getDefaultNetwork(); 337 }); 338 339 // ✅ ADD: Get current config 340 const networkConfig = getNetworkConfig(currentNetwork); 341 342 // ✅ ADD: Helper flags 343 const isMainnet = currentNetwork === 'mainnet'; 344 const isTestnet = currentNetwork === 'testnet'; 345 346 // ✅ ADD: Switch network function 347 const switchNetwork = (network: NetworkType) => { 348 console.log(`Switching network from ${currentNetwork} to ${network}`); 349 setCurrentNetwork(network); 350 351 // Persist to localStorage 352 if (typeof window !== 'undefined') { 353 localStorage.setItem('espresso-network', network); 354 } 355 356 // Emit custom event for services to react 357 if (typeof window !== 'undefined') { 358 window.dispatchEvent(new CustomEvent('network-changed', { 359 detail: { network, config: getNetworkConfig(network) } 360 })); 361 } 362 }; 363 364 // ✅ ADD: Log network changes 365 useEffect(() => { 366 console.log('Current network:', currentNetwork, networkConfig); 367 }, [currentNetwork]); 368 369 // ✅ CHANGE: Updated value 370 const value: NetworkContextType = { 371 currentNetwork, 372 networkConfig, 373 switchNetwork, 374 isMainnet, 375 isTestnet 376 }; 377 378 return ( 379 <NetContext.Provider value={value}> 380 {children} 381 </NetContext.Provider> 382 ) 383 } 384 385 // ✅ CHANGE: Updated hook 386 export function useNetwork(): NetworkContextType { 387 const context = useContext(NetContext) 388 if (context === undefined) { 389 throw new Error('useNetwork must be used within NetworkProvider'); 390 } 391 return context; 392 } 393 394 // ✅ ADD: Re-export types 395 export type { NetworkType, NetworkConfig }; 396 ``` 397 398 **Summary of Changes**: 399 - ✅ Added state management with `useState` 400 - ✅ Added localStorage persistence 401 - ✅ Added `switchNetwork()` function 402 - ✅ Added custom event emission 403 - ✅ Added `isMainnet`, `isTestnet` helpers 404 - ✅ Removed hardcoded 'mainnet' 405 - ✅ Better error handling 406 407 --- 408 409 ## 📁 File 3: `src/app/page.tsx` 410 411 ### BEFORE (Current - No Toggle) 412 413 ```typescript 414 import SearchInterface from "@/components/search/interface" 415 416 export default function Home() { 417 return ( 418 <div className="min-h-screen bg-white flex items-center justify-center p-4"> 419 <div className="w-full max-w-2xl"> 420 <SearchInterface /> 421 </div> 422 </div> 423 ) 424 } 425 ``` 426 427 ### AFTER (With Toggle) 428 429 ```typescript 430 import SearchInterface from "@/components/search/interface" 431 // ✅ ADD: Import NetworkToggle 432 import { NetworkToggle } from "@/components/ui/NetworkToggle" 433 434 export default function Home() { 435 return ( 436 // ✅ CHANGE: Remove centering, add header 437 <div className="min-h-screen bg-white"> 438 {/* ✅ ADD: Header with Network Toggle */} 439 <header className="border-b border-gray-200 bg-white sticky top-0 z-50"> 440 <div className="max-w-7xl mx-auto px-4 py-3 flex justify-between items-center"> 441 <h1 className="text-xl font-bold text-gray-900"> 442 ComposableScan 443 </h1> 444 <NetworkToggle /> 445 </div> 446 </header> 447 448 {/* ✅ CHANGE: Main content */} 449 <div className="flex items-center justify-center p-4"> 450 <div className="w-full max-w-2xl"> 451 <SearchInterface /> 452 </div> 453 </div> 454 </div> 455 ) 456 } 457 ``` 458 459 **Summary of Changes**: 460 - ✅ Added header with title and toggle 461 - ✅ Added sticky header (stays on scroll) 462 - ✅ Imported `NetworkToggle` component 463 - ✅ Adjusted layout structure 464 465 --- 466 467 ## 📁 File 4: `src/components/ui/NetworkToggle.tsx` (NEW FILE) 468 469 ### BEFORE 470 *File does not exist* 471 472 ### AFTER (Complete New File) 473 474 ```typescript 475 'use client'; 476 477 import React from 'react'; 478 import { useNetwork } from '@/contexts/networkcontext'; 479 import { motion } from 'framer-motion'; 480 481 /** 482 * Network toggle switch component 483 * Allows users to switch between mainnet and testnet 484 */ 485 export function NetworkToggle() { 486 const { currentNetwork, switchNetwork, isMainnet, isTestnet } = useNetwork(); 487 488 const handleToggle = () => { 489 const newNetwork = isMainnet ? 'testnet' : 'mainnet'; 490 switchNetwork(newNetwork); 491 }; 492 493 return ( 494 <div className="flex items-center gap-3"> 495 {/* Network Label */} 496 <span className="text-sm font-medium text-gray-700"> 497 Network: 498 </span> 499 500 {/* Toggle Switch */} 501 <button 502 onClick={handleToggle} 503 className="relative inline-flex h-8 w-32 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2" 504 style={{ 505 backgroundColor: isMainnet ? '#10b981' : '#f59e0b' 506 }} 507 aria-label={`Switch to ${isMainnet ? 'testnet' : 'mainnet'}`} 508 > 509 {/* Background Labels */} 510 <span className="absolute left-2 text-xs font-semibold text-white"> 511 {isMainnet ? 'MAIN' : ''} 512 </span> 513 <span className="absolute right-2 text-xs font-semibold text-white"> 514 {isTestnet ? 'TEST' : ''} 515 </span> 516 517 {/* Animated Slider */} 518 <motion.span 519 className="inline-block h-6 w-14 transform rounded-full bg-white shadow-lg" 520 layout 521 animate={{ 522 x: isMainnet ? 2 : 62 523 }} 524 transition={{ 525 type: "spring", 526 stiffness: 500, 527 damping: 30 528 }} 529 /> 530 </button> 531 532 {/* Network Badge */} 533 <span className={` 534 px-2 py-1 text-xs font-semibold rounded-full 535 ${isMainnet 536 ? 'bg-green-100 text-green-800' 537 : 'bg-orange-100 text-orange-800' 538 } 539 `}> 540 {currentNetwork.toUpperCase()} 541 </span> 542 </div> 543 ); 544 } 545 546 /** 547 * Compact version for mobile/small spaces 548 */ 549 export function NetworkToggleCompact() { 550 const { currentNetwork, switchNetwork, isMainnet } = useNetwork(); 551 552 return ( 553 <button 554 onClick={() => switchNetwork(isMainnet ? 'testnet' : 'mainnet')} 555 className={` 556 px-3 py-1 text-xs font-bold rounded-full transition-colors 557 ${isMainnet 558 ? 'bg-green-500 hover:bg-green-600 text-white' 559 : 'bg-orange-500 hover:bg-orange-600 text-white' 560 } 561 `} 562 > 563 {currentNetwork.toUpperCase()} 564 </button> 565 ); 566 } 567 ``` 568 569 **Summary**: 570 - ✅ New file: `src/components/ui/NetworkToggle.tsx` 571 - ✅ Full toggle component with animation 572 - ✅ Compact version included 573 - ✅ Accessible (ARIA labels) 574 - ✅ Visual feedback (colors, badges) 575 576 --- 577 578 ## 📁 File 5: `src/hooks/useNetworkApi.ts` (NEW FILE) 579 580 ### BEFORE 581 *File does not exist* 582 583 ### AFTER (Complete New File) 584 585 ```typescript 586 import { useNetwork } from '@/contexts/networkcontext'; 587 import { getApiUrl, getCaffNodeUrl } from '@/lib/config'; 588 import { useCallback } from 'react'; 589 590 /** 591 * Hook for network-aware API calls 592 * Automatically uses the current network from context 593 */ 594 export function useNetworkApi() { 595 const { currentNetwork, networkConfig } = useNetwork(); 596 597 /** 598 * Build API URL for current network 599 */ 600 const buildApiUrl = useCallback((endpoint: string) => { 601 return getApiUrl(currentNetwork, endpoint); 602 }, [currentNetwork]); 603 604 /** 605 * Make API call to current network 606 */ 607 const apiCall = useCallback(async (endpoint: string, options?: RequestInit) => { 608 const url = buildApiUrl(endpoint); 609 const response = await fetch(url, options); 610 if (!response.ok) { 611 throw new Error(`API call failed: ${response.statusText}`); 612 } 613 return response.json(); 614 }, [buildApiUrl]); 615 616 /** 617 * Make Caff Node RPC call to current network 618 */ 619 const caffNodeCall = useCallback(async (method: string, params: any[]) => { 620 const url = getCaffNodeUrl(currentNetwork); 621 const response = await fetch(url, { 622 method: 'POST', 623 headers: { 'Content-Type': 'application/json' }, 624 body: JSON.stringify({ 625 jsonrpc: '2.0', 626 method, 627 params, 628 id: 1 629 }) 630 }); 631 632 if (!response.ok) { 633 throw new Error(`Caff Node call failed: ${response.statusText}`); 634 } 635 636 const data = await response.json(); 637 if (data.error) { 638 throw new Error(`Caff Node error: ${data.error.message}`); 639 } 640 641 return data.result; 642 }, [currentNetwork]); 643 644 return { 645 currentNetwork, 646 networkConfig, 647 buildApiUrl, 648 apiCall, 649 caffNodeCall 650 }; 651 } 652 ``` 653 654 **Summary**: 655 - ✅ New file: `src/hooks/useNetworkApi.ts` 656 - ✅ Network-aware API calls 657 - ✅ Caff Node RPC support 658 - ✅ Auto-updates when network switches 659 660 --- 661 662 ## 📁 File 6: `env.example` 663 664 ### BEFORE (Current) 665 666 ```bash 667 # Environment variables for ComposableScan 668 # Copy this file to .env.local and configure for your environment 669 670 # Espresso Network Mainnet Configuration 671 NEXT_PUBLIC_MAINNET_API_BASE_URL=https://query.main.net.espresso.network 672 NEXT_PUBLIC_MAINNET_API_VERSION=v0 673 NEXT_PUBLIC_MAINNET_WS_BASE_URL=wss://query.main.net.espresso.network 674 NEXT_PUBLIC_MAINNET_SCAN_BASE_URL=https://explorer.main.net.espresso.network 675 NEXT_PUBLIC_MAINNET_WEB_WORKER_URL=https://explorer.main.net.espresso.network/assets/node_validator_web_worker_api.js-bT9djMJi.js 676 677 # Network selection (mainnet is the primary network) 678 NEXT_PUBLIC_NETWORK=mainnet 679 680 # Optional: Network statistics refresh interval (milliseconds) 681 # NEXT_PUBLIC_NETWORK_STATS_REFRESH_MS=30000 682 ``` 683 684 ### AFTER (Multi-Network) 685 686 ```bash 687 # Environment variables for ComposableScan 688 # Copy this file to .env.local and configure for your environment 689 690 # ============================================ 691 # MAINNET CONFIGURATION 692 # ============================================ 693 NEXT_PUBLIC_MAINNET_API_BASE_URL=https://query.main.net.espresso.network 694 NEXT_PUBLIC_MAINNET_API_VERSION=v0 695 NEXT_PUBLIC_MAINNET_WS_BASE_URL=wss://query.main.net.espresso.network 696 NEXT_PUBLIC_MAINNET_SCAN_BASE_URL=https://explorer.main.net.espresso.network 697 NEXT_PUBLIC_MAINNET_WEB_WORKER_URL=https://explorer.main.net.espresso.network/assets/node_validator_web_worker_api.js-bT9djMJi.js 698 # ✅ ADD: Mainnet Caff Node 699 NEXT_PUBLIC_MAINNET_CAFF_NODE_URL=https://rari.caff.mainnet.espresso.network 700 701 # ============================================ 702 # TESTNET CONFIGURATION 703 # ============================================ 704 # ✅ ADD: All testnet variables 705 NEXT_PUBLIC_TESTNET_API_BASE_URL=https://query.testnet.espresso.network 706 NEXT_PUBLIC_TESTNET_API_VERSION=v0 707 NEXT_PUBLIC_TESTNET_WS_BASE_URL=wss://query.testnet.espresso.network 708 NEXT_PUBLIC_TESTNET_SCAN_BASE_URL=https://explorer.testnet.espresso.network 709 NEXT_PUBLIC_TESTNET_WEB_WORKER_URL=https://explorer.testnet.espresso.network/assets/testnet-worker.js 710 NEXT_PUBLIC_TESTNET_CAFF_NODE_URL=https://rari.caff.testnet.espresso.network 711 712 # ============================================ 713 # GENERAL SETTINGS 714 # ============================================ 715 # ✅ CHANGE: Default network (mainnet or testnet) 716 NEXT_PUBLIC_DEFAULT_NETWORK=mainnet 717 718 # Network statistics refresh interval (milliseconds) 719 NEXT_PUBLIC_NETWORK_STATS_REFRESH_MS=30000 720 ``` 721 722 **Summary of Changes**: 723 - ✅ Added all testnet environment variables 724 - ✅ Added Caff Node URLs for both networks 725 - ✅ Better organization with sections 726 - ✅ Changed NEXT_PUBLIC_NETWORK to NEXT_PUBLIC_DEFAULT_NETWORK 727 728 --- 729 730 ## 📁 File 7: `src/app/layout.tsx` (ALREADY HAS PROVIDER) 731 732 ### BEFORE (Current) 733 734 ```typescript 735 import type { Metadata } from "next"; 736 import "./globals.css"; 737 import { NetworkProvider } from "@/contexts/networkcontext"; 738 739 export const metadata: Metadata = { 740 title: "Composable Scan", 741 description: "Scan the Espresso Network in real-time", 742 }; 743 744 export default function RootLayout({ 745 children, 746 }: Readonly<{ 747 children: React.ReactNode; 748 }>) { 749 return ( 750 <html lang="en" className="dark"> 751 <body 752 className="antialiased font-sans" 753 > 754 <NetworkProvider> 755 {children} 756 </NetworkProvider> 757 </body> 758 </html> 759 ); 760 } 761 ``` 762 763 ### AFTER 764 765 **NO CHANGES NEEDED** ✅ 766 767 *Layout already has NetworkProvider, no modifications required!* 768 769 --- 770 771 ## 📊 Summary of All Changes 772 773 ### New Files (3) 774 1. ✅ `src/components/ui/NetworkToggle.tsx` - Toggle component 775 2. ✅ `src/hooks/useNetworkApi.ts` - Network-aware API hook 776 3. ✅ (Later) `src/__tests__/network-toggle.test.tsx` - Tests 777 778 ### Modified Files (4) 779 1. ✅ `src/lib/config.ts` - Add testnet config + network parameter 780 2. ✅ `src/contexts/networkcontext.tsx` - Add state + switch function 781 3. ✅ `src/app/page.tsx` - Add header + toggle 782 4. ✅ `env.example` - Add testnet variables 783 784 ### No Changes (1) 785 1. ✅ `src/app/layout.tsx` - Already has NetworkProvider 786 787 ### Total Lines Changed 788 - **Added**: ~250 lines 789 - **Modified**: ~100 lines 790 - **Removed**: ~20 lines (replaced) 791 - **Net Change**: +230 lines 792 793 --- 794 795 ## 🎯 Discussion Points 796 797 ### 1. Config File Changes 798 **Question**: Are the testnet URLs correct? 799 800 ```typescript 801 TESTNET_API_BASE_URL: 'https://query.testnet.espresso.network' 802 TESTNET_WS_BASE_URL: 'wss://query.testnet.espresso.network' 803 TESTNET_CAFF_NODE_URL: 'https://rari.caff.testnet.espresso.network' 804 ``` 805 806 **Need to confirm**: 807 - [ ] Testnet explorer URL 808 - [ ] Testnet web worker URL 809 810 --- 811 812 ### 2. NetworkContext Changes 813 **Question**: Should we add loading state during switch? 814 815 **Option A**: No loading (instant switch) 816 ```typescript 817 const switchNetwork = (network) => { 818 setCurrentNetwork(network); // Instant 819 } 820 ``` 821 822 **Option B**: With loading state 823 ```typescript 824 const switchNetwork = async (network) => { 825 setIsLoading(true); 826 setCurrentNetwork(network); 827 await new Promise(r => setTimeout(r, 300)); // Brief delay 828 setIsLoading(false); 829 } 830 ``` 831 832 **Recommendation**: Option A (instant, simpler) 833 834 --- 835 836 ### 3. Toggle Placement 837 **Question**: Do you like the header placement? 838 839 **Current**: 840 ``` 841 ┌────────────────────────────────────────┐ 842 │ ComposableScan [ MAIN | test ] 🟢 │ 843 └────────────────────────────────────────┘ 844 ``` 845 846 **Alternative**: Above search box? 847 848 --- 849 850 ### 4. Backward Compatibility 851 **Question**: Keep old functions or break? 852 853 **Current Plan**: Keep old functions with deprecation warning 854 ```typescript 855 export const getCurrentNetworkConfig = (): NetworkConfig => { 856 console.warn('Deprecated, use getNetworkConfig instead'); 857 return MAINNET_CONFIG; 858 } 859 ``` 860 861 **Alternative**: Remove old functions (breaking change) 862 863 **Recommendation**: Keep for now, remove in v2.0 864 865 --- 866 867 ### 5. Default Network 868 **Question**: First visit default? 869 870 **Option A**: Always mainnet 871 **Option B**: Remember last choice (localStorage) 872 873 **Current Implementation**: Option B with mainnet default 874 875 --- 876 877 ## ✅ Implementation Checklist 878 879 Before we start coding, confirm: 880 881 - [ ] All testnet URLs are correct 882 - [ ] Toggle placement is approved 883 - [ ] Color scheme is acceptable (green/orange) 884 - [ ] localStorage persistence is OK 885 - [ ] No loading state is fine 886 - [ ] Backward compatibility approach is good 887 - [ ] Ready to create new files 888 - [ ] Ready to modify existing files 889 890 --- 891 892 ## 🚀 Ready to Implement? 893 894 **All code changes are documented above.** 895 896 **Next steps**: 897 1. Review all BEFORE/AFTER sections 898 2. Confirm testnet URLs 899 3. Approve the changes 900 4. Start implementation! 901 902 **Questions? Concerns? Changes needed?** 903 904 Let's discuss before we start coding! 💪 905 906 --- 907 908 *Code Patches Documentation v1.0* 909 *Ready for review and discussion*