From e4e2fa949b2d1d16c81bd9fe5fe25b1080b9713b Mon Sep 17 00:00:00 2001 From: Mmeso Love Date: Sun, 22 Feb 2026 09:05:17 +0100 Subject: [PATCH 1/2] feat: replace StarkNet with Stellar wallet integration - Created StellarWalletProvider context with connection management - Updated ConnectWalletModal to support 5 Stellar wallets (Freighter, xBull, Albedo, Lobstr, Hana) - Removed @starknet-react dependencies from wallet components - Added wallet icons for all supported Stellar wallets - Implemented error handling for wallet not installed, user rejection, and connection failures - Maintained existing UI design and auto-close behavior --- components/connectWallet.tsx | 163 ++++++++++++++++++++-------- components/providers.tsx | 32 +----- contexts/stellar-wallet-context.tsx | 112 +++++++++++++++++++ public/wallets/albedo.svg | 4 + public/wallets/freighter.svg | 5 + public/wallets/hana.svg | 5 + public/wallets/lobstr.svg | 6 + public/wallets/xbull.svg | 5 + 8 files changed, 258 insertions(+), 74 deletions(-) create mode 100644 contexts/stellar-wallet-context.tsx create mode 100644 public/wallets/albedo.svg create mode 100644 public/wallets/freighter.svg create mode 100644 public/wallets/hana.svg create mode 100644 public/wallets/lobstr.svg create mode 100644 public/wallets/xbull.svg diff --git a/components/connectWallet.tsx b/components/connectWallet.tsx index 5fad2c78..f71b53d0 100644 --- a/components/connectWallet.tsx +++ b/components/connectWallet.tsx @@ -5,55 +5,93 @@ import type React from "react"; import Image from "next/image"; import { useState, useEffect } from "react"; import { MdClose } from "react-icons/md"; -import { useConnect, useAccount } from "@starknet-react/core"; +import { useStellarWallet } from "@/contexts/stellar-wallet-context"; +import { + FREIGHTER_ID, + xBULL_ID, + ALBEDO_ID, + LOBSTR_ID, + HANA_ID, +} from "@creit.tech/stellar-wallets-kit"; interface ConnectModalProps { isModalOpen: boolean; setIsModalOpen: (isModalOpen: boolean) => void; } +interface StellarWallet { + id: string; + name: string; + icon: string; + installUrl?: string; +} + +// Stellar wallet configurations +const STELLAR_WALLETS: StellarWallet[] = [ + { + id: FREIGHTER_ID, + name: "Freighter", + icon: "/wallets/freighter.svg", + installUrl: + "https://chrome.google.com/webstore/detail/freighter/bcacfldlkkdogcmkkibnjlakofdplcbk", + }, + { + id: xBULL_ID, + name: "xBull", + icon: "/wallets/xbull.svg", + installUrl: "https://xbull.app/", + }, + { + id: ALBEDO_ID, + name: "Albedo", + icon: "/wallets/albedo.svg", + installUrl: "https://albedo.link/", + }, + { + id: LOBSTR_ID, + name: "Lobstr", + icon: "/wallets/lobstr.svg", + installUrl: "https://lobstr.co/", + }, + { + id: HANA_ID, + name: "Hana", + icon: "/wallets/hana.svg", + installUrl: "https://hanawallet.io/", + }, +]; + export default function ConnectWalletModal({ isModalOpen, setIsModalOpen, }: ConnectModalProps) { - const { connect, connectors } = useConnect(); - const { isConnected, status } = useAccount(); + const { isConnected, isConnecting, error, connectWallet } = + useStellarWallet(); - const [selectedWallet, setSelectedWallet] = useState(connectors?.[0] || null); - const [isConnecting, setIsConnecting] = useState(false); + const [selectedWallet, setSelectedWallet] = useState(null); const [connectionError, setConnectionError] = useState(null); // Close modal when wallet connects successfully useEffect(() => { if (isConnected && isModalOpen) { setIsModalOpen(false); - setIsConnecting(false); + setSelectedWallet(null); setConnectionError(null); } }, [isConnected, isModalOpen, setIsModalOpen]); - // Handle connection status changes + // Update connection error from context useEffect(() => { - if (status === "connecting") { - setIsConnecting(true); - setConnectionError(null); - } else if (status === "disconnected" && isConnecting) { - // Braavos (and some wallets) briefly hit "disconnected" during the - // popup/handshake before landing on "connected". Debounce so that - // a transient disconnect doesn't look like a failure. - const timer = setTimeout(() => { - setIsConnecting(false); - setConnectionError("Connection failed. Please try again."); - console.log("[ConnectWalletModal] Disconnected, resetting state"); - }, 2000); - return () => clearTimeout(timer); + if (error) { + setConnectionError(error); } - }, [status, isConnecting]); + }, [error]); const handleOverlayClick = () => { if (!isConnecting) { setIsModalOpen(false); setConnectionError(null); + setSelectedWallet(null); } }; @@ -61,23 +99,40 @@ export default function ConnectWalletModal({ e.stopPropagation(); }; - const handleWalletClick = async (wallet: (typeof connectors)[0]) => { + const handleWalletClick = async (wallet: StellarWallet) => { if (isConnecting) { return; } try { - setSelectedWallet(wallet); - setIsConnecting(true); + setSelectedWallet(wallet.id); setConnectionError(null); - await connect({ connector: wallet }); + await connectWallet(wallet.id); + } catch (err) { + console.error("[ConnectWalletModal] Connection error:", err); + const errorMessage = + err instanceof Error ? err.message : "Failed to connect wallet"; - // The AuthProvider will automatically detect and store the active connector - } catch (error) { - console.error("[ConnectWalletModal] Connection error:", error); - setConnectionError("Failed to connect wallet. Please try again."); - setIsConnecting(false); + // Check if wallet is not installed + if ( + errorMessage.includes("not installed") || + errorMessage.includes("not found") || + errorMessage.includes("not available") + ) { + setConnectionError( + `${wallet.name} is not installed. Please install it first.` + ); + } else if ( + errorMessage.includes("rejected") || + errorMessage.includes("denied") + ) { + setConnectionError("Connection rejected. Please try again."); + } else { + setConnectionError(errorMessage); + } + + setSelectedWallet(null); } }; @@ -85,9 +140,15 @@ export default function ConnectWalletModal({ if (!isConnecting) { setIsModalOpen(false); setConnectionError(null); + setSelectedWallet(null); } }; + const getInstallLink = (walletId: string) => { + const wallet = STELLAR_WALLETS.find(w => w.id === walletId); + return wallet?.installUrl; + }; + return (
{connectionError}

