= ({
)}
+ {tab === "deposit" && xlmBalance < feeXlm && (
+
+
+
+ Insufficient XLM balance
+ You do not have enough XLM to cover the estimated network fee.
+
+
+ )}
+
{tab === "deposit" && isValidAmount && needsApproval(enteredAmount) && (
= ({
onClick={() => void handleTransaction(tab)}
disabled={
isBusy ||
- (tab === "deposit" && needsApproval(enteredAmount) && approvalStatus !== "confirmed")
+ (tab === "deposit" && needsApproval(enteredAmount) && approvalStatus !== "confirmed") ||
+ (tab === "deposit" && xlmBalance < feeXlm)
}
>
{isBusy ? (
diff --git a/frontend/src/hooks/useBalanceData.ts b/frontend/src/hooks/useBalanceData.ts
index 7c891106..98f5da2f 100644
--- a/frontend/src/hooks/useBalanceData.ts
+++ b/frontend/src/hooks/useBalanceData.ts
@@ -1,5 +1,5 @@
import { useQuery } from "@tanstack/react-query";
-import { fetchUsdcBalance } from "../lib/stellarAccount";
+import { fetchUsdcBalance, fetchXlmBalance } from "../lib/stellarAccount";
import { queryKeys } from "../lib/queryClient";
/**
@@ -24,3 +24,26 @@ export function useUsdcBalance(walletAddress: string | null) {
enabled: !!walletAddress, // Only fetch when wallet is connected
});
}
+
+/**
+ * Hook for fetching native XLM balance with caching.
+ * Stale time: 10s
+ * Only fetches when wallet is connected.
+ */
+export function useXlmBalance(walletAddress: string | null) {
+ return useQuery({
+ queryKey: queryKeys.balance.xlm(walletAddress),
+ queryFn: async () => {
+ if (!walletAddress) {
+ return 0;
+ }
+ try {
+ return await fetchXlmBalance(walletAddress);
+ } catch {
+ return 0;
+ }
+ },
+ staleTime: 10000, // 10 seconds
+ enabled: !!walletAddress, // Only fetch when wallet is connected
+ });
+}
diff --git a/frontend/src/lib/queryClient.ts b/frontend/src/lib/queryClient.ts
index 7db450c7..bbb2faa0 100644
--- a/frontend/src/lib/queryClient.ts
+++ b/frontend/src/lib/queryClient.ts
@@ -54,5 +54,7 @@ export const queryKeys = {
all: ["balance"] as const,
usdc: (walletAddress?: string | null) =>
[...queryKeys.balance.all, "usdc", walletAddress] as const,
+ xlm: (walletAddress?: string | null) =>
+ [...queryKeys.balance.all, "xlm", walletAddress] as const,
},
} as const;
diff --git a/frontend/src/lib/stellarAccount.ts b/frontend/src/lib/stellarAccount.ts
index 96a52e7c..a4441348 100644
--- a/frontend/src/lib/stellarAccount.ts
+++ b/frontend/src/lib/stellarAccount.ts
@@ -69,3 +69,16 @@ export async function fetchUsdcBalance(
return usdc ? Number(usdc.balance) : 0;
}
+
+export async function fetchXlmBalance(
+ walletAddress: string,
+ rpcUrl = import.meta.env.VITE_SOROBAN_RPC_URL || `https://${TESTNET_SOROBAN_RPC}`,
+): Promise {
+ const horizonUrl = toHorizonUrl(rpcUrl);
+ const server = new Horizon.Server(horizonUrl);
+ const account = await server.accounts().accountId(walletAddress).call();
+
+ const native = account.balances.find((balance) => balance.asset_type === "native");
+ return native ? Number(native.balance) : 0;
+}
+
diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx
index eedf3ed3..876b7aea 100644
--- a/frontend/src/pages/Home.tsx
+++ b/frontend/src/pages/Home.tsx
@@ -5,9 +5,10 @@ import { usePageHeadingFocus } from "../hooks/usePageHeadingFocus";
interface HomeProps {
walletAddress: string | null;
usdcBalance: number;
+ xlmBalance: number;
}
-const Home: React.FC = ({ walletAddress, usdcBalance }) => {
+const Home: React.FC = ({ walletAddress, usdcBalance, xlmBalance }) => {
const headingRef = usePageHeadingFocus();
return (
@@ -26,7 +27,7 @@ const Home: React.FC = ({ walletAddress, usdcBalance }) => {
-
+
>
);
};
diff --git a/frontend/src/pages/Portfolio.emptystate.test.tsx b/frontend/src/pages/Portfolio.emptystate.test.tsx
index 535388e5..5efd2843 100644
--- a/frontend/src/pages/Portfolio.emptystate.test.tsx
+++ b/frontend/src/pages/Portfolio.emptystate.test.tsx
@@ -1,4 +1,4 @@
-import { render, screen, waitFor } from "@testing-library/react";
+import { render, screen, waitFor } from "@testing-library/react";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { MemoryRouter } from "react-router-dom";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
diff --git a/frontend/src/tests/VaultDashboardWizard.test.tsx b/frontend/src/tests/VaultDashboardWizard.test.tsx
index 7ef13f53..d4d1c806 100644
--- a/frontend/src/tests/VaultDashboardWizard.test.tsx
+++ b/frontend/src/tests/VaultDashboardWizard.test.tsx
@@ -60,7 +60,7 @@ describe("VaultDashboard Wizard", () => {
it("navigates through the deposit wizard steps", async () => {
render(
-
+
);
diff --git a/frontend/src/tests/routing.test.tsx b/frontend/src/tests/routing.test.tsx
index b6c2bbf1..03600804 100644
--- a/frontend/src/tests/routing.test.tsx
+++ b/frontend/src/tests/routing.test.tsx
@@ -38,7 +38,8 @@ vi.mock('../i18n', () => ({
// Mock hooks to avoid network requests
vi.mock('../hooks/useBalanceData', () => ({
- useUsdcBalance: () => ({ data: 1000, isLoading: false })
+ useUsdcBalance: () => ({ data: 1000, isLoading: false }),
+ useXlmBalance: () => ({ data: 10.0, isLoading: false }),
}));
function renderWithProviders(initialEntries: string[]) {