From 1c0204c17cd30390f03edacd5110546d439b1ad0 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Tue, 20 Jan 2026 12:46:33 -0800 Subject: [PATCH] Add switch_relays to nip 46 signer --- packages/lib/src/Tools.ts | 4 +- packages/signer/src/signers/nip46.ts | 77 ++++++++++++++++++---------- packages/util/src/List.ts | 2 +- 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/packages/lib/src/Tools.ts b/packages/lib/src/Tools.ts index d1a3221..875df8c 100644 --- a/packages/lib/src/Tools.ts +++ b/packages/lib/src/Tools.ts @@ -1363,11 +1363,11 @@ export const displayDomain = (url: string) => displayUrl(first(url.split(/[\/\?] * @param json - JSON string to parse * @returns Parsed object or null if invalid */ -export const parseJson = (json: string | undefined) => { +export function parseJson(json: string | undefined) { if (!json) return undefined try { - return JSON.parse(json) + return JSON.parse(json) as T } catch (e) { return undefined } diff --git a/packages/signer/src/signers/nip46.ts b/packages/signer/src/signers/nip46.ts index 871acb8..5073139 100644 --- a/packages/signer/src/signers/nip46.ts +++ b/packages/signer/src/signers/nip46.ts @@ -7,6 +7,7 @@ import { tryCatch, randomId, MaybeAsync, + parseJson, } from "@welshman/lib" import { HashedEvent, @@ -398,39 +399,43 @@ export class Nip46Broker extends Emitter { waitForNostrconnect = (url: string, signal: AbortSignal) => { const secret = new URL(url).searchParams.get("secret") - return makePromise((resolve, reject) => { - const onReceive = (response: Nip46Response) => { - if (response.result === "auth_url") return + return makePromise( + async (resolve, reject) => { + const onReceive = async (response: Nip46Response) => { + if (response.result === "auth_url") return - if (["ack", secret].includes(response.result!)) { - this.params.signerPubkey = response.event.pubkey + if (["ack", secret].includes(response.result!)) { + this.params.signerPubkey = response.event.pubkey - if (response.result === "ack") { - console.warn( - "Bunker responded to nostrconnect with 'ack', which can lead to session hijacking", - ) + if (response.result === "ack") { + console.warn( + "Bunker responded to nostrconnect with 'ack', which can lead to session hijacking", + ) + } + + this.switchRelays() + + resolve(response as Nip46ResponseWithResult) + } else { + reject(response) } - resolve(response as Nip46ResponseWithResult) - } else { - reject(response) + cleanup() } - cleanup() - } + const cleanup = () => { + this.receiver.off(Nip46Event.Receive, onReceive) + } - const cleanup = () => { - this.receiver.off(Nip46Event.Receive, onReceive) - } + this.receiver.on(Nip46Event.Receive, onReceive) + this.receiver.start() - this.receiver.on(Nip46Event.Receive, onReceive) - this.receiver.start() - - signal.addEventListener("abort", () => { - reject(undefined) - cleanup() - }) - }) + signal.addEventListener("abort", () => { + reject(undefined) + cleanup() + }) + }, + ) } // Methods for serializing a connection @@ -467,7 +472,11 @@ export class Nip46Broker extends Emitter { throw new Error("Attempted to `connect` with no signerPubkey") } - return this.send("connect", [this.params.signerPubkey, connectSecret, perms]) + const result = await this.send("connect", [this.params.signerPubkey, connectSecret, perms]) + + this.switchRelays() + + return result } signEvent = async (event: StampedEvent) => @@ -480,6 +489,22 @@ export class Nip46Broker extends Emitter { nip44Encrypt = (pk: string, message: string) => this.send("nip44_encrypt", [pk, message]) nip44Decrypt = (pk: string, message: string) => this.send("nip44_decrypt", [pk, message]) + + switchRelays = async () => { + const relays = parseJson(await this.send("switch_relays", [])) + + if (relays && relays.length > 0) { + this.params.relays = relays.map(normalizeRelayUrl) + + this.sender.stop() + this.sender = this.makeSender() + + this.receiver.stop() + this.receiver = this.makeReceiver() + } + + return this.params.relays + } } export class Nip46Signer implements ISigner { diff --git a/packages/util/src/List.ts b/packages/util/src/List.ts index 2196d13..a133311 100644 --- a/packages/util/src/List.ts +++ b/packages/util/src/List.ts @@ -38,7 +38,7 @@ const isValidTag = (tag: string[]) => { export const readList = (event: DecryptedEvent): PublishedList => { const getTags = (tags: string[][]) => (Array.isArray(tags) ? tags.filter(isValidTag) : []) - const privateTags = getTags(parseJson(event.plaintext?.content)) + const privateTags = getTags(parseJson(event.plaintext?.content) || []) const publicTags = getTags(event.tags) return {event, kind: event.kind, publicTags, privateTags}