applesauce-wallet-connect 0.0.0-next-20250930093922 → 0.0.0-next-20251009073624

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,10 +41,10 @@ Create a wallet service that handles NIP-47 requests:
41
41
 
42
42
  ```typescript
43
43
  import { WalletService } from "applesauce-wallet-connect";
44
- import { SimpleSigner } from "applesauce-signers";
44
+ import { PrivateKeySigner } from "applesauce-signers";
45
45
 
46
46
  // Create a signer for the service
47
- const signer = new SimpleSigner();
47
+ const signer = new PrivateKeySigner();
48
48
 
49
49
  // Define method handlers
50
50
  const handlers = {
@@ -1,4 +1,9 @@
1
1
  import { EventBlueprint } from "applesauce-factory";
2
2
  import { WalletSupport } from "../helpers/support.js";
3
- /** Creates a wallet info event */
4
- export declare function WalletSupportBlueprint(info: WalletSupport, client?: string): EventBlueprint;
3
+ /**
4
+ * Creates a wallet info event
5
+ * @param info - The wallet support information
6
+ * @param client - The client pubkey
7
+ * @param overrideRelay - An optional relay to tell the client which relay to use (for nostr+walletauth URI connections)
8
+ */
9
+ export declare function WalletSupportBlueprint(info: WalletSupport, client?: string, overrideRelay?: string): EventBlueprint;
@@ -2,9 +2,14 @@ import { blueprint } from "applesauce-factory";
2
2
  import { setContent } from "applesauce-factory/operations/content";
3
3
  import { includeSingletonTag } from "applesauce-factory/operations";
4
4
  import { WALLET_INFO_KIND } from "../helpers/support.js";
5
- /** Creates a wallet info event */
6
- export function WalletSupportBlueprint(info, client) {
5
+ /**
6
+ * Creates a wallet info event
7
+ * @param info - The wallet support information
8
+ * @param client - The client pubkey
9
+ * @param overrideRelay - An optional relay to tell the client which relay to use (for nostr+walletauth URI connections)
10
+ */
11
+ export function WalletSupportBlueprint(info, client, overrideRelay) {
7
12
  return blueprint(WALLET_INFO_KIND, setContent(info.methods.join(" ")), info.encryption ? includeSingletonTag(["encryption", info.encryption.join(" ")]) : undefined, info.notifications ? includeSingletonTag(["notifications", info.notifications.join(" ")]) : undefined,
8
13
  // An optional client pubkey to notify the service is created (used for nostr+walletauth URI connections)
9
- client ? includeSingletonTag(["p", client]) : undefined);
14
+ client ? includeSingletonTag(overrideRelay ? ["p", client, overrideRelay] : ["p", client]) : undefined);
10
15
  }
@@ -14,6 +14,8 @@ export type WalletConnectOptions = NostrConnectionMethodsOptions & {
14
14
  service?: string;
15
15
  /** Default timeout for RPC requests in milliseconds */
16
16
  timeout?: number;
17
+ /** Whether to accept the relay hint from the wallet service */
18
+ acceptRelayHint?: boolean;
17
19
  };
18
20
  export declare class WalletConnect {
19
21
  /** A fallback method to use for subscriptionMethod if none is passed in when creating the client */
@@ -30,7 +32,10 @@ export declare class WalletConnect {
30
32
  readonly secret: Uint8Array;
31
33
  protected readonly signer: EventSigner;
32
34
  /** The relays to use for the connection */
33
- readonly relays: string[];
35
+ protected relays$: BehaviorSubject<string[]>;
36
+ get relays(): string[];
37
+ /** Whether to accept the relay hint from the wallet service */
38
+ acceptRelayHint: boolean;
34
39
  /** The wallet service public key ( unset if waiting for service ) */
35
40
  service$: BehaviorSubject<string | undefined>;
36
41
  get service(): string | undefined;
@@ -2,7 +2,7 @@ import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
2
2
  import { simpleTimeout } from "applesauce-core";
3
3
  import { create } from "applesauce-factory";
4
4
  import { finalizeEvent, getPublicKey, nip04, nip44, verifyEvent } from "nostr-tools";
5
- import { BehaviorSubject, defer, filter, firstValueFrom, from, fromEvent, identity, ignoreElements, lastValueFrom, map, merge, mergeMap, repeat, ReplaySubject, retry, share, switchMap, takeUntil, tap, timer, toArray, } from "rxjs";
5
+ import { BehaviorSubject, combineLatest, defer, filter, firstValueFrom, from, fromEvent, identity, ignoreElements, lastValueFrom, map, merge, mergeMap, of, repeat, ReplaySubject, retry, share, switchMap, take, takeUntil, tap, timer, toArray, } from "rxjs";
6
6
  import { WalletRequestBlueprint } from "./blueprints/index.js";
7
7
  import { createWalletError } from "./helpers/error.js";
8
8
  import { createWalletAuthURI, getPreferredEncryption, getWalletRequestEncryption, getWalletResponseRequestId, getWalletSupport, isValidWalletNotification, isValidWalletResponse, parseWalletConnectURI, supportsMethod, supportsNotifications, supportsNotificationType, unlockWalletNotification, unlockWalletResponse, WALLET_INFO_KIND, WALLET_LEGACY_NOTIFICATION_KIND, WALLET_NOTIFICATION_KIND, WALLET_RESPONSE_KIND, } from "./helpers/index.js";
@@ -22,7 +22,12 @@ export class WalletConnect {
22
22
  secret;
23
23
  signer;
24
24
  /** The relays to use for the connection */
25
- relays;
25
+ relays$ = new BehaviorSubject([]);
26
+ get relays() {
27
+ return this.relays$.value;
28
+ }
29
+ /** Whether to accept the relay hint from the wallet service */
30
+ acceptRelayHint;
26
31
  /** The wallet service public key ( unset if waiting for service ) */
27
32
  service$ = new BehaviorSubject(undefined);
28
33
  get service() {
@@ -42,7 +47,8 @@ export class WalletConnect {
42
47
  waitForService$;
43
48
  constructor(options) {
44
49
  this.secret = options.secret;
45
- this.relays = options.relays;
50
+ this.relays$.next(options.relays);
51
+ this.acceptRelayHint = options.acceptRelayHint ?? true;
46
52
  this.service$.next(options.service);
47
53
  this.defaultTimeout = options.timeout || 30000; // 30 second default timeout
48
54
  // Create a signer for the factory
@@ -64,18 +70,18 @@ export class WalletConnect {
64
70
  this.subscriptionMethod = (relays, filters) => subscriptionMethod(relays, filters);
65
71
  this.publishMethod = (relays, event) => publishMethod(relays, event);
66
72
  // Create shared observable for all wallet events
67
- this.events$ = this.service$.pipe(switchMap((service) => {
73
+ this.events$ = combineLatest([this.service$, this.relays$]).pipe(switchMap(([service, relays]) => {
68
74
  const client = getPublicKey(this.secret);
69
75
  // If the service is not known yet, subscribe to a wallet info event tagging the client
70
76
  if (!service)
71
- return from(this.subscriptionMethod(this.relays, [{ kinds: [WALLET_INFO_KIND], "#p": [client] }])).pipe(
77
+ return from(this.subscriptionMethod(relays, [{ kinds: [WALLET_INFO_KIND], "#p": [client] }])).pipe(
72
78
  // Keep the connection open indefinitely
73
79
  repeat(),
74
80
  // Retry on connection failure
75
81
  retry(),
76
82
  // Ignore strings (support for applesauce-relay)
77
83
  filter((event) => typeof event !== "string"));
78
- return from(this.subscriptionMethod(this.relays, [
84
+ return from(this.subscriptionMethod(relays, [
79
85
  // Subscribe to response events
80
86
  {
81
87
  kinds: [WALLET_RESPONSE_KIND, WALLET_NOTIFICATION_KIND, WALLET_LEGACY_NOTIFICATION_KIND],
@@ -104,18 +110,29 @@ export class WalletConnect {
104
110
  }));
105
111
  this.encryption$ = this.support$.pipe(map((info) => (info ? getPreferredEncryption(info) : "nip04")));
106
112
  this.notifications$ = this.events$.pipe(filter((event) => isValidWalletNotification(event)), mergeMap((event) => this.handleNotificationEvent(event)));
107
- this.waitForService$ = this.events$.pipe(
108
- // Complete when the service is set
109
- takeUntil(this.service$),
110
- // Only listen for wallet info events
111
- filter((event) => event.kind === WALLET_INFO_KIND && !this.service),
112
- // Set the service to the pubkey of the wallet info event
113
- tap((event) => {
114
- // Set the service to the pubkey of the wallet info event
115
- this.service$.next(event.pubkey);
116
- }),
117
- // Get the service pubkey from the event
118
- map((event) => event.pubkey),
113
+ this.waitForService$ = defer(() =>
114
+ // If service is already set, return it
115
+ this.service$.value
116
+ ? of(this.service$.value)
117
+ : // Otherwise listen for new wallet info events
118
+ this.events$.pipe(
119
+ // Only listen for wallet info events
120
+ filter((event) => event.kind === WALLET_INFO_KIND),
121
+ // Set the service to the pubkey of the wallet info event
122
+ tap((event) => {
123
+ // Set the service to the pubkey of the wallet info event
124
+ this.service$.next(event.pubkey);
125
+ // Switch to the relay from the service if its set
126
+ if (this.acceptRelayHint) {
127
+ const relay = event.tags.find((t) => t[0] === "p" && t[2])?.[2];
128
+ if (relay)
129
+ this.relays$.next([relay]);
130
+ }
131
+ }),
132
+ // Get the service pubkey from the event
133
+ map((event) => event.pubkey),
134
+ // Complete after the first value
135
+ take(1))).pipe(
119
136
  // Only create a single subscription to avoid multiple side effects
120
137
  share());
121
138
  }
@@ -144,7 +161,7 @@ export class WalletConnect {
144
161
  request(request, options = {}) {
145
162
  if (!this.service)
146
163
  throw new Error("WalletConnect is not connected to a service");
147
- // Create the request evnet
164
+ // Create the request event
148
165
  return defer(async () => {
149
166
  // Get the preferred encryption method for the wallet
150
167
  const encryption = await firstValueFrom(this.encryption$);
@@ -115,5 +115,8 @@ export declare class WalletService {
115
115
  /** Send a response event */
116
116
  protected sendResponse(requestEvent: WalletRequestEvent, response: WalletResponse): Promise<void>;
117
117
  /** Creates a service for a nostr+walletauth URI */
118
- static fromAuthURI(uri: string | WalletAuthURI, options: Omit<WalletServiceOptions, "relays">): WalletService;
118
+ static fromAuthURI(uri: string | WalletAuthURI, options: Omit<WalletServiceOptions, "relays"> & {
119
+ /** A relay or method to select a single relay for the client and service to communicate over */
120
+ overrideRelay?: string | ((relays: string[]) => string);
121
+ }): WalletService;
119
122
  }
@@ -162,7 +162,9 @@ export class WalletService {
162
162
  /** Publish the wallet support event */
163
163
  async publishSupportEvent() {
164
164
  try {
165
- const draft = await create({ signer: this.signer }, WalletSupportBlueprint, this.support, this.client);
165
+ // Tell the client which relay to use if there is only one (for nostr+walletauth URI connections)
166
+ const overrideRelay = this.relays.length === 1 ? this.relays[0] : undefined;
167
+ const draft = await create({ signer: this.signer }, WalletSupportBlueprint, this.support, this.client, overrideRelay);
166
168
  const event = await this.signer.signEvent(draft);
167
169
  await this.publishMethod(this.relays, event);
168
170
  }
@@ -284,10 +286,13 @@ export class WalletService {
284
286
  }
285
287
  /** Creates a service for a nostr+walletauth URI */
286
288
  static fromAuthURI(uri, options) {
287
- const { client, relays } = typeof uri === "string" ? parseWalletAuthURI(uri) : uri;
289
+ const authURI = typeof uri === "string" ? parseWalletAuthURI(uri) : uri;
290
+ const relays = options.overrideRelay
291
+ ? [typeof options.overrideRelay === "function" ? options.overrideRelay(authURI.relays) : options.overrideRelay]
292
+ : authURI.relays;
288
293
  return new WalletService({
289
294
  ...options,
290
- client,
295
+ client: authURI.client,
291
296
  relays,
292
297
  });
293
298
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-wallet-connect",
3
- "version": "0.0.0-next-20250930093922",
3
+ "version": "0.0.0-next-20251009073624",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -59,8 +59,8 @@
59
59
  },
60
60
  "dependencies": {
61
61
  "@noble/hashes": "^1.7.1",
62
- "applesauce-core": "0.0.0-next-20250930093922",
63
- "applesauce-factory": "0.0.0-next-20250930093922",
62
+ "applesauce-core": "^4.0.0",
63
+ "applesauce-factory": "^4.0.0",
64
64
  "nostr-tools": "~2.17",
65
65
  "rxjs": "^7.8.1"
66
66
  },