aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/nip17.ts52
-rw-r--r--src/lib/seed.ts41
2 files changed, 93 insertions, 0 deletions
diff --git a/src/lib/nip17.ts b/src/lib/nip17.ts
index 29a14549..1400b5a5 100644
--- a/src/lib/nip17.ts
+++ b/src/lib/nip17.ts
@@ -302,6 +302,58 @@ const createAndPublishMessageEvent = async (
return publishToPreferred(event, eventFor, expiryDays != null);
};
+// Subscribes to new incoming gift wraps for the user.
+// Decrypts new events and passes the rumour to onNewRumour().
+// Returns a close function to stop the subscription.
+// Uses Relay directly instead of SimplePool.subscribeMany to avoid
+// a filter double-nesting bug in nostr-tools v2.23.0.
+export const subscribeToMessages = async (
+ user: any,
+ onNewRumour: (rumour: any) => void,
+): Promise<() => void> => {
+ const preferredRelays = await getPreferredRelays(user.pubkey);
+ const relayUrls = Array.from(new Set([...preferredRelays, ...DM_RELAYS_LIST]));
+ const since = Math.floor(Date.now() / 1000) - TWO_DAYS;
+ const seen = new Set<string>();
+ let eoseCount = 0;
+
+ const closers: (() => void)[] = [];
+
+ for (const url of relayUrls) {
+ try {
+ const relay = await Relay.connect(url);
+ const sub = relay.subscribe(
+ [{ kinds: [1059], "#p": [user.pubkey], since }],
+ {
+ onevent: async (event) => {
+ if (seen.has(event.id)) return;
+ seen.add(event.id);
+ if (eoseCount < relayUrls.length) return;
+ if (expired(event)) return;
+ try {
+ const rumour: any = await decrypt(event, user);
+ const expires = expiration(event);
+ if (expires) rumour.expiration = expires;
+ onNewRumour(rumour);
+ } catch (e) {
+ console.error("Failed to decrypt subscription event:", event.id, e);
+ }
+ },
+ oneose: () => {
+ eoseCount++;
+ },
+ },
+ );
+ closers.push(() => sub.close());
+ } catch (e) {
+ console.error("Failed to connect to relay for subscription:", url, e);
+ eoseCount++;
+ }
+ }
+
+ return () => closers.forEach((close) => close());
+};
+
// Sends a NIP-17 message from `user` to `recipient`.
// If expiryDays is set, messages will expire after that many days.
export const send = async (message: string, user: any, recipient: any, expiryDays?: number) => {
diff --git a/src/lib/seed.ts b/src/lib/seed.ts
new file mode 100644
index 00000000..3d58bcb5
--- /dev/null
+++ b/src/lib/seed.ts
@@ -0,0 +1,41 @@
+import { getWalletEntropy } from "$lib/walletEntropy";
+import { signer } from "$lib/store";
+
+export async function deriveNostrKey(entropy: ArrayBuffer) {
+ const [{ entropyToMnemonic }, { wordlist }, { privateKeyFromSeedWords }, { getPublicKey }] =
+ await Promise.all([
+ import("@scure/bip39"),
+ import("@scure/bip39/wordlists/english.js"),
+ import("nostr-tools/nip06"),
+ import("nostr-tools"),
+ ]);
+
+ const ent = new Uint8Array(entropy).slice(0, 16);
+ const mnemonic = entropyToMnemonic(ent, wordlist);
+ const sk = privateKeyFromSeedWords(mnemonic);
+ const pubkey = getPublicKey(sk);
+ return { sk, pubkey };
+}
+
+export async function autoUnlockNostr(userPubkey: string) {
+ if (localStorage.getItem("nsec")) return;
+
+ const entropy = await getWalletEntropy();
+ if (!entropy) return;
+
+ try {
+ const { sk, pubkey } = await deriveNostrKey(entropy);
+ if (pubkey !== userPubkey) return;
+
+ const { nip19 } = await import("nostr-tools");
+ const { bytesToHex } = await import("@noble/hashes/utils.js");
+ localStorage.setItem("nsec", nip19.nsecEncode(sk));
+ signer.set({
+ method: "nsec",
+ params: { sk: bytesToHex(sk), pk: pubkey },
+ ready: true,
+ });
+ } catch (e) {
+ console.error("Nostr auto-unlock failed:", e);
+ }
+}