+ {connectionError.includes("not installed") && selectedWallet && ( + + Install Wallet + + )}
)} {/* Wallet List */} -
- {connectors.map(wallet => ( +
+ {STELLAR_WALLETS.map(wallet => (
handleWalletClick(wallet)}>
))} diff --git a/components/providers.tsx b/components/providers.tsx index ae5c784a..3db5e24c 100644 --- a/components/providers.tsx +++ b/components/providers.tsx @@ -2,18 +2,9 @@ import type React from "react"; import { useEffect } from "react"; import { SWRConfig } from "swr"; -import { sepolia, mainnet } from "@starknet-react/chains"; -import { - StarknetConfig, - publicProvider, - argent, - braavos, - useInjectedConnectors, - voyager, -} from "@starknet-react/core"; import { AuthProvider } from "./auth/auth-provider"; - import { ThemeProvider } from "@/contexts/theme-context"; +import { StellarWalletProvider } from "@/contexts/stellar-wallet-context"; // Create a stable cache instance outside the component to ensure proper cache sharing const swrCache = new Map(); @@ -39,17 +30,6 @@ function StarknetKeyCleanup({ children }: { children: React.ReactNode }) { } export function Providers({ children }: { children: React.ReactNode }) { - const { connectors } = useInjectedConnectors({ - // Recommended connectors for StarkNet - - recommended: [argent(), braavos()], - // Include all injected connectors - includeRecommended: "always", - // Order of connectors - - order: "alphabetical", - }); - return ( - + {children} - + ); diff --git a/contexts/stellar-wallet-context.tsx b/contexts/stellar-wallet-context.tsx new file mode 100644 index 00000000..21b78947 --- /dev/null +++ b/contexts/stellar-wallet-context.tsx @@ -0,0 +1,112 @@ +"use client"; + +import React, { createContext, useContext, useState, useEffect } from "react"; +import { + StellarWalletsKit, + WalletNetwork, + allowAllModules, + FREIGHTER_ID, + xBULL_ID, + ALBEDO_ID, + LOBSTR_ID, + HANA_ID, +} from "@creit.tech/stellar-wallets-kit"; + +interface StellarWalletContextType { + kit: StellarWalletsKit | null; + publicKey: string | null; + isConnected: boolean; + isConnecting: boolean; + error: string | null; + connectWallet: (walletId: string) => Promise; + disconnectWallet: () => void; +} + +const StellarWalletContext = createContext< + StellarWalletContextType | undefined +>(undefined); + +export function StellarWalletProvider({ + children, +}: { + children: React.ReactNode; +}) { + const [kit, setKit] = useState(null); + const [publicKey, setPublicKey] = useState(null); + const [isConnected, setIsConnected] = useState(false); + const [isConnecting, setIsConnecting] = useState(false); + const [error, setError] = useState(null); + + // Initialize Stellar Wallets Kit + useEffect(() => { + const stellarKit = new StellarWalletsKit({ + network: WalletNetwork.TESTNET, // Change to MAINNET for production + selectedWalletId: FREIGHTER_ID, + modules: allowAllModules(), + }); + + setKit(stellarKit); + }, []); + + const connectWallet = async (walletId: string) => { + if (!kit) { + setError("Wallet kit not initialized"); + return; + } + + try { + setIsConnecting(true); + setError(null); + + // Set the selected wallet + kit.setWallet(walletId); + + // Get the public key (this triggers the wallet connection) + const { address } = await kit.getAddress(); + + setPublicKey(address); + setIsConnected(true); + } catch (err) { + console.error("Failed to connect wallet:", err); + setError( + err instanceof Error ? err.message : "Failed to connect wallet" + ); + setIsConnected(false); + setPublicKey(null); + } finally { + setIsConnecting(false); + } + }; + + const disconnectWallet = () => { + setPublicKey(null); + setIsConnected(false); + setError(null); + }; + + return ( + + {children} + + ); +} + +export function useStellarWallet() { + const context = useContext(StellarWalletContext); + if (context === undefined) { + throw new Error( + "useStellarWallet must be used within a StellarWalletProvider" + ); + } + return context; +} diff --git a/public/wallets/albedo.svg b/public/wallets/albedo.svg new file mode 100644 index 00000000..68d6d68b --- /dev/null +++ b/public/wallets/albedo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/wallets/freighter.svg b/public/wallets/freighter.svg new file mode 100644 index 00000000..a9e2b6e6 --- /dev/null +++ b/public/wallets/freighter.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/wallets/hana.svg b/public/wallets/hana.svg new file mode 100644 index 00000000..6f99ad35 --- /dev/null +++ b/public/wallets/hana.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/wallets/lobstr.svg b/public/wallets/lobstr.svg new file mode 100644 index 00000000..66091367 --- /dev/null +++ b/public/wallets/lobstr.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/wallets/xbull.svg b/public/wallets/xbull.svg new file mode 100644 index 00000000..b5ddb534 --- /dev/null +++ b/public/wallets/xbull.svg @@ -0,0 +1,5 @@ + + + + + From 366035ce1f04d514c1a209d98b4f9a6f31b926ec Mon Sep 17 00:00:00 2001 From: Mmeso Love Date: Sun, 22 Feb 2026 13:55:56 +0100 Subject: [PATCH 2/2] feat: migrate ProfileModal from StarkNet to Stellar wallet - Replace useAccount from @starknet-react/core with useStellarWallet hook - Update registration payload to use Stellar publicKey (G-address format) - Update localStorage/sessionStorage to store Stellar public key - Remove all StarkNet dependencies from ProfileModal component This change enables user registration with Stellar wallet addresses instead of StarkNet hex addresses, maintaining backward compatibility with the /api/users/register endpoint. --- components/explore/ProfileModal.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/explore/ProfileModal.tsx b/components/explore/ProfileModal.tsx index 8f1b52d0..1cc5af29 100644 --- a/components/explore/ProfileModal.tsx +++ b/components/explore/ProfileModal.tsx @@ -7,7 +7,7 @@ import { motion } from "framer-motion"; import { ChevronLeft } from "lucide-react"; import { MdClose } from "react-icons/md"; import { useRouter } from "next/navigation"; -import { useAccount } from "@starknet-react/core"; +import { useStellarWallet } from "@/contexts/stellar-wallet-context"; import SimpleLoader from "@/components/ui/loader/simple-loader"; interface ProfileModalProps { @@ -40,7 +40,7 @@ export default function ProfileModal({ // Router and wallet const router = useRouter(); - const { address } = useAccount(); + const { publicKey } = useStellarWallet(); // Verification code state const [verificationCode, setVerificationCode] = useState([ @@ -82,12 +82,12 @@ export default function ProfileModal({ setIsLoading(true); try { - console.log("ProfileModal: Registering user with wallet:", address); + console.log("ProfileModal: Registering user with wallet:", publicKey); const formData = { username: displayName, email: email, - wallet: address, + wallet: publicKey, bio: bio || undefined, }; @@ -109,11 +109,11 @@ export default function ProfileModal({ console.log("ProfileModal: Registration successful"); // Store wallet and username in localStorage for persistence - localStorage.setItem("wallet", address || ""); + localStorage.setItem("wallet", publicKey || ""); localStorage.setItem("username", displayName); // Also store in sessionStorage for redundancy - sessionStorage.setItem("wallet", address || ""); + sessionStorage.setItem("wallet", publicKey || ""); sessionStorage.setItem("username", displayName); // Skip verification for now and go straight to success