mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-05 02:01:22 +02:00
fix(nwc): keep support$ subscribed to prevent encryption cache expiry
The primary cause of pay_invoice timeouts was the library's support$
observable losing its ReplaySubject(1) cache after 60 seconds with
zero subscribers.
The support$ observable uses share({ connector: () => new ReplaySubject(1),
resetOnRefCountZero: () => timer(60000) }). When no UI component
subscribes to wallet.support$ for 60s (e.g., user closes wallet window
and browses the app), the cached kind 13194 wallet info is discarded.
Since events$ is a hot shared observable that already received the info
event from the relay (past EOSE), a fresh support$ subscription will
never see a new info event. This causes genericCall's
firstValueFrom(encryption$) to hang indefinitely — the defer() block
never completes, so the request event is never created, never published,
and the simpleTimeout on responses$ never even starts.
The fix maintains a persistent subscription to wallet.support$ in the
NWC service module for the lifetime of the wallet connection. This
keeps the ReplaySubject(1) cache warm so encryption$ always resolves
immediately when genericCall needs it.
https://claude.ai/code/session_01LNdzz2qi4hvjCzKBjTK5Gy
This commit is contained in:
@@ -38,6 +38,7 @@ WalletConnect.publishMethod = (relays, event) =>
|
||||
// Internal state
|
||||
let notificationSubscription: Subscription | null = null;
|
||||
let notificationRetryTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
let supportSubscription: Subscription | null = null;
|
||||
|
||||
/**
|
||||
* Connection status for the NWC wallet
|
||||
@@ -75,6 +76,22 @@ export const transactionsState$ = new BehaviorSubject<TransactionsState>(
|
||||
// Internal helpers
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Subscribe to wallet support info to keep the ReplaySubject(1) cache warm.
|
||||
*
|
||||
* The library's support$ uses share({ connector: () => new ReplaySubject(1),
|
||||
* resetOnRefCountZero: () => timer(60000) }). If all subscribers drop for 60s,
|
||||
* the cached wallet info (kind 13194) is lost. Since events$ is hot, the relay
|
||||
* won't re-send the info event, so genericCall's firstValueFrom(encryption$)
|
||||
* hangs indefinitely — causing every wallet operation to time out.
|
||||
*
|
||||
* This persistent subscription prevents the cache from expiring.
|
||||
*/
|
||||
function subscribeToSupport(wallet: WalletConnect) {
|
||||
supportSubscription?.unsubscribe();
|
||||
supportSubscription = wallet.support$.subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to wallet notifications with automatic retry on error.
|
||||
* Notifications trigger balance refresh for real-time updates.
|
||||
@@ -157,6 +174,7 @@ export function createWalletFromURI(connectionString: string): WalletConnect {
|
||||
const wallet = WalletConnect.fromConnectURI(connectionString);
|
||||
wallet$.next(wallet);
|
||||
|
||||
subscribeToSupport(wallet);
|
||||
subscribeToNotifications(wallet);
|
||||
refreshBalance(); // Fetch initial balance
|
||||
|
||||
@@ -208,6 +226,7 @@ export async function restoreWallet(
|
||||
// Continue anyway - notifications will retry
|
||||
}
|
||||
|
||||
subscribeToSupport(wallet);
|
||||
subscribeToNotifications(wallet);
|
||||
refreshBalance();
|
||||
|
||||
@@ -218,7 +237,9 @@ export async function restoreWallet(
|
||||
* Disconnects and clears the wallet.
|
||||
*/
|
||||
export function clearWallet(): void {
|
||||
// Clean up subscription and pending retry
|
||||
// Clean up subscriptions and pending retry
|
||||
supportSubscription?.unsubscribe();
|
||||
supportSubscription = null;
|
||||
notificationSubscription?.unsubscribe();
|
||||
notificationSubscription = null;
|
||||
if (notificationRetryTimeout) {
|
||||
@@ -294,6 +315,7 @@ export async function reconnect(): Promise<void> {
|
||||
connectionStatus$.next("connecting");
|
||||
lastError$.next(null);
|
||||
|
||||
subscribeToSupport(wallet);
|
||||
subscribeToNotifications(wallet);
|
||||
await refreshBalance();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user