diff --git a/package-lock.json b/package-lock.json
index 739270b..a1407a8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -387,7 +387,6 @@
},
"node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7",
@@ -402,7 +401,6 @@
},
"node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -416,7 +414,6 @@
},
"node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -430,7 +427,6 @@
},
"node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7",
@@ -445,7 +441,6 @@
},
"node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7",
@@ -461,7 +456,6 @@
},
"node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7",
@@ -593,7 +587,6 @@
},
"node_modules/@babel/plugin-proposal-private-property-in-object": {
"version": "7.21.0-placeholder-for-preset-env.2",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -686,7 +679,6 @@
},
"node_modules/@babel/plugin-syntax-import-assertions": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -700,7 +692,6 @@
},
"node_modules/@babel/plugin-syntax-import-attributes": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -851,7 +842,6 @@
},
"node_modules/@babel/plugin-syntax-unicode-sets-regex": {
"version": "7.18.6",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
@@ -879,7 +869,6 @@
},
"node_modules/@babel/plugin-transform-async-generator-functions": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7",
@@ -910,7 +899,6 @@
},
"node_modules/@babel/plugin-transform-block-scoped-functions": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -937,7 +925,6 @@
},
"node_modules/@babel/plugin-transform-class-properties": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.29.7",
@@ -952,7 +939,6 @@
},
"node_modules/@babel/plugin-transform-class-static-block": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.29.7",
@@ -1013,7 +999,6 @@
},
"node_modules/@babel/plugin-transform-dotall-regex": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.29.7",
@@ -1028,7 +1013,6 @@
},
"node_modules/@babel/plugin-transform-duplicate-keys": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1042,7 +1026,6 @@
},
"node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.29.7",
@@ -1057,7 +1040,6 @@
},
"node_modules/@babel/plugin-transform-dynamic-import": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1071,7 +1053,6 @@
},
"node_modules/@babel/plugin-transform-explicit-resource-management": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7",
@@ -1086,7 +1067,6 @@
},
"node_modules/@babel/plugin-transform-exponentiation-operator": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1100,7 +1080,6 @@
},
"node_modules/@babel/plugin-transform-export-namespace-from": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1128,7 +1107,6 @@
},
"node_modules/@babel/plugin-transform-for-of": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7",
@@ -1158,7 +1136,6 @@
},
"node_modules/@babel/plugin-transform-json-strings": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1185,7 +1162,6 @@
},
"node_modules/@babel/plugin-transform-logical-assignment-operators": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1199,7 +1175,6 @@
},
"node_modules/@babel/plugin-transform-member-expression-literals": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1213,7 +1188,6 @@
},
"node_modules/@babel/plugin-transform-modules-amd": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.29.7",
@@ -1242,7 +1216,6 @@
},
"node_modules/@babel/plugin-transform-modules-systemjs": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.29.7",
@@ -1259,7 +1232,6 @@
},
"node_modules/@babel/plugin-transform-modules-umd": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.29.7",
@@ -1288,7 +1260,6 @@
},
"node_modules/@babel/plugin-transform-new-target": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1302,7 +1273,6 @@
},
"node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1316,7 +1286,6 @@
},
"node_modules/@babel/plugin-transform-numeric-separator": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1330,7 +1299,6 @@
},
"node_modules/@babel/plugin-transform-object-rest-spread": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.29.7",
@@ -1348,7 +1316,6 @@
},
"node_modules/@babel/plugin-transform-object-super": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7",
@@ -1363,7 +1330,6 @@
},
"node_modules/@babel/plugin-transform-optional-catch-binding": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1377,7 +1343,6 @@
},
"node_modules/@babel/plugin-transform-optional-chaining": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7",
@@ -1434,7 +1399,6 @@
},
"node_modules/@babel/plugin-transform-property-literals": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1504,7 +1468,6 @@
},
"node_modules/@babel/plugin-transform-regenerator": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1518,7 +1481,6 @@
},
"node_modules/@babel/plugin-transform-regexp-modifiers": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.29.7",
@@ -1533,7 +1495,6 @@
},
"node_modules/@babel/plugin-transform-reserved-words": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1616,7 +1577,6 @@
},
"node_modules/@babel/plugin-transform-template-literals": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1630,7 +1590,6 @@
},
"node_modules/@babel/plugin-transform-typeof-symbol": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1661,7 +1620,6 @@
},
"node_modules/@babel/plugin-transform-unicode-escapes": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
@@ -1675,7 +1633,6 @@
},
"node_modules/@babel/plugin-transform-unicode-property-regex": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.29.7",
@@ -1704,7 +1661,6 @@
},
"node_modules/@babel/plugin-transform-unicode-sets-regex": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.29.7",
@@ -1719,7 +1675,6 @@
},
"node_modules/@babel/preset-env": {
"version": "7.29.7",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.29.7",
@@ -1818,7 +1773,6 @@
},
"node_modules/@babel/preset-modules": {
"version": "0.1.6-no-external-plugins",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -3838,12 +3792,12 @@
},
"node_modules/@types/prop-types": {
"version": "15.7.15",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.3.30",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@@ -5484,7 +5438,6 @@
},
"node_modules/babel-plugin-polyfill-corejs3": {
"version": "0.14.2",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.8",
@@ -6298,7 +6251,7 @@
},
"node_modules/csstype": {
"version": "3.2.3",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/data-view-buffer": {
@@ -7181,7 +7134,6 @@
},
"node_modules/esutils": {
"version": "2.0.3",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
diff --git a/src/__tests__/WalletScreen.test.tsx b/src/__tests__/WalletScreen.test.tsx
new file mode 100644
index 0000000..03db9fd
--- /dev/null
+++ b/src/__tests__/WalletScreen.test.tsx
@@ -0,0 +1,183 @@
+/**
+ * WalletScreen.test.tsx
+ *
+ * Tests:
+ * 1. empty state when no wallet is connected
+ * 2. last-known balances render after a successful refresh
+ * 3. a failed refresh surfaces "Failed to refresh balance" and a stale
+ * indicator without replacing the last known balance with zero
+ * 4. Retry (button and pull-to-refresh) re-runs the balance fetch and
+ * clears the error when it succeeds
+ */
+
+import './__mocks__/setup';
+import React from 'react';
+import renderer, { act } from 'react-test-renderer';
+import { RefreshControl, Text, TouchableOpacity } from 'react-native';
+import WalletScreen from '../screens/WalletScreen';
+import { useWalletStore } from '../store/walletStore';
+
+const mockNavigate = jest.fn();
+const mockRefreshBalance = jest.fn();
+const mockDisconnectWallet = jest.fn();
+
+jest.mock('@react-navigation/native', () => ({
+ useNavigation: () => ({ navigate: mockNavigate }),
+}));
+
+jest.mock('../hooks/useStellarWallet', () => ({
+ useStellarWallet: () => ({
+ disconnectWallet: mockDisconnectWallet,
+ refreshBalance: mockRefreshBalance,
+ }),
+}));
+
+jest.mock('../components/TransactionHistory', () => ({
+ __esModule: true,
+ default: () => null,
+}));
+
+jest.mock('../components/PublicKeyDisplay', () => ({
+ __esModule: true,
+ default: () => null,
+}));
+
+function textValues(tree: renderer.ReactTestRenderer): string[] {
+ return tree.root
+ .findAllByType(Text)
+ .flatMap(node =>
+ (Array.isArray(node.props.children)
+ ? node.props.children
+ : [node.props.children]
+ ).filter((child: unknown): child is string => typeof child === 'string'),
+ );
+}
+
+function buttonWithText(
+ tree: renderer.ReactTestRenderer,
+ label: string,
+): renderer.ReactTestInstance {
+ const button = tree.root
+ .findAllByType(TouchableOpacity)
+ .find(node =>
+ node.findAllByType(Text).some(text => text.props.children === label),
+ );
+ if (!button) {
+ throw new Error(`Could not find a button labelled "${label}"`);
+ }
+ return button;
+}
+
+function resetWalletStore(connected: boolean) {
+ useWalletStore.setState({
+ isConnected: connected,
+ status: connected ? 'connected' : 'disconnected',
+ connectError: null,
+ publicKey: connected ? 'GCWALLETTESTKEY' : null,
+ balance: connected ? '42.5' : null,
+ ecoBalance: connected ? '10' : null,
+ usdcBalance: connected ? '5' : null,
+ walletType: connected ? 'inapp' : null,
+ });
+}
+
+async function renderScreen() {
+ const tree = renderer.create();
+ await act(async () => {
+ await Promise.resolve();
+ });
+ return tree;
+}
+
+describe('WalletScreen', () => {
+ let tree: renderer.ReactTestRenderer | null = null;
+
+ beforeEach(() => {
+ mockNavigate.mockClear();
+ mockDisconnectWallet.mockClear();
+ mockRefreshBalance.mockReset();
+ mockRefreshBalance.mockResolvedValue(true);
+ resetWalletStore(true);
+ });
+
+ afterEach(async () => {
+ await act(async () => {
+ tree?.unmount();
+ });
+ tree = null;
+ });
+
+ it('shows the empty state when no wallet is connected', async () => {
+ resetWalletStore(false);
+ tree = await renderScreen();
+
+ const texts = textValues(tree);
+ expect(texts).toContain('No wallet connected');
+ expect(mockRefreshBalance).not.toHaveBeenCalled();
+ });
+
+ it('renders last-known balances after a successful refresh', async () => {
+ tree = await renderScreen();
+
+ expect(mockRefreshBalance).toHaveBeenCalledTimes(1);
+ const texts = textValues(tree);
+ expect(texts).toContain('Wallet');
+ expect(texts).toContain('42.5');
+ expect(texts).toContain('10');
+ expect(texts).toContain('5');
+ expect(texts).not.toContain('Failed to refresh balance');
+ expect(texts).not.toContain('May be out of date');
+ });
+
+ it('shows the error, stale indicator, and last known balance when refresh fails', async () => {
+ mockRefreshBalance.mockResolvedValue(false);
+ tree = await renderScreen();
+
+ const texts = textValues(tree);
+ expect(texts).toContain('Failed to refresh balance');
+ expect(texts).toContain('May be out of date');
+ expect(texts).toContain('42.5');
+ expect(texts).not.toContain('No wallet connected');
+ });
+
+ it('retries the balance refresh and clears the error when it succeeds', async () => {
+ mockRefreshBalance.mockResolvedValueOnce(false);
+ tree = await renderScreen();
+ expect(textValues(tree)).toContain('Failed to refresh balance');
+
+ mockRefreshBalance.mockResolvedValueOnce(true);
+ await act(async () => {
+ buttonWithText(tree!, 'Retry').props.onPress();
+ });
+
+ expect(mockRefreshBalance).toHaveBeenCalledTimes(2);
+ const texts = textValues(tree);
+ expect(texts).not.toContain('Failed to refresh balance');
+ expect(texts).not.toContain('May be out of date');
+ expect(texts).toContain('42.5');
+ });
+
+ it('retries via pull-to-refresh', async () => {
+ mockRefreshBalance.mockResolvedValueOnce(false);
+ tree = await renderScreen();
+
+ mockRefreshBalance.mockResolvedValueOnce(true);
+ const control = tree.root.findByType(RefreshControl);
+ await act(async () => {
+ control.props.onRefresh();
+ });
+
+ expect(mockRefreshBalance).toHaveBeenCalledTimes(2);
+ expect(textValues(tree)).not.toContain('Failed to refresh balance');
+ });
+
+ it('treats a thrown refresh as a failed refresh', async () => {
+ mockRefreshBalance.mockRejectedValue(new Error('Horizon timeout'));
+ tree = await renderScreen();
+
+ const texts = textValues(tree);
+ expect(texts).toContain('Failed to refresh balance');
+ expect(texts).toContain('May be out of date');
+ expect(texts).toContain('42.5');
+ });
+});
diff --git a/src/__tests__/useStellarWallet.test.tsx b/src/__tests__/useStellarWallet.test.tsx
index 137cd3b..9b5085b 100644
--- a/src/__tests__/useStellarWallet.test.tsx
+++ b/src/__tests__/useStellarWallet.test.tsx
@@ -516,10 +516,12 @@ describe('useStellarWallet connect flow', () => {
// A later network blip during refresh must not reject into the
// fire-and-forget call sites nor wipe the verified balance.
getBalance.mockRejectedValue(new Error('offline blip'));
+ let refreshOk: boolean | undefined;
await act(async () => {
- await hook.refreshBalance();
+ refreshOk = await hook.refreshBalance();
});
+ expect(refreshOk).toBe(false);
expect(useWalletStore.getState().balance).toBe('123.45');
expect(useWalletStore.getState().isConnected).toBe(true);
expect(hook.error).toBe('offline blip');
diff --git a/src/hooks/useStellarWallet.ts b/src/hooks/useStellarWallet.ts
index 21e7ba5..5173fb4 100644
--- a/src/hooks/useStellarWallet.ts
+++ b/src/hooks/useStellarWallet.ts
@@ -298,16 +298,26 @@ export function useStellarWallet() {
disconnect();
}, [publicKey, disconnect]);
- const refreshBalance = useCallback(async () => {
- if (publicKey) {
- try {
- const balance = await stellar.getBalance(publicKey);
- setBalance(balance);
- } catch (err) {
- // Keep the last known balance on refresh failure instead of
- // rejecting into fire-and-forget call sites.
- setError(toErrorMessage(err, 'Could not refresh balance'));
- }
+ /**
+ * Refresh the native XLM balance.
+ *
+ * Resolves `true` on success and `false` on network/Horizon failure so
+ * screens can surface an error without this function ever rejecting —
+ * several call sites fire-and-forget (`void refreshBalance()`).
+ * A failed refresh keeps the last known balance in the store.
+ */
+ const refreshBalance = useCallback(async (): Promise => {
+ if (!publicKey) {
+ return true;
+ }
+ try {
+ const balance = await stellar.getBalance(publicKey);
+ setBalance(balance);
+ setError(null);
+ return true;
+ } catch (err) {
+ setError(toErrorMessage(err, 'Could not refresh balance'));
+ return false;
}
}, [publicKey, setBalance]);
diff --git a/src/screens/WalletScreen.tsx b/src/screens/WalletScreen.tsx
index 722eb1e..5bc851d 100644
--- a/src/screens/WalletScreen.tsx
+++ b/src/screens/WalletScreen.tsx
@@ -1,5 +1,11 @@
-import React, { useEffect, useState } from 'react';
-import { View, Text, TouchableOpacity, ScrollView } from 'react-native';
+import React, { useCallback, useEffect, useState } from 'react';
+import {
+ View,
+ Text,
+ TouchableOpacity,
+ ScrollView,
+ RefreshControl,
+} from 'react-native';
import { useWalletStore } from '../store/walletStore';
import { useStellarWallet } from '../hooks/useStellarWallet';
import { colors, spacing } from '../utils/theme';
@@ -9,16 +15,60 @@ import TransactionHistory from '../components/TransactionHistory';
import PublicKeyDisplay from '../components/PublicKeyDisplay';
import { useTabNavigation } from '../navigation/useAppNavigation';
+const REFRESH_CONTROL_COLORS = [colors.primary];
+
export default function WalletScreen() {
const navigation = useTabNavigation();
const { balance, ecoBalance, usdcBalance, publicKey, isConnected } =
useWalletStore();
const { disconnectWallet, refreshBalance } = useStellarWallet();
const [loading, setLoading] = useState(true);
+ const [refreshing, setRefreshing] = useState(false);
+ const [refreshError, setRefreshError] = useState(null);
+ const [isBalanceStale, setIsBalanceStale] = useState(false);
+
+ const loadBalance = useCallback(
+ async (mode: 'initial' | 'retry') => {
+ if (mode === 'retry') {
+ setRefreshing(true);
+ } else {
+ setLoading(true);
+ }
+ try {
+ const ok = await refreshBalance();
+ if (ok === false) {
+ setRefreshError('Failed to refresh balance');
+ setIsBalanceStale(true);
+ } else {
+ setRefreshError(null);
+ setIsBalanceStale(false);
+ }
+ } catch {
+ // refreshBalance is fail-safe and should not throw; still treat a
+ // rejection as a failed refresh so the UI never swallows it.
+ setRefreshError('Failed to refresh balance');
+ setIsBalanceStale(true);
+ } finally {
+ setLoading(false);
+ setRefreshing(false);
+ }
+ },
+ [refreshBalance],
+ );
useEffect(() => {
- void refreshBalance().finally(() => setLoading(false));
- }, [refreshBalance]);
+ if (!isConnected) {
+ setLoading(false);
+ setRefreshError(null);
+ setIsBalanceStale(false);
+ return;
+ }
+ void loadBalance('initial');
+ }, [isConnected, loadBalance]);
+
+ const handleRetry = useCallback(() => {
+ void loadBalance('retry');
+ }, [loadBalance]);
if (!isConnected) {
return (
@@ -62,11 +112,53 @@ export default function WalletScreen() {
backgroundColor: colors.background,
padding: spacing.lg,
}}
+ refreshControl={
+
+ }
>
Wallet
+ {refreshError && (
+
+
+ {refreshError}
+
+
+
+ Retry
+
+
+
+ )}
+
{balance ?? '0'}{' '}
XLM
+ {isBalanceStale && (
+
+ May be out of date
+
+ )}