From 15941a2ee8d9c2fb209137872429f38c52a6dadb Mon Sep 17 00:00:00 2001 From: kryputh Date: Fri, 24 Apr 2026 08:50:51 +0100 Subject: [PATCH 01/18] Implement connectWallet logic and sophisticated UI for Stellar integration --- apps/web/app/globals.css | 13 +- apps/web/components/auth/wallet-connect.tsx | 124 ++++++++++++++++++++ apps/web/components/navigation/top-nav.tsx | 83 +++++-------- apps/web/lib/stellar.ts | 47 ++++++-- apps/web/lib/store/use-wallet-store.ts | 40 +++++++ 5 files changed, 245 insertions(+), 62 deletions(-) create mode 100644 apps/web/components/auth/wallet-connect.tsx create mode 100644 apps/web/lib/store/use-wallet-store.ts diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 3ddab396..7628972f 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -15,12 +15,20 @@ --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); + + --radius-xl: 12px; +} + +@layer utilities { + .glass-surface { + @apply border border-white/10 bg-white/5 backdrop-blur-md; + } } @media (prefers-color-scheme: dark) { :root { - --background: #0a0a0a; - --foreground: #ededed; + --background: #09090b; /* zinc-950 */ + --foreground: #fafafa; /* zinc-50 */ } } @@ -28,4 +36,5 @@ body { background: var(--background); color: var(--foreground); font-family: var(--font-geist-sans), sans-serif; + @apply antialiased selection:bg-indigo-500/30; } diff --git a/apps/web/components/auth/wallet-connect.tsx b/apps/web/components/auth/wallet-connect.tsx new file mode 100644 index 00000000..fba7972e --- /dev/null +++ b/apps/web/components/auth/wallet-connect.tsx @@ -0,0 +1,124 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { useWalletStore } from "@/lib/store/use-wallet-store"; +import { connectWallet, getWalletsKit, signAuthMessage } from "@/lib/stellar"; +import { Loader2, Wallet, LogOut, AlertCircle, ChevronRight, User } from "lucide-react"; +import { toast } from "sonner"; +import { cn } from "@/lib/utils"; + +export function WalletConnect() { + const { + publicKey, + isConnected, + isConnecting, + setConnecting, + setConnection, + disconnect, + setError, + error + } = useWalletStore(); + + const [isHydrated, setIsHydrated] = useState(false); + + // Handle hydration to avoid mismatch in Next.js + useEffect(() => { + setIsHydrated(true); + }, []); + + const handleConnect = async () => { + setConnecting(true); + setError(null); + + try { + const address = await connectWallet(); + + // SIWS Flow (Sign-In With Stellar) + // 1. Generate nonce/message (mocked here, usually from backend) + const message = `Sign in to Lance\n\nDomain: lance.so\nAddress: ${address}\nNonce: ${Math.random().toString(36).substring(2)}`; + + // 2. Sign message + await signAuthMessage(message); + + // 3. Check Network + const connectedNetwork = await kit.getNetwork(); + const appNetwork = (process.env.NEXT_PUBLIC_STELLAR_NETWORK as string) ?? "TESTNET"; + + if (connectedNetwork.network.toUpperCase() !== appNetwork.toUpperCase()) { + toast.warning(`Network mismatch: Wallet is on ${connectedNetwork.network}, but app is on ${appNetwork}`); + } + + setConnection(address, walletId); + toast.success("Wallet connected successfully"); + } catch (err: any) { + const errorMessage = err.message || "Failed to connect wallet"; + setError(errorMessage); + toast.error(errorMessage); + } finally { + setConnecting(false); + } + }; + + if (!isHydrated) return null; + + if (isConnected && publicKey) { + return ( +
+
+
+ +
+ + {publicKey.slice(0, 4)}...{publicKey.slice(-4)} + +
+ +
+
+ ); + } + + return ( +
+ + + {error && ( +
+ + {error} +
+ )} +
+ ); +} diff --git a/apps/web/components/navigation/top-nav.tsx b/apps/web/components/navigation/top-nav.tsx index e8d17589..a7ba72b2 100644 --- a/apps/web/components/navigation/top-nav.tsx +++ b/apps/web/components/navigation/top-nav.tsx @@ -8,9 +8,12 @@ import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Input } from "@/components/ui/input"; import { SessionSwitcher } from "@/components/auth/session-switcher"; import { ThemeToggle } from "@/components/theme/theme-toggle"; +import { WalletConnect } from "@/components/auth/wallet-connect"; +import { useWalletStore } from "@/lib/store/use-wallet-store"; export function TopNav({ onOpenSidebar }: { onOpenSidebar?: () => void }) { const { isLoggedIn, logout, login, role, user } = useAuthStore(); + const { isConnected: isWalletConnected } = useWalletStore(); return (
@@ -66,59 +69,37 @@ export function TopNav({ onOpenSidebar }: { onOpenSidebar?: () => void }) {
- {isLoggedIn ? ( -
- -
- - - {user?.name - ?.split(" ") - .map((part) => part[0]) - .join("") - .slice(0, 2) ?? "LN"} - - -
-

{user?.name}

-

{user?.email}

+
+ + + {isLoggedIn ? ( +
+ +
+ + + {user?.name + ?.split(" ") + .map((part) => part[0]) + .join("") + .slice(0, 2) ?? "LN"} + + +
+

{user?.name}

+

{user?.email}

+
+
- -
- ) : ( -
- - -
- )} + ) : null} +
diff --git a/apps/web/lib/stellar.ts b/apps/web/lib/stellar.ts index f476939d..e30445ae 100644 --- a/apps/web/lib/stellar.ts +++ b/apps/web/lib/stellar.ts @@ -1,15 +1,25 @@ -import { StellarWalletsKit, Networks } from "@creit.tech/stellar-wallets-kit"; +import { + StellarWalletsKit, + WalletNetwork, + AllowHttpProvider, + FREIGHTER_ID, + ALBEDO_ID, + XBULL_ID, +} from "@creit.tech/stellar-wallets-kit"; -// TODO: See docs/ISSUES.md — "Wallet Connection" let kit: StellarWalletsKit | null = null; +export const SUPPORTED_WALLETS = [ + FREIGHTER_ID, + ALBEDO_ID, + XBULL_ID, +]; + export function getWalletsKit(): StellarWalletsKit { if (!kit) { kit = new StellarWalletsKit({ - network: - (process.env.NEXT_PUBLIC_STELLAR_NETWORK as Networks) ?? - Networks.TESTNET, - selectedWalletId: "freighter", + network: (process.env.NEXT_PUBLIC_STELLAR_NETWORK as WalletNetwork) ?? WalletNetwork.TESTNET, + allowHttpProviders: true, }); } return kit; @@ -54,10 +64,29 @@ export async function getConnectedWalletAddress(): Promise { export async function signTransaction(xdr: string): Promise { if (process.env.NEXT_PUBLIC_E2E === "true") return xdr; const walletsKit = getWalletsKit(); - const networkPassphrase = - (process.env.NEXT_PUBLIC_STELLAR_NETWORK as Networks) ?? Networks.TESTNET; + const network = (process.env.NEXT_PUBLIC_STELLAR_NETWORK as WalletNetwork) ?? WalletNetwork.TESTNET; const { signedTxXdr } = await walletsKit.signTransaction(xdr, { - networkPassphrase, + network, }); return signedTxXdr; } + +/** + * Signs a SIWS message for backend verification. + */ +export async function signAuthMessage(message: string): Promise { + const walletsKit = getWalletsKit(); + const { signature } = await walletsKit.signAuthMessage(message); + return signature; +} + +export async function disconnectWallet(): Promise { + const walletsKit = getWalletsKit(); + // kit doesn't have a direct disconnect but we can clear state +} + +export async function getNetwork(): Promise { + const walletsKit = getWalletsKit(); + const { network } = await walletsKit.getNetwork(); + return network; +} diff --git a/apps/web/lib/store/use-wallet-store.ts b/apps/web/lib/store/use-wallet-store.ts new file mode 100644 index 00000000..37980239 --- /dev/null +++ b/apps/web/lib/store/use-wallet-store.ts @@ -0,0 +1,40 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +interface WalletState { + publicKey: string | null; + walletId: string | null; + isConnected: boolean; + isConnecting: boolean; + error: string | null; + + setConnection: (publicKey: string, walletId: string) => void; + setConnecting: (isConnecting: boolean) => void; + setError: (error: string | null) => void; + disconnect: () => void; +} + +export const useWalletStore = create()( + persist( + (set) => ({ + publicKey: null, + walletId: null, + isConnected: false, + isConnecting: false, + error: null, + + setConnection: (publicKey, walletId) => + set({ publicKey, walletId, isConnected: true, isConnecting: false, error: null }), + + setConnecting: (isConnecting) => set({ isConnecting }), + + setError: (error) => set({ error, isConnecting: false }), + + disconnect: () => + set({ publicKey: null, walletId: null, isConnected: false, isConnecting: false, error: null }), + }), + { + name: "lance-wallet-storage", + } + ) +); From f37cb9a2d1f9df5a8b509b3c742f701668eae8e2 Mon Sep 17 00:00:00 2001 From: kryputh Date: Fri, 24 Apr 2026 09:07:55 +0100 Subject: [PATCH 02/18] Fix linting errors and undefined variables to resolve CI failures --- apps/web/components/auth/wallet-connect.tsx | 6 ++++-- apps/web/components/navigation/top-nav.tsx | 5 ++--- apps/web/lib/stellar.ts | 4 +--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/web/components/auth/wallet-connect.tsx b/apps/web/components/auth/wallet-connect.tsx index fba7972e..b7709475 100644 --- a/apps/web/components/auth/wallet-connect.tsx +++ b/apps/web/components/auth/wallet-connect.tsx @@ -42,6 +42,7 @@ export function WalletConnect() { await signAuthMessage(message); // 3. Check Network + const kit = getWalletsKit(); const connectedNetwork = await kit.getNetwork(); const appNetwork = (process.env.NEXT_PUBLIC_STELLAR_NETWORK as string) ?? "TESTNET"; @@ -49,10 +50,11 @@ export function WalletConnect() { toast.warning(`Network mismatch: Wallet is on ${connectedNetwork.network}, but app is on ${appNetwork}`); } + const walletId = kit.selectedWalletId || "freighter"; setConnection(address, walletId); toast.success("Wallet connected successfully"); - } catch (err: any) { - const errorMessage = err.message || "Failed to connect wallet"; + } catch (err: unknown) { + const errorMessage = err instanceof Error ? err.message : "Failed to connect wallet"; setError(errorMessage); toast.error(errorMessage); } finally { diff --git a/apps/web/components/navigation/top-nav.tsx b/apps/web/components/navigation/top-nav.tsx index a7ba72b2..e6a6d2df 100644 --- a/apps/web/components/navigation/top-nav.tsx +++ b/apps/web/components/navigation/top-nav.tsx @@ -3,7 +3,7 @@ import Link from "next/link"; import { useAuthStore } from "@/lib/store/use-auth-store"; import { Button } from "@/components/ui/button"; -import { Search, Bell, Menu, LogOut, BriefcaseBusiness } from "lucide-react"; +import { Search, Bell, Menu, LogOut } from "lucide-react"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Input } from "@/components/ui/input"; import { SessionSwitcher } from "@/components/auth/session-switcher"; @@ -12,8 +12,7 @@ import { WalletConnect } from "@/components/auth/wallet-connect"; import { useWalletStore } from "@/lib/store/use-wallet-store"; export function TopNav({ onOpenSidebar }: { onOpenSidebar?: () => void }) { - const { isLoggedIn, logout, login, role, user } = useAuthStore(); - const { isConnected: isWalletConnected } = useWalletStore(); + const { isLoggedIn, logout, role, user } = useAuthStore(); return (
diff --git a/apps/web/lib/stellar.ts b/apps/web/lib/stellar.ts index e30445ae..5565663e 100644 --- a/apps/web/lib/stellar.ts +++ b/apps/web/lib/stellar.ts @@ -1,7 +1,6 @@ import { StellarWalletsKit, WalletNetwork, - AllowHttpProvider, FREIGHTER_ID, ALBEDO_ID, XBULL_ID, @@ -81,8 +80,7 @@ export async function signAuthMessage(message: string): Promise { } export async function disconnectWallet(): Promise { - const walletsKit = getWalletsKit(); - // kit doesn't have a direct disconnect but we can clear state + // Clear any local state if necessary } export async function getNetwork(): Promise { From 4b75bd6541ec72376551b598c429fff65a37ca9e Mon Sep 17 00:00:00 2001 From: kryputh Date: Fri, 24 Apr 2026 22:34:32 +0100 Subject: [PATCH 03/18] fix: resolve build and type errors in wallet integration --- apps/web/components/navigation/top-nav.tsx | 71 +-- apps/web/hooks/use-wallet.ts | 2 +- apps/web/lib/stellar.ts | 257 ++++++++- apps/web/lib/store/use-wallet-store.ts | 59 +- frontend_logs.txt | Bin 0 -> 57026 bytes package-lock.json | 540 ++++++++++++++++++ ...f264fbb4891a59e957758099429c04faa41f5a.zip | Bin 0 -> 2485 bytes ...d636d35ca1ab3ed2b500f51c9075a08d7e49c0.zip | Bin 0 -> 1435 bytes ...ecf9e6aab8f145b3e25afe24cefe4c7bfd7fbb.zip | Bin 0 -> 1440 bytes ...c0d44ed91730327c73205b67cdae20a0a98d16.zip | Bin 0 -> 1448 bytes playwright-report/index.html | 85 +++ .../trace/assets/codeMirrorModule-a5XoALAZ.js | 32 ++ .../assets/defaultSettingsView-CJSZINFr.js | 266 +++++++++ .../trace/codeMirrorModule.DYBRYzYX.css | 1 + playwright-report/trace/codicon.DCmgc-ay.ttf | Bin 0 -> 80340 bytes .../trace/defaultSettingsView.7ch9cixO.css | 1 + playwright-report/trace/index.BDwrLSGN.js | 2 + playwright-report/trace/index.BVu7tZDe.css | 1 + playwright-report/trace/index.html | 43 ++ playwright-report/trace/manifest.webmanifest | 16 + playwright-report/trace/playwright-logo.svg | 9 + playwright-report/trace/snapshot.html | 21 + playwright-report/trace/sw.bundle.js | 5 + playwright-report/trace/uiMode.Btcz36p_.css | 1 + playwright-report/trace/uiMode.CQJ9SCIQ.js | 5 + playwright-report/trace/uiMode.html | 17 + .../trace/xtermModule.DYP7pi_n.css | 32 ++ stellar_main.ts | Bin 0 -> 24778 bytes stellar_main_utf8.ts | 394 +++++++++++++ test-results/.last-run.json | 1 + .../trace.zip | Bin 0 -> 2485 bytes .../trace.zip | Bin 0 -> 1440 bytes .../trace.zip | Bin 0 -> 1448 bytes .../trace.zip | Bin 0 -> 1435 bytes 34 files changed, 1754 insertions(+), 107 deletions(-) create mode 100644 frontend_logs.txt create mode 100644 playwright-report/data/63f264fbb4891a59e957758099429c04faa41f5a.zip create mode 100644 playwright-report/data/c7d636d35ca1ab3ed2b500f51c9075a08d7e49c0.zip create mode 100644 playwright-report/data/ddecf9e6aab8f145b3e25afe24cefe4c7bfd7fbb.zip create mode 100644 playwright-report/data/ebc0d44ed91730327c73205b67cdae20a0a98d16.zip create mode 100644 playwright-report/index.html create mode 100644 playwright-report/trace/assets/codeMirrorModule-a5XoALAZ.js create mode 100644 playwright-report/trace/assets/defaultSettingsView-CJSZINFr.js create mode 100644 playwright-report/trace/codeMirrorModule.DYBRYzYX.css create mode 100644 playwright-report/trace/codicon.DCmgc-ay.ttf create mode 100644 playwright-report/trace/defaultSettingsView.7ch9cixO.css create mode 100644 playwright-report/trace/index.BDwrLSGN.js create mode 100644 playwright-report/trace/index.BVu7tZDe.css create mode 100644 playwright-report/trace/index.html create mode 100644 playwright-report/trace/manifest.webmanifest create mode 100644 playwright-report/trace/playwright-logo.svg create mode 100644 playwright-report/trace/snapshot.html create mode 100644 playwright-report/trace/sw.bundle.js create mode 100644 playwright-report/trace/uiMode.Btcz36p_.css create mode 100644 playwright-report/trace/uiMode.CQJ9SCIQ.js create mode 100644 playwright-report/trace/uiMode.html create mode 100644 playwright-report/trace/xtermModule.DYP7pi_n.css create mode 100644 stellar_main.ts create mode 100644 stellar_main_utf8.ts create mode 100644 test-results/client-dashboard-Client-Da-6f176-metrics-and-active-registry-chromium/trace.zip create mode 100644 test-results/platform-dispute-flow-renders-verdict-page-chromium/trace.zip create mode 100644 test-results/platform-job-board-loads-chromium/trace.zip create mode 100644 test-results/platform-post-a-job-navigates-to-job-board-chromium/trace.zip diff --git a/apps/web/components/navigation/top-nav.tsx b/apps/web/components/navigation/top-nav.tsx index 54b9b056..3f2f91d8 100644 --- a/apps/web/components/navigation/top-nav.tsx +++ b/apps/web/components/navigation/top-nav.tsx @@ -71,82 +71,13 @@ export function TopNav({ onOpenSidebar }: { onOpenSidebar?: () => void }) {
-
- {isConnected && address ? ( - - ) : ( - - )} - {networkMismatch ? ( - - - - ) : null} -
-
- {isConnected && address ? ( -
- - {xlmBalance ? `${Number(xlmBalance).toFixed(2)} XLM` : "XLM --"} - - {shortAddress(address)} - -
- ) : ( - - )} - {networkMismatch ? ( - - - {walletNetwork} vs {appNetwork} - - ) : null} - {error ? ( - - Wallet error - - ) : null} -
{isLoggedIn ? ( +