Skip to content

Commit a321927

Browse files
committed
allow users to automatically store wallet settings
1 parent 42c2143 commit a321927

File tree

1 file changed

+120
-11
lines changed

1 file changed

+120
-11
lines changed

ndk-mobile/src/providers/session/index.tsx

Lines changed: 120 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,32 @@ import React from 'react';
22
import NDKSessionContext from '../../context/session';
33
import { NDKEventWithFrom } from '../../hooks';
44
import { useNDK } from '../../hooks/ndk';
5-
import { NDKEvent, NDKEventId, NDKFilter, NDKKind, NDKRelay, NDKSubscription, NDKSubscriptionCacheUsage } from '@nostr-dev-kit/ndk';
5+
import NDK, { NDKEvent, NDKEventId, NDKFilter, NDKKind, NDKRelay, NDKSubscription, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk';
66
import { PropsWithChildren, useEffect } from 'react';
77
import { useSessionStore } from '../../stores/session';
88
import { NDKCashuWallet, NDKNutzapMonitor, NDKNWCWallet, NDKWallet, NDKWalletTypes } from '@nostr-dev-kit/ndk-wallet';
99
import { useWalletStore } from '../../stores/wallet';
1010

11+
type SettingsStore = {
12+
get: (key: string) => Promise<string | null>;
13+
set: (key: string, value: string) => Promise<void>;
14+
delete: (key: string) => Promise<void>;
15+
}
16+
17+
/**
18+
* Options for the NDKSessionProvider
19+
*
20+
* @param follows - Whether to subscribe to follow events
21+
* @param muteList - Whether to subscribe to mute list events
22+
* @param wallet - Whether to subscribe to wallet events
23+
* @param settingsStore - A store for storing and retrieving configuration values
24+
* @param kinds - A map of kinds to wrap with a custom wrapper
25+
*/
1126
interface NDKSessionProviderProps {
1227
follows?: boolean;
1328
muteList?: boolean;
1429
wallet?: boolean
15-
walletConfig?: { type: NDKWalletTypes, pairingCode: string };
30+
settingsStore?: SettingsStore;
1631
kinds?: Map<NDKKind, { wrapper?: NDKEventWithFrom<any> }>;
1732
}
1833

@@ -63,7 +78,15 @@ const NDKSessionProvider = ({ children, ...opts }: PropsWithChildren<NDKSessionP
6378
}
6479
};
6580

