diff --git a/src/app/settings/verify/page.tsx b/src/app/settings/verify/page.tsx
index ed59bd17..3368238a 100644
--- a/src/app/settings/verify/page.tsx
+++ b/src/app/settings/verify/page.tsx
@@ -15,10 +15,22 @@ import {
isValidUrl,
type VerificationRequest,
} from '@/lib/verificationRequests';
+import { useAddressValidation } from '@/hooks/useAddressValidation';
+import { WalletAddress } from '@/components/WalletAddress';
export default function VerifyIdentityPage() {
const [mounted, setMounted] = useState(false);
const [address, setAddress] = useState
(null);
+ const [walletInput, setWalletInput] = useState('');
+ const {
+ input: validatedInput,
+ setInput: setValidatedInput,
+ result: validationResult,
+ loading: validating,
+ error: validationError,
+ validate: validateAddress,
+ reset: resetValidation,
+ } = useAddressValidation();
const [attestation, setAttestation] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
@@ -188,7 +200,7 @@ export default function VerifyIdentityPage() {
Connected Address:
- {address}
+
) : (
@@ -198,6 +210,105 @@ export default function VerifyIdentityPage() {
)}
+ {/* Wallet Address Verification */}
+
+
Verify a Wallet Address
+
+ Enter a Stellar address or a federation address (e.g. name*domain.com)
+ to validate it and resolve it to its Stellar account.
+
+
+
+ setValidatedInput(e.target.value)}
+ placeholder="G... or name*domain.com"
+ aria-label="Wallet address or federation address"
+ className="flex-1 px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-gray-200 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent text-sm font-mono"
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ void validateAddress();
+ }
+ }}
+ />
+
+ {(validationResult || validationError) && (
+
+ )}
+
+
+ {validationError && (
+
+ {validationError}
+
+ )}
+
+ {validationResult && (
+
+
+
+ ✓ Valid address
+
+ {validationResult.federation && (
+
+ Resolved from federation
+
+ )}
+
+
+ {validationResult.federation && (
+
+
+ Federation address:
+
+ {validationResult.federation}
+
+
+
+ )}
+
+
+ Stellar address:
+
+
+
+ {validationResult.memo && (
+
+ Memo:
+
+ {validationResult.memo.type}: {validationResult.memo.value}
+
+
+ )}
+
+ {address && validationResult.address !== address && (
+
+ )}
+
+ )}
+
+
{/* Verification Status */}
Verification Status
diff --git a/src/components/NetworkStatus.tsx b/src/components/NetworkStatus.tsx
index 4d5d2aa1..c48ece85 100644
--- a/src/components/NetworkStatus.tsx
+++ b/src/components/NetworkStatus.tsx
@@ -1,6 +1,8 @@
"use client";
import { useEffect, useState, useRef, useCallback } from "react";
+import { useWalletBalance } from "../hooks/useWalletBalance";
+import { useWalletConnect } from "../hooks/useWalletConnect";
const RPC_URL = process.env.NEXT_PUBLIC_RPC_URL ?? "";
const POLL_MS = 60_000;
@@ -67,6 +69,9 @@ export default function NetworkStatus() {
const [browserOffline, setBrowserOffline] = useState(false);
const timerRef = useRef | null>(null);
+ const { lowBalance, lowBalanceAssets } = useWalletBalance();
+ const { status: wcStatus } = useWalletConnect();
+
const poll = useCallback(async () => {
const s = await checkHealth();
setStatus((prev) => {
@@ -153,6 +158,36 @@ export default function NetworkStatus() {
}`
: "Soroban RPC: checking…";
+ // ── WalletConnect connection indicator styles ───────────────────────────────
+
+ const wcConnected = wcStatus === "connected";
+ const wcPending = wcStatus === "connecting" || wcStatus === "pairing";
+
+ const wcDotColor = wcConnected
+ ? "bg-blue-400"
+ : wcPending
+ ? "bg-blue-400 animate-pulse"
+ : wcStatus === "error" || wcStatus === "disconnected"
+ ? "bg-gray-500"
+ : "bg-gray-400";
+
+ const wcLabel = wcConnected
+ ? "Wallet connected"
+ : wcPending
+ ? "Connecting…"
+ : wcStatus === "error"
+ ? "Wallet error"
+ : "Wallet disconnected";
+
+ const wcTooltip = `WalletConnect: ${wcLabel}`;
+
+ // ── Low balance warning styles ──────────────────────────────────────────────
+
+ const lowBalanceTooltip =
+ lowBalanceAssets && lowBalanceAssets.length > 0
+ ? `Low balance: ${lowBalanceAssets.join(", ")}`
+ : "One or more token balances are low";
+
// ── Network badge styles ─────────────────────────────────────────────────────
const networkBadgeClasses =
@@ -209,8 +244,29 @@ export default function NetworkStatus() {
)}
- {/* ── Network badge + RPC health ── */}
+ {/* ── Network badge + RPC health + WalletConnect + low balance ── */}
+ {/* Low balance warning badge */}
+ {lowBalance && (
+
+
+ ⚠️
+ Low balance
+
+ {/* Tooltip */}
+
+ {lowBalanceTooltip}
+
+
+ )}
+
{/* Permanent Mainnet / Testnet badge */}
+
+ {/* WalletConnect connection dot */}
+
+
+ {wcLabel}
+ {/* Tooltip */}
+
+ {wcTooltip}
+
+
>
);
diff --git a/src/components/PaymentSourceBar.tsx b/src/components/PaymentSourceBar.tsx
index 2baa5cfd..f1c63e47 100644
--- a/src/components/PaymentSourceBar.tsx
+++ b/src/components/PaymentSourceBar.tsx
@@ -3,6 +3,7 @@
import { useState } from "react";
import { formatAmount, truncateAddress } from "@stellar-split/sdk";
import type { Payment } from "@stellar-split/sdk";
+import { formatAssetAmount } from "../lib/balanceFormat";
const COLORS = ["#6366f1", "#22d3ee", "#f59e0b", "#10b981", "#f43f5e", "#a78bfa", "#fb923c"];
@@ -55,7 +56,7 @@ export default function PaymentSourceBar({ payments, total }: Props) {
className="relative group cursor-pointer transition-opacity hover:opacity-80"
onMouseEnter={() => setHoveredPayer(segment.payer)}
onMouseLeave={() => setHoveredPayer(null)}
- title={`${segment.payer}: ${formatAmount(segment.amount)} USDC`}
+ title={`${segment.payer}: ${formatAssetAmount(segment.amount, "USDC")}`}
>
{segment.percentage > 8 && (
@@ -64,7 +65,7 @@ export default function PaymentSourceBar({ payments, total }: Props) {
)}
{hoveredPayer === segment.payer && (
- {truncateAddress(segment.payer)}: {formatAmount(segment.amount)} USDC
+ {truncateAddress(segment.payer)}: {formatAssetAmount(segment.amount, "USDC")}
)}
@@ -73,7 +74,7 @@ export default function PaymentSourceBar({ payments, total }: Props) {