| null>(null);
+ // Monotonic request id so a slow/aborted fetch can't clobber a newer one
+ // (e.g. when the user taps "retry" while a prior request is still in flight).
+ const latestRequestIdRef = useRef(0);
+
+ // Fetch wallet status and resolve the chip state. Distinguishes the genuine
+ // "not set up" case (a successful wallet_status with no Solana account) from a
+ // transient RPC/transport failure — the latter must NOT masquerade as a locked
+ // wallet for a user who already has one configured.
+ const loadStatus = useCallback(async () => {
+ const requestId = ++latestRequestIdRef.current;
+ try {
+ const status = await fetchWalletStatus();
+ const solana = (status.accounts ?? []).find(a => a.chain === 'solana');
+ if (requestId !== latestRequestIdRef.current) return;
+ if (solana?.address) {
+ setState({ status: 'ready', address: solana.address });
+ } else {
+ // Successful response, but the wallet has no Solana account yet.
+ setState({ status: 'locked' });
+ }
+ } catch (err) {
+ if (requestId !== latestRequestIdRef.current) return;
+ // Core RPC unavailable / timeout / transport error — surface a retryable
+ // error state rather than mislabelling a configured wallet as "not set up".
+ const message = err instanceof Error ? err.message : String(err);
+ console.debug('[walletAddressChip] wallet_status fetch failed:', message);
+ setState({ status: 'error' });
+ }
+ }, []);
+
+ // Fetch wallet status on mount.
+ useEffect(() => {
+ // loadStatus only calls setState after an awaited fetch (never synchronously),
+ // so it does not cause the cascading renders this rule guards against.
+ // eslint-disable-next-line react-hooks/set-state-in-effect
+ void loadStatus();
+ return () => {
+ // Invalidate any in-flight request so its resolution is ignored.
+ latestRequestIdRef.current += 1;
+ };
+ }, [loadStatus]);
+
+ // Cleanup copy-reset timer on unmount.
+ useEffect(
+ () => () => {
+ if (copyResetTimerRef.current !== null) {
+ clearTimeout(copyResetTimerRef.current);
+ copyResetTimerRef.current = null;
+ }
+ },
+ []
+ );
+
+ const handleCopy = useCallback(async (address: string) => {
+ try {
+ await navigator.clipboard.writeText(address);
+ setCopied(true);
+ if (copyResetTimerRef.current !== null) {
+ clearTimeout(copyResetTimerRef.current);
+ }
+ copyResetTimerRef.current = setTimeout(() => {
+ setCopied(false);
+ copyResetTimerRef.current = null;
+ }, 2000);
+ } catch {
+ // Clipboard unavailable in this webview context; silently skip.
+ }
+ }, []);
+
+ // ── Loading skeleton ─────────────────────────────────────────────────────
+ if (state.status === 'loading') {
+ return (
+
+ );
+ }
+
+ // ── Error — transient RPC/transport failure, offer a retry ────────────────
+ if (state.status === 'error') {
+ return (
+
+ );
+ }
+
+ // ── Locked / not configured ───────────────────────────────────────────────
+ if (state.status === 'locked') {
+ return (
+
+
+ {t('agentWorld.walletNotConfigured')}
+
+ );
+ }
+
+ // ── Ready — show truncated address + copy button ──────────────────────────
+ const { address } = state;
+ const truncated = truncateAddress(address);
+
+ return (
+
+
+
+ {truncated}
+
+
+
+ );
+}
diff --git a/app/src/agentworld/pages/AgentWorld.tsx b/app/src/agentworld/pages/AgentWorld.tsx
index 9f51d39a1..66a7a302e 100644
--- a/app/src/agentworld/pages/AgentWorld.tsx
+++ b/app/src/agentworld/pages/AgentWorld.tsx
@@ -14,6 +14,7 @@ import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-
import { SidebarContent } from '../../components/layout/shell/SidebarSlot';
import TwoPaneNav from '../../components/layout/TwoPaneNav';
import { useT } from '../../lib/i18n/I18nContext';
+import WalletAddressChip from '../components/WalletAddressChip';
import BountiesSection from './BountiesSection';
import DirectorySection from './DirectorySection';
import ExploreSection from './ExploreSection';
@@ -131,9 +132,12 @@ export default function AgentWorld() {
},
]}
header={
-
- {t('agentWorld.description')}
-
+
+
+ {t('agentWorld.description')}
+
+
+
}
/>
diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts
index 59043b08c..700516307 100644
--- a/app/src/lib/i18n/ar.ts
+++ b/app/src/lib/i18n/ar.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'الملفات الشخصية',
'agentWorld.marketplace': 'السوق',
'agentWorld.messaging': 'الرسائل',
+ 'agentWorld.walletNotConfigured': 'المحفظة غير مُهيَّأة',
+ 'agentWorld.copyAddress': 'نسخ العنوان',
+ 'agentWorld.addressCopied': 'تم النسخ',
+ 'agentWorld.walletUnavailable': 'المحفظة غير متاحة',
+ 'agentWorld.walletRetry': 'إعادة محاولة تحميل المحفظة',
// Agent World — Explore section live data
'explore.networkOverview': 'نظرة عامة على الشبكة',
'explore.trendingCommunities': 'المجتمعات الرائجة',
diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts
index 0c9e5947d..e4b398c00 100644
--- a/app/src/lib/i18n/bn.ts
+++ b/app/src/lib/i18n/bn.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'প্রোফাইল',
'agentWorld.marketplace': 'মার্কেটপ্লেস',
'agentWorld.messaging': 'বার্তা',
+ 'agentWorld.walletNotConfigured': 'ওয়ালেট সেট আপ হয়নি',
+ 'agentWorld.copyAddress': 'ঠিকানা কপি করুন',
+ 'agentWorld.addressCopied': 'কপি হয়েছে',
+ 'agentWorld.walletUnavailable': 'ওয়ালেট অনুপলব্ধ',
+ 'agentWorld.walletRetry': 'ওয়ালেট পুনরায় লোড করুন',
// Agent World — Explore section live data
'explore.networkOverview': 'নেটওয়ার্ক ওভারভিউ',
'explore.trendingCommunities': 'ট্রেন্ডিং কমিউনিটি',
diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts
index 87eaa99bf..7eb73eb01 100644
--- a/app/src/lib/i18n/de.ts
+++ b/app/src/lib/i18n/de.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'Profile',
'agentWorld.marketplace': 'Marktplatz',
'agentWorld.messaging': 'Nachrichten',
+ 'agentWorld.walletNotConfigured': 'Wallet nicht eingerichtet',
+ 'agentWorld.copyAddress': 'Adresse kopieren',
+ 'agentWorld.addressCopied': 'Kopiert',
+ 'agentWorld.walletUnavailable': 'Wallet nicht verfügbar',
+ 'agentWorld.walletRetry': 'Wallet erneut laden',
// Agent World — Explore section live data
'explore.networkOverview': 'Netzwerkübersicht',
'explore.trendingCommunities': 'Trending-Communities',
diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts
index 92cc8e969..0a32882c0 100644
--- a/app/src/lib/i18n/en.ts
+++ b/app/src/lib/i18n/en.ts
@@ -37,6 +37,11 @@ const en: TranslationMap = {
'agentWorld.profiles': 'Profiles',
'agentWorld.marketplace': 'Marketplace',
'agentWorld.messaging': 'Messages',
+ 'agentWorld.walletNotConfigured': 'Wallet not set up',
+ 'agentWorld.copyAddress': 'Copy address',
+ 'agentWorld.addressCopied': 'Copied',
+ 'agentWorld.walletUnavailable': 'Wallet unavailable',
+ 'agentWorld.walletRetry': 'Retry loading wallet',
// Agent World — Explore section live data
'explore.networkOverview': 'Network Overview',
'explore.trendingCommunities': 'Trending Communities',
diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts
index 075bdb502..85abe6ea9 100644
--- a/app/src/lib/i18n/es.ts
+++ b/app/src/lib/i18n/es.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'Perfiles',
'agentWorld.marketplace': 'Mercado',
'agentWorld.messaging': 'Mensajes',
+ 'agentWorld.walletNotConfigured': 'Cartera no configurada',
+ 'agentWorld.copyAddress': 'Copiar dirección',
+ 'agentWorld.addressCopied': 'Copiada',
+ 'agentWorld.walletUnavailable': 'Cartera no disponible',
+ 'agentWorld.walletRetry': 'Reintentar cargar la cartera',
// Agent World — Explore section live data
'explore.networkOverview': 'Resumen de red',
'explore.trendingCommunities': 'Comunidades en tendencia',
diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts
index ca6555005..998636d0f 100644
--- a/app/src/lib/i18n/fr.ts
+++ b/app/src/lib/i18n/fr.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'Profils',
'agentWorld.marketplace': 'Marché',
'agentWorld.messaging': 'Messages',
+ 'agentWorld.walletNotConfigured': 'Portefeuille non configuré',
+ 'agentWorld.copyAddress': "Copier l'adresse",
+ 'agentWorld.addressCopied': 'Copié',
+ 'agentWorld.walletUnavailable': 'Portefeuille indisponible',
+ 'agentWorld.walletRetry': 'Réessayer de charger le portefeuille',
// Agent World — Explore section live data
'explore.networkOverview': 'Aperçu du réseau',
'explore.trendingCommunities': 'Communautés tendance',
diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts
index 7f4755321..a9e9a40b6 100644
--- a/app/src/lib/i18n/hi.ts
+++ b/app/src/lib/i18n/hi.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'प्रोफ़ाइल',
'agentWorld.marketplace': 'मार्केटप्लेस',
'agentWorld.messaging': 'संदेश',
+ 'agentWorld.walletNotConfigured': 'वॉलेट सेट नहीं है',
+ 'agentWorld.copyAddress': 'पता कॉपी करें',
+ 'agentWorld.addressCopied': 'कॉपी हो गया',
+ 'agentWorld.walletUnavailable': 'वॉलेट उपलब्ध नहीं',
+ 'agentWorld.walletRetry': 'वॉलेट फिर से लोड करें',
// Agent World — Explore section live data
'explore.networkOverview': 'नेटवर्क अवलोकन',
'explore.trendingCommunities': 'ट्रेंडिंग समुदाय',
diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts
index d315195fa..35780dcf0 100644
--- a/app/src/lib/i18n/id.ts
+++ b/app/src/lib/i18n/id.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'Profil',
'agentWorld.marketplace': 'Pasar',
'agentWorld.messaging': 'Pesan',
+ 'agentWorld.walletNotConfigured': 'Dompet belum diatur',
+ 'agentWorld.copyAddress': 'Salin alamat',
+ 'agentWorld.addressCopied': 'Disalin',
+ 'agentWorld.walletUnavailable': 'Dompet tidak tersedia',
+ 'agentWorld.walletRetry': 'Coba muat dompet lagi',
// Agent World — Explore section live data
'explore.networkOverview': 'Ikhtisar Jaringan',
'explore.trendingCommunities': 'Komunitas Trending',
diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts
index 2b62ac97a..cc2a0c55a 100644
--- a/app/src/lib/i18n/it.ts
+++ b/app/src/lib/i18n/it.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'Profili',
'agentWorld.marketplace': 'Mercato',
'agentWorld.messaging': 'Messaggi',
+ 'agentWorld.walletNotConfigured': 'Wallet non configurato',
+ 'agentWorld.copyAddress': 'Copia indirizzo',
+ 'agentWorld.addressCopied': 'Copiato',
+ 'agentWorld.walletUnavailable': 'Wallet non disponibile',
+ 'agentWorld.walletRetry': 'Riprova a caricare il wallet',
// Agent World — Explore section live data
'explore.networkOverview': 'Panoramica della rete',
'explore.trendingCommunities': 'Comunità di tendenza',
diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts
index 1f215343e..063c66ba1 100644
--- a/app/src/lib/i18n/ko.ts
+++ b/app/src/lib/i18n/ko.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': '프로필',
'agentWorld.marketplace': '마켓플레이스',
'agentWorld.messaging': '메시지',
+ 'agentWorld.walletNotConfigured': '지갑이 설정되지 않음',
+ 'agentWorld.copyAddress': '주소 복사',
+ 'agentWorld.addressCopied': '복사됨',
+ 'agentWorld.walletUnavailable': '지갑을 사용할 수 없음',
+ 'agentWorld.walletRetry': '지갑 다시 불러오기',
// Agent World — Explore section live data
'explore.networkOverview': '네트워크 개요',
'explore.trendingCommunities': '트렌딩 커뮤니티',
diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts
index 51b7fcf50..0b870ff97 100644
--- a/app/src/lib/i18n/pl.ts
+++ b/app/src/lib/i18n/pl.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'Profile',
'agentWorld.marketplace': 'Rynek',
'agentWorld.messaging': 'Wiadomości',
+ 'agentWorld.walletNotConfigured': 'Portfel nie jest skonfigurowany',
+ 'agentWorld.copyAddress': 'Kopiuj adres',
+ 'agentWorld.addressCopied': 'Skopiowano',
+ 'agentWorld.walletUnavailable': 'Portfel niedostępny',
+ 'agentWorld.walletRetry': 'Spróbuj ponownie załadować portfel',
// Agent World — Explore section live data
'explore.networkOverview': 'Przegląd sieci',
'explore.trendingCommunities': 'Popularne społeczności',
diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts
index 0662f5885..65e87b0df 100644
--- a/app/src/lib/i18n/pt.ts
+++ b/app/src/lib/i18n/pt.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'Perfis',
'agentWorld.marketplace': 'Mercado',
'agentWorld.messaging': 'Mensagens',
+ 'agentWorld.walletNotConfigured': 'Carteira não configurada',
+ 'agentWorld.copyAddress': 'Copiar endereço',
+ 'agentWorld.addressCopied': 'Copiado',
+ 'agentWorld.walletUnavailable': 'Carteira indisponível',
+ 'agentWorld.walletRetry': 'Tentar carregar a carteira novamente',
// Agent World — Explore section live data
'explore.networkOverview': 'Visão geral da rede',
'explore.trendingCommunities': 'Comunidades em alta',
diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts
index a8fe1b3ac..fcf0fb255 100644
--- a/app/src/lib/i18n/ru.ts
+++ b/app/src/lib/i18n/ru.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': 'Профили',
'agentWorld.marketplace': 'Маркетплейс',
'agentWorld.messaging': 'Сообщения',
+ 'agentWorld.walletNotConfigured': 'Кошелёк не настроен',
+ 'agentWorld.copyAddress': 'Скопировать адрес',
+ 'agentWorld.addressCopied': 'Скопировано',
+ 'agentWorld.walletUnavailable': 'Кошелёк недоступен',
+ 'agentWorld.walletRetry': 'Повторить загрузку кошелька',
// Agent World — Explore section live data
'explore.networkOverview': 'Обзор сети',
'explore.trendingCommunities': 'Трендовые сообщества',
diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts
index 3aaf99af3..6ee3304b3 100644
--- a/app/src/lib/i18n/zh-CN.ts
+++ b/app/src/lib/i18n/zh-CN.ts
@@ -52,6 +52,11 @@ const messages: TranslationMap = {
'agentWorld.profiles': '档案',
'agentWorld.marketplace': '市场',
'agentWorld.messaging': '消息',
+ 'agentWorld.walletNotConfigured': '钱包未设置',
+ 'agentWorld.copyAddress': '复制地址',
+ 'agentWorld.addressCopied': '已复制',
+ 'agentWorld.walletUnavailable': '钱包不可用',
+ 'agentWorld.walletRetry': '重试加载钱包',
// Agent World — Explore section live data
'explore.networkOverview': '网络概览',
'explore.trendingCommunities': '热门社区',