/ 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*