66-
const setActiveWallet = (wallet: NDKWallet) => {
81+
/**
82+
* Set the active wallet
83+
* @param wallet - The wallet to set
84+
* @param save - Whether to store this setting locally
85+
*/
86+
const setActiveWallet = (
87+
wallet: NDKWallet,
88+
save = true
89+
) => {
6790
ndk.wallet = wallet;
6891
if (wallet instanceof NDKCashuWallet) {
6992
setBalances(wallet.balance());
@@ -104,6 +127,10 @@ const NDKSessionProvider = ({ children, ...opts }: PropsWithChildren<NDKSessionP
104127
else {
105128
setBalances([]);
106129
}
130+
131+
if (save && opts.settingsStore) {
132+
persistWalletConfiguration(wallet, opts.settingsStore);
133+
}
107134
}
108135

109136
useEffect(() => {
@@ -121,21 +148,16 @@ const NDKSessionProvider = ({ children, ...opts }: PropsWithChildren<NDKSessionP
121148
if (opts.wallet) filters[0].kinds!.push(NDKKind.CashuWallet);
122149
if (opts.kinds) filters[0].kinds!.push(...opts.kinds.keys());
123150

124-
if (opts.walletConfig) {
125-
if (opts.walletConfig.type === 'nwc') {
126-
const wallet = new NDKNWCWallet(ndk);
127-
wallet.initWithPairingCode(opts.walletConfig.pairingCode).then(() => {
128-
setActiveWallet(wallet);
129-
});
130-
}
151+
if (opts.settingsStore) {
152+
loadWallet(ndk, opts.settingsStore, (wallet) => setActiveWallet(wallet, false));
131153
}
132154

133155
if (filters[0].kinds!.length > 0) {
134156
sub = ndk.subscribe(filters, { closeOnEose: false }, undefined, false);
135157
sub.on('event', handleEvent);
136158
sub.start();
137159
}
138-
}, [ndk, opts.follows, opts.muteList, opts.walletConfig, currentUser]);
160+
}, [ndk, opts.follows, opts.muteList, opts.settingsStore, currentUser]);
139161

140162
return (
141163
<NDKSessionContext.Provider
@@ -153,4 +175,91 @@ const NDKSessionProvider = ({ children, ...opts }: PropsWithChildren<NDKSessionP
153175
);
154176
};
155177

178+
function walletPayload(wallet: NDKWallet) {
179+
if (wallet instanceof NDKNWCWallet) {
180+
return wallet.pairingCode;
181+
} else if (wallet instanceof NDKCashuWallet) {
182+
return wallet.event.rawEvent();
183+
}
184+
}
185+
186+
/**
187+
* Persist the wallet configuration
188+
* @param wallet - The wallet to persist
189+
* @param settingsStore - The settings store to use
190+
*/
191+
function persistWalletConfiguration(wallet: NDKWallet, settingsStore: SettingsStore) {
192+
if (!wallet) {
193+
settingsStore.delete('wallet');
194+
return;
195+
}
196+
197+
const payload = walletPayload(wallet);
198+
if (!payload) {
199+
alert('Failed to persist wallet configuration!');
200+
return;
201+
}
202+
203+
const type = wallet.type;
204+
settingsStore.set('wallet', JSON.stringify({ type, payload }));
205+
}
206+
207+
async function loadWallet(ndk: NDK, settingsStore: SettingsStore, setActiveWallet: (wallet: NDKWallet | null) => void) {
208+
const walletConfig = await settingsStore.get('wallet');
209+
if (!walletConfig) return;
210+
211+
const loadNWCWallet = (pairingCode: string) => {
212+
const wallet = new NDKNWCWallet(ndk);
213+
wallet.initWithPairingCode(pairingCode).then(() => {
214+
setActiveWallet(wallet);
215+
});
216+
}
217+
218+
const loadNIP60Wallet = async (payload: NostrEvent) => {
219+
try {
220+
// Load the cached event
221+
const event = new NDKEvent(ndk, payload);
222+
const wallet = await NDKCashuWallet.from(event);
223+
setActiveWallet(wallet);
224+
225+
const relaySet = wallet.relaySet;
226+
227+
// Load remotely
228+
const freshEvent = await ndk.fetchEvent(event.encode(), { cacheUsage: NDKSubscriptionCacheUsage.ONLY_RELAY }, relaySet);
229+
if (freshEvent.hasTag('deleted')) {
230+
alert('This wallet has been deleted');
231+
setActiveWallet(null);
232+
return null;
233+
} else if (freshEvent.created_at! > event.created_at!) {
234+
const wallet = await NDKCashuWallet.from(freshEvent);
235+
alert('This wallet has been updated');
236+
setActiveWallet(wallet);
237+
238+
// update the cache
239+
persistWalletConfiguration(wallet, settingsStore);
240+
241+
return wallet;
242+
}
243+
244+
return wallet;
245+
} catch (e) {
246+
console.error('Error activating wallet', e);
247+
}
248+
}
249+
250+
try {
251+
const { type, payload } = JSON.parse(walletConfig);
252+
if (type === 'nwc') {
253+
loadNWCWallet(payload);
254+
} else if (type === 'nip-60') {
255+
loadNIP60Wallet(payload);
256+
} else {
257+
alert('Unknown wallet type: ' + type);
258+
}
259+
} catch (e) {
260+
alert('Failed to load wallet configuration');
261+
settingsStore.delete('wallet');
262+
}
263+
}
264+
156265
export { NDKSessionProvider };

0 commit comments

Comments
 (0)