diff --git a/frontend/app/connect-wallet/page.tsx b/frontend/app/connect-wallet/page.tsx
new file mode 100644
index 00000000..24de7aa6
--- /dev/null
+++ b/frontend/app/connect-wallet/page.tsx
@@ -0,0 +1,30 @@
+"use client";
+
+import React from "react";
+import Link from "next/link";
+import WalletConnectButton from "@/components/WalletConnectButton";
+
+export default function ConnectWalletPage() {
+ return (
+
+
+
Connect Wallet
+
+ Connect your Freighter wallet to submit puzzle answers and claim
+ on-chain NFT rewards.
+
+
+
+
+
+
+
+ Back to puzzles
+
+
+
+ );
+}
diff --git a/frontend/components/WalletConnectButton.jsx b/frontend/components/WalletConnectButton.jsx
new file mode 100644
index 00000000..775d979d
--- /dev/null
+++ b/frontend/components/WalletConnectButton.jsx
@@ -0,0 +1,101 @@
+"use client";
+
+import { useEffect } from "react";
+import { Button } from "@/components/ui/button";
+import LoadingSpinner from "@/components/ui/LoadingSpinner";
+import { useWalletConnection, WALLET_STATUS } from "@/hooks/useWalletConnection";
+
+/**
+ * localStorage key used to persist the last connected Stellar address so the
+ * button can show a stable session across page reloads (#691).
+ */
+const STORAGE_KEY = "stellarhunts.wallet.address";
+
+/**
+ * Shorten a Stellar public key for display, e.g. GABCDE...WXYZ.
+ */
+function shortenAddress(address) {
+ if (!address) return "";
+ return `${address.slice(0, 4)}...${address.slice(-4)}`;
+}
+
+/**
+ * WalletConnectButton
+ * --------------------
+ * Freighter wallet connect button (#691). Wraps `useWalletConnection` to
+ * expose a single control that:
+ * - connects / disconnects via Freighter
+ * - displays the active account address
+ * - surfaces a user rejection or a missing extension
+ * - restores the last connected address across reloads
+ */
+export default function WalletConnectButton({ className }) {
+ const {
+ status,
+ address,
+ error,
+ isExtensionInstalled,
+ connect,
+ disconnect,
+ } = useWalletConnection();
+
+ const isConnecting = status === WALLET_STATUS.CONNECTING;
+ const isConnected = status === WALLET_STATUS.CONNECTED && Boolean(address);
+
+ // Persist the connected address so a reload keeps showing the active
+ // account instead of flashing back to the disconnected state.
+ useEffect(() => {
+ if (typeof window === "undefined") return;
+ if (isConnected && address) {
+ window.localStorage.setItem(STORAGE_KEY, address);
+ } else if (status === WALLET_STATUS.DISCONNECTED) {
+ window.localStorage.removeItem(STORAGE_KEY);
+ }
+ }, [isConnected, address, status]);
+
+ return (
+
+ {isConnected ? (
+
+
+ {shortenAddress(address)}
+
+
+
+ ) : (
+
+ )}
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {!error && !isConnected && status === WALLET_STATUS.DISCONNECTED && !isExtensionInstalled && (
+
+ Freighter extension not detected.
+
+ )}
+
+ );
+}