+ Are you sure you want to switch to{" "}
+
+ {pendingNetwork === "mainnet"
+ ? "Stellar Mainnet"
+ : "Stellar Testnet"}
+
+ ? Cached data will be cleared and the page will reload with the
+ new network configuration.
+
+
+
+
+
+
+
+ )}
+ >
+ );
+}
diff --git a/src/contexts/NetworkContext.tsx b/src/contexts/NetworkContext.tsx
new file mode 100644
index 0000000..9a4769e
--- /dev/null
+++ b/src/contexts/NetworkContext.tsx
@@ -0,0 +1,65 @@
+"use client";
+
+import React, {
+ createContext,
+ useContext,
+ useState,
+ useCallback,
+ useEffect,
+} from "react";
+import {
+ NetworkId,
+ NetworkConfig,
+ getNetworkConfig,
+ loadSelectedNetwork,
+ saveSelectedNetwork,
+} from "@/lib/network";
+
+interface NetworkContextType {
+ /** The currently active network id. */
+ networkId: NetworkId;
+ /** Full config object for the active network. */
+ network: NetworkConfig;
+ /** Switch to a different network. Persists the choice and clears cached data. */
+ switchNetwork: (id: NetworkId) => void;
+}
+
+const NetworkContext = createContext({
+ networkId: "testnet",
+ network: getNetworkConfig("testnet"),
+ switchNetwork: () => {},
+});
+
+export function useNetwork() {
+ return useContext(NetworkContext);
+}
+
+export function NetworkProvider({ children }: { children: React.ReactNode }) {
+ const [networkId, setNetworkId] = useState("testnet");
+
+ // Load persisted selection on mount (client-only)
+ useEffect(() => {
+ setNetworkId(loadSelectedNetwork());
+ }, []);
+
+ const switchNetwork = useCallback(
+ (id: NetworkId) => {
+ if (id === networkId) return;
+ saveSelectedNetwork(id);
+ setNetworkId(id);
+ // Clear any cached contract / group data so the UI re-fetches for the new network
+ if (typeof window !== "undefined") {
+ sessionStorage.clear();
+ }
+ },
+ [networkId],
+ );
+
+ const network = getNetworkConfig(networkId);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/lib/network.ts b/src/lib/network.ts
new file mode 100644
index 0000000..29a5ead
--- /dev/null
+++ b/src/lib/network.ts
@@ -0,0 +1,77 @@
+/**
+ * Network configuration for Stellar Testnet and Mainnet.
+ * Provides network definitions, persistence, and helpers for switching
+ * between networks in the SoroSave frontend.
+ */
+
+export type NetworkId = "testnet" | "mainnet";
+
+export interface NetworkConfig {
+ id: NetworkId;
+ name: string;
+ rpcUrl: string;
+ networkPassphrase: string;
+ horizonUrl: string;
+ contractId: string;
+}
+
+const NETWORKS: Record = {
+ testnet: {
+ id: "testnet",
+ name: "Testnet",
+ rpcUrl:
+ process.env.NEXT_PUBLIC_TESTNET_RPC_URL ||
+ "https://soroban-testnet.stellar.org",
+ networkPassphrase: "Test SDF Network ; September 2015",
+ horizonUrl: "https://horizon-testnet.stellar.org",
+ contractId: process.env.NEXT_PUBLIC_TESTNET_CONTRACT_ID || "",
+ },
+ mainnet: {
+ id: "mainnet",
+ name: "Mainnet",
+ rpcUrl:
+ process.env.NEXT_PUBLIC_MAINNET_RPC_URL ||
+ "https://soroban.stellar.org",
+ networkPassphrase: "Public Global Stellar Network ; September 2015",
+ horizonUrl: "https://horizon.stellar.org",
+ contractId: process.env.NEXT_PUBLIC_MAINNET_CONTRACT_ID || "",
+ },
+};
+
+const STORAGE_KEY = "sorosave_selected_network";
+
+/**
+ * Retrieve the full config for a given network ID.
+ */
+export function getNetworkConfig(id: NetworkId): NetworkConfig {
+ return NETWORKS[id];
+}
+
+/**
+ * Return all available networks.
+ */
+export function getAllNetworks(): NetworkConfig[] {
+ return Object.values(NETWORKS);
+}
+
+/**
+ * Persist the selected network in localStorage.
+ */
+export function saveSelectedNetwork(id: NetworkId): void {
+ if (typeof window !== "undefined") {
+ localStorage.setItem(STORAGE_KEY, id);
+ }
+}
+
+/**
+ * Load the previously selected network from localStorage, defaulting to testnet.
+ */
+export function loadSelectedNetwork(): NetworkId {
+ if (typeof window !== "undefined") {
+ const stored = localStorage.getItem(STORAGE_KEY);
+ if (stored === "testnet" || stored === "mainnet") {
+ return stored;
+ }
+ }
+ return "testnet";
+}
diff --git a/src/lib/sorosave.ts b/src/lib/sorosave.ts
index ca84abb..4e49712 100644
--- a/src/lib/sorosave.ts
+++ b/src/lib/sorosave.ts
@@ -1,15 +1,27 @@
import { SoroSaveClient } from "@sorosave/sdk";
+import { getNetworkConfig, loadSelectedNetwork, type NetworkConfig } from "./network";
-const TESTNET_RPC_URL =
- process.env.NEXT_PUBLIC_RPC_URL || "https://soroban-testnet.stellar.org";
-const NETWORK_PASSPHRASE =
- process.env.NEXT_PUBLIC_NETWORK_PASSPHRASE || "Test SDF Network ; September 2015";
-const CONTRACT_ID = process.env.NEXT_PUBLIC_CONTRACT_ID || "";
+/**
+ * Create a SoroSaveClient configured for the given network.
+ * Falls back to env vars for backwards compatibility when no network is provided.
+ */
+export function createSoroSaveClient(network?: NetworkConfig): SoroSaveClient {
+ const config = network ?? getNetworkConfig(loadSelectedNetwork());
-export const sorosaveClient = new SoroSaveClient({
- contractId: CONTRACT_ID,
- rpcUrl: TESTNET_RPC_URL,
- networkPassphrase: NETWORK_PASSPHRASE,
-});
+ return new SoroSaveClient({
+ contractId: config.contractId,
+ rpcUrl: config.rpcUrl,
+ networkPassphrase: config.networkPassphrase,
+ });
+}
-export { TESTNET_RPC_URL, NETWORK_PASSPHRASE, CONTRACT_ID };
+// Default client — uses the persisted network selection.
+// Components that are network-aware should prefer calling createSoroSaveClient()
+// with the network from useNetwork() instead.
+export const sorosaveClient = createSoroSaveClient();
+
+// Re-export for backwards compatibility
+const fallbackConfig = getNetworkConfig(loadSelectedNetwork());
+export const TESTNET_RPC_URL = fallbackConfig.rpcUrl;
+export const NETWORK_PASSPHRASE = fallbackConfig.networkPassphrase;
+export const CONTRACT_ID = fallbackConfig.contractId;