applesauce-wallet-connect 0.0.0-next-20250808173123 → 0.0.0-next-20250815164532

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.
Files changed (75) hide show
  1. package/README.md +3 -3
  2. package/dist/blueprints/support.d.ts +1 -1
  3. package/dist/blueprints/support.js +4 -2
  4. package/dist/helpers/auth-uri.d.ts +44 -0
  5. package/dist/helpers/auth-uri.js +113 -0
  6. package/dist/helpers/connect-uri.d.ts +5 -0
  7. package/dist/helpers/connect-uri.js +6 -2
  8. package/dist/helpers/index.d.ts +1 -0
  9. package/dist/helpers/index.js +1 -0
  10. package/dist/types.d.ts +5 -0
  11. package/dist/wallet-connect.d.ts +28 -11
  12. package/dist/wallet-connect.js +90 -30
  13. package/dist/wallet-service.d.ts +20 -5
  14. package/dist/wallet-service.js +35 -12
  15. package/package.json +3 -3
  16. package/dist/actions/index.d.ts +0 -1
  17. package/dist/actions/index.js +0 -1
  18. package/dist/actions/tokens.d.ts +0 -17
  19. package/dist/actions/tokens.js +0 -110
  20. package/dist/actions/wallet.d.ts +0 -13
  21. package/dist/actions/wallet.js +0 -64
  22. package/dist/actions/zap-info.d.ts +0 -22
  23. package/dist/actions/zap-info.js +0 -83
  24. package/dist/actions/zaps.d.ts +0 -8
  25. package/dist/actions/zaps.js +0 -30
  26. package/dist/blueprints/history.d.ts +0 -4
  27. package/dist/blueprints/history.js +0 -11
  28. package/dist/blueprints/info.d.ts +0 -4
  29. package/dist/blueprints/info.js +0 -8
  30. package/dist/blueprints/tokens.d.ts +0 -7
  31. package/dist/blueprints/tokens.js +0 -11
  32. package/dist/blueprints/wallet.d.ts +0 -5
  33. package/dist/blueprints/wallet.js +0 -11
  34. package/dist/blueprints/zaps.d.ts +0 -8
  35. package/dist/blueprints/zaps.js +0 -12
  36. package/dist/helpers/animated-qr.d.ts +0 -30
  37. package/dist/helpers/animated-qr.js +0 -71
  38. package/dist/helpers/history.d.ts +0 -26
  39. package/dist/helpers/history.js +0 -47
  40. package/dist/helpers/info.d.ts +0 -34
  41. package/dist/helpers/info.js +0 -97
  42. package/dist/helpers/methods.d.ts +0 -1
  43. package/dist/helpers/methods.js +0 -1
  44. package/dist/helpers/nutzap.d.ts +0 -27
  45. package/dist/helpers/nutzap.js +0 -66
  46. package/dist/helpers/tokens.d.ts +0 -58
  47. package/dist/helpers/tokens.js +0 -162
  48. package/dist/helpers/wallet.d.ts +0 -15
  49. package/dist/helpers/wallet.js +0 -41
  50. package/dist/helpers/zap-info.d.ts +0 -19
  51. package/dist/helpers/zap-info.js +0 -42
  52. package/dist/interface.d.ts +0 -6
  53. package/dist/interface.js +0 -1
  54. package/dist/models/history.d.ts +0 -6
  55. package/dist/models/history.js +0 -21
  56. package/dist/models/index.d.ts +0 -4
  57. package/dist/models/index.js +0 -4
  58. package/dist/models/nutzap.d.ts +0 -6
  59. package/dist/models/nutzap.js +0 -16
  60. package/dist/models/tokens.d.ts +0 -6
  61. package/dist/models/tokens.js +0 -58
  62. package/dist/models/wallet.d.ts +0 -13
  63. package/dist/models/wallet.js +0 -18
  64. package/dist/operations/history.d.ts +0 -7
  65. package/dist/operations/history.js +0 -34
  66. package/dist/operations/index.d.ts +0 -5
  67. package/dist/operations/index.js +0 -5
  68. package/dist/operations/nutzap.d.ts +0 -14
  69. package/dist/operations/nutzap.js +0 -33
  70. package/dist/operations/tokens.d.ts +0 -4
  71. package/dist/operations/tokens.js +0 -24
  72. package/dist/operations/wallet.d.ts +0 -8
  73. package/dist/operations/wallet.js +0 -30
  74. package/dist/operations/zap-info.d.ts +0 -10
  75. package/dist/operations/zap-info.js +0 -17
package/README.md CHANGED
@@ -24,7 +24,7 @@ Connect to a wallet service using a connection string:
24
24
  ```typescript
25
25
  import { WalletConnect } from "applesauce-wallet-connect";
26
26
 
27
- const wallet = WalletConnect.fromConnectionString("nostr+walletconnect://relay.example.com?secret=...&pubkey=...");
27
+ const wallet = WalletConnect.fromConnectURI("nostr+walletconnect://relay.example.com?secret=...&pubkey=...");
28
28
 
29
29
  // Pay an invoice
30
30
  const result = await wallet.payInvoice("lnbc1...");
@@ -87,10 +87,10 @@ await service.start();
87
87
  console.log("Wallet service started");
88
88
 
89
89
  // Get the connection string for the wallet service
90
- console.log(service.getConnectionString());
90
+ console.log(service.getConnectURI());
91
91
 
92
92
  // Stop the service when done
93
- // service.stop();
93
+ service.stop();
94
94
  ```
95
95
 
96
96
  ## Supported Methods
@@ -1,4 +1,4 @@
1
1
  import { EventBlueprint } from "applesauce-factory";
2
2
  import { WalletSupport } from "../helpers/support.js";
3
3
  /** Creates a wallet info event */
4
- export declare function WalletSupportBlueprint(info: WalletSupport): EventBlueprint;
4
+ export declare function WalletSupportBlueprint(info: WalletSupport, client?: string): EventBlueprint;
@@ -3,6 +3,8 @@ 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
5
  /** Creates a wallet info event */
6
- export function WalletSupportBlueprint(info) {
7
- 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);
6
+ export function WalletSupportBlueprint(info, client) {
7
+ 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
+ // An optional client pubkey to notify the service is created (used for nostr+walletauth URI connections)
9
+ client ? includeSingletonTag(["p", client]) : undefined);
8
10
  }
@@ -0,0 +1,44 @@
1
+ import { NotificationType } from "./notification.js";
2
+ import { WalletMethod } from "./support.js";
3
+ export interface WalletAuthURI {
4
+ /** The public key of the client requesting authorization */
5
+ client: string;
6
+ /** Required. URL of the relay where the client intends to communicate with the wallet service */
7
+ relays: string[];
8
+ /** The name of the client app (optional) */
9
+ name?: string;
10
+ /** The URL of an icon of the client app to display on the confirmation page (optional) */
11
+ icon?: string;
12
+ /** URI to open after the connection is created (optional) */
13
+ returnTo?: string;
14
+ /** The connection cannot be used after this date. Unix timestamp in seconds (optional) */
15
+ expiresAt?: number;
16
+ /** The maximum amount in millisats that can be sent per renewal period (optional) */
17
+ maxAmount?: number;
18
+ /** The reset the budget at the end of the given budget renewal. Can be never (default), daily, weekly, monthly, yearly (optional) */
19
+ budgetRenewal?: "never" | "daily" | "weekly" | "monthly" | "yearly";
20
+ /** List of request types that you need permission for (optional) */
21
+ methods?: WalletMethod[];
22
+ /** List of notification types that you need permission for (optional) */
23
+ notifications?: NotificationType[];
24
+ /** The makes an isolated app connection / sub-wallet with its own balance and only access to its own transaction list (optional) */
25
+ isolated?: boolean;
26
+ /** Url encoded, JSON-serialized metadata that describes the app connection (optional) */
27
+ metadata?: Record<string, any>;
28
+ /** The wallet name for nostr+walletauth+walletname scheme (optional) */
29
+ walletName?: string;
30
+ }
31
+ /**
32
+ * Parses a nostr+walletauth URI
33
+ * @throws {Error} if the authorization URI is invalid
34
+ */
35
+ export declare function parseWalletAuthURI(authURI: string): WalletAuthURI;
36
+ /**
37
+ * Creates a nostr+walletauth URI from a WalletAuthURI object
38
+ */
39
+ export declare function createWalletAuthURI(parts: WalletAuthURI): string;
40
+ /**
41
+ * Validates a WalletAuthURI object
42
+ * @returns true if valid, throws Error if invalid
43
+ */
44
+ export declare function validateWalletAuthURI(parts: WalletAuthURI): boolean;
@@ -0,0 +1,113 @@
1
+ import { mergeRelaySets } from "applesauce-core/helpers";
2
+ /**
3
+ * Parses a nostr+walletauth URI
4
+ * @throws {Error} if the authorization URI is invalid
5
+ */
6
+ export function parseWalletAuthURI(authURI) {
7
+ const { host, pathname, searchParams, protocol } = new URL(authURI);
8
+ // Check if it's a valid wallet auth protocol
9
+ if (!protocol.startsWith("nostr+walletauth")) {
10
+ throw new Error("invalid wallet auth uri protocol");
11
+ }
12
+ // Extract wallet name if present (nostr+walletauth+walletname://)
13
+ const walletName = protocol.includes("+") && protocol.split("+").length > 2 ? protocol.split("+")[2]?.replace(/:$/, "") : undefined;
14
+ // The client pubkey is in the pathname or host
15
+ const client = pathname || host;
16
+ if (!client)
17
+ throw new Error("missing client public key in authorization URI");
18
+ // Relay is required
19
+ const relays = mergeRelaySets(searchParams.getAll("relay"));
20
+ if (relays.length === 0)
21
+ throw new Error("missing required relay parameter in authorization URI");
22
+ // Parse optional parameters
23
+ const name = searchParams.get("name") ?? undefined;
24
+ const icon = searchParams.get("icon") ?? undefined;
25
+ const returnTo = searchParams.get("return_to") ?? undefined;
26
+ const expiresAtParam = searchParams.get("expires_at");
27
+ const expiresAt = expiresAtParam ? parseInt(expiresAtParam, 10) : undefined;
28
+ const maxAmountParam = searchParams.get("max_amount");
29
+ const maxAmount = maxAmountParam ? parseInt(maxAmountParam, 10) : undefined;
30
+ const budgetRenewal = searchParams.get("budget_renewal");
31
+ const methodsParam = searchParams.get("request_methods");
32
+ const methods = methodsParam ? methodsParam.split(" ") : undefined;
33
+ const notificationsParam = searchParams.get("notification_types");
34
+ const notifications = notificationsParam ? notificationsParam.split(" ") : undefined;
35
+ const isolatedParam = searchParams.get("isolated");
36
+ const isolated = isolatedParam ? isolatedParam === "true" : undefined;
37
+ const metadataParam = searchParams.get("metadata");
38
+ let metadata;
39
+ if (metadataParam) {
40
+ try {
41
+ metadata = JSON.parse(decodeURIComponent(metadataParam));
42
+ }
43
+ catch (error) {
44
+ throw new Error("invalid metadata parameter in authorization URI");
45
+ }
46
+ }
47
+ return {
48
+ client,
49
+ relays,
50
+ name,
51
+ icon,
52
+ returnTo,
53
+ expiresAt,
54
+ maxAmount,
55
+ budgetRenewal: budgetRenewal || undefined,
56
+ methods,
57
+ notifications,
58
+ isolated,
59
+ metadata,
60
+ walletName,
61
+ };
62
+ }
63
+ /**
64
+ * Creates a nostr+walletauth URI from a WalletAuthURI object
65
+ */
66
+ export function createWalletAuthURI(parts) {
67
+ validateWalletAuthURI(parts);
68
+ // Determine the protocol based on whether wallet name is specified
69
+ const protocol = parts.walletName ? `nostr+walletauth+${parts.walletName}` : "nostr+walletauth";
70
+ const url = new URL(`${protocol}://${parts.client}`);
71
+ // Add required relay parameter
72
+ for (const relay of parts.relays)
73
+ url.searchParams.append("relay", relay);
74
+ // Add optional parameters
75
+ if (parts.name)
76
+ url.searchParams.append("name", parts.name);
77
+ if (parts.icon)
78
+ url.searchParams.append("icon", parts.icon);
79
+ if (parts.returnTo)
80
+ url.searchParams.append("return_to", parts.returnTo);
81
+ if (parts.expiresAt)
82
+ url.searchParams.append("expires_at", parts.expiresAt.toString());
83
+ if (parts.maxAmount)
84
+ url.searchParams.append("max_amount", parts.maxAmount.toString());
85
+ if (parts.budgetRenewal && parts.budgetRenewal !== "never")
86
+ url.searchParams.append("budget_renewal", parts.budgetRenewal);
87
+ if (parts.methods && parts.methods.length > 0)
88
+ url.searchParams.append("request_methods", parts.methods.join(" "));
89
+ if (parts.notifications && parts.notifications.length > 0)
90
+ url.searchParams.append("notification_types", parts.notifications.join(" "));
91
+ if (parts.isolated !== undefined)
92
+ url.searchParams.append("isolated", parts.isolated.toString());
93
+ if (parts.metadata)
94
+ url.searchParams.append("metadata", encodeURIComponent(JSON.stringify(parts.metadata)));
95
+ return url.toString();
96
+ }
97
+ /**
98
+ * Validates a WalletAuthURI object
99
+ * @returns true if valid, throws Error if invalid
100
+ */
101
+ export function validateWalletAuthURI(parts) {
102
+ if (!parts.client || parts.client.length === 0)
103
+ throw new Error("client public key is required");
104
+ if (!parts.relays || parts.relays.length === 0)
105
+ throw new Error("at least one relay is required");
106
+ if (parts.expiresAt && parts.expiresAt <= Math.floor(Date.now() / 1000))
107
+ throw new Error("expires_at must be in the future");
108
+ if (parts.maxAmount && parts.maxAmount <= 0)
109
+ throw new Error("max_amount must be positive");
110
+ if (parts.budgetRenewal && !["never", "daily", "weekly", "monthly", "yearly"].includes(parts.budgetRenewal))
111
+ throw new Error("invalid budget_renewal value");
112
+ return true;
113
+ }
@@ -1,7 +1,12 @@
1
1
  export interface WalletConnectURI {
2
+ /** The pubkey of the wallet service */
2
3
  service: string;
4
+ /** The relays to use for the connection */
3
5
  relays: string[];
6
+ /** The secret key that the client will use to encrypt messages */
4
7
  secret: string;
8
+ /** An optional lub16 lightning address that is associated with the wallet */
9
+ lud16?: string;
5
10
  }
6
11
  /**
7
12
  * Parses a nostr+walletconnect URI
@@ -1,3 +1,4 @@
1
+ import { mergeRelaySets } from "applesauce-core/helpers";
1
2
  /**
2
3
  * Parses a nostr+walletconnect URI
3
4
  * @throws {Error} if the connection string is invalid
@@ -7,11 +8,12 @@ export function parseWalletConnectURI(connectionString) {
7
8
  if (protocol !== "nostr+walletconnect:")
8
9
  throw new Error("invalid wallet connect uri protocol");
9
10
  const service = pathname || host;
10
- const relays = searchParams.getAll("relay");
11
+ const relays = mergeRelaySets(searchParams.getAll("relay"));
11
12
  const secret = searchParams.get("secret");
13
+ const lud16 = searchParams.get("lud16") ?? undefined;
12
14
  if (!service || relays.length === 0 || !secret)
13
15
  throw new Error("invalid connection string");
14
- return { service, relays, secret };
16
+ return { service, relays, secret, lud16 };
15
17
  }
16
18
  /** Creates a nostr+walletconnect URI from a WalletConnectURI object */
17
19
  export function createWalletConnectURI(parts) {
@@ -19,5 +21,7 @@ export function createWalletConnectURI(parts) {
19
21
  for (const relay of parts.relays)
20
22
  url.searchParams.append("relay", relay);
21
23
  url.searchParams.append("secret", parts.secret);
24
+ if (parts.lud16)
25
+ url.searchParams.append("lud16", parts.lud16);
22
26
  return url.toString();
23
27
  }
@@ -5,3 +5,4 @@ export * from "./response.js";
5
5
  export * from "./support.js";
6
6
  export * from "./notification.js";
7
7
  export * from "./encryption.js";
8
+ export * from "./auth-uri.js";
@@ -5,3 +5,4 @@ export * from "./response.js";
5
5
  export * from "./support.js";
6
6
  export * from "./notification.js";
7
7
  export * from "./encryption.js";
8
+ export * from "./auth-uri.js";
package/dist/types.d.ts CHANGED
@@ -4,3 +4,8 @@ import { Observable } from "rxjs";
4
4
  export type NostrSubscriptionMethod = (relays: string[], filters: Filter[]) => Observable<NostrEvent | string>;
5
5
  /** A method used for publishing an event, can return a Promise that completes when published or an Observable that completes when published*/
6
6
  export type NostrPublishMethod = (relays: string[], event: NostrEvent) => Promise<any> | Observable<any>;
7
+ /** A simple pool type that combines the subscription and publish methods */
8
+ export type NostrPool = {
9
+ subscription: NostrSubscriptionMethod;
10
+ publish: NostrPublishMethod;
11
+ };
@@ -1,23 +1,33 @@
1
+ import { EncryptionMethod } from "applesauce-core/helpers";
1
2
  import { EventSigner } from "applesauce-factory";
2
3
  import { NostrEvent } from "nostr-tools";
3
- import { Observable, Subscription } from "rxjs";
4
- import { EncryptionMethod } from "applesauce-core/helpers";
5
- import { GetBalanceResult, GetInfoResult, ListTransactionsResult, LookupInvoiceResult, MakeInvoiceParams, MakeInvoiceResult, NotificationType, PayInvoiceResult, PayKeysendResult, WalletConnectEncryptionMethod, WalletConnectURI, WalletMethod, WalletNotification, WalletRequest, WalletResponse, WalletSupport } from "./helpers/index.js";
6
- import { NostrPublishMethod, NostrSubscriptionMethod } from "./types.js";
4
+ import { BehaviorSubject, Observable, Subscription } from "rxjs";
5
+ import { GetBalanceResult, GetInfoResult, ListTransactionsResult, LookupInvoiceResult, MakeInvoiceParams, MakeInvoiceResult, NotificationType, PayInvoiceResult, PayKeysendResult, WalletAuthURI, WalletConnectEncryptionMethod, WalletConnectURI, WalletMethod, WalletNotification, WalletRequest, WalletResponse, WalletSupport } from "./helpers/index.js";
6
+ import { NostrPool, NostrPublishMethod, NostrSubscriptionMethod } from "./types.js";
7
7
  export type SerializedWalletConnect = WalletConnectURI;
8
8
  export type WalletConnectOptions = {
9
+ /** The secret to use for the connection */
10
+ secret: Uint8Array;
11
+ /** The relays to use for the connection */
12
+ relays: string[];
13
+ /** The service pubkey to use for the connection (optional) */
14
+ service?: string;
15
+ /** Default timeout for RPC requests in milliseconds */
16
+ timeout?: number;
9
17
  /** A method for subscribing to relays */
10
18
  subscriptionMethod?: NostrSubscriptionMethod;
11
19
  /** A method for publishing events */
12
20
  publishMethod?: NostrPublishMethod;
13
- /** Default timeout for RPC requests in milliseconds */
14
- timeout?: number;
21
+ /** An optional pool for connection methods */
22
+ pool?: NostrPool;
15
23
  };
16
24
  export declare class WalletConnect {
17
25
  /** A fallback method to use for subscriptionMethod if none is passed in when creating the client */
18
26
  static subscriptionMethod: NostrSubscriptionMethod | undefined;
19
27
  /** A fallback method to use for publishMethod if none is passed in when creating the client */
20
28
  static publishMethod: NostrPublishMethod | undefined;
29
+ /** A fallback pool to use if none is pass in when creating the signer */
30
+ static pool: NostrPool | undefined;
21
31
  /** A method that is called when an event needs to be published */
22
32
  protected publishMethod: NostrPublishMethod;
23
33
  /** The active nostr subscription method */
@@ -27,8 +37,9 @@ export declare class WalletConnect {
27
37
  protected readonly signer: EventSigner;
28
38
  /** The relays to use for the connection */
29
39
  readonly relays: string[];
30
- /** The wallet service public key */
31
- readonly service: string;
40
+ /** The wallet service public key ( unset if waiting for service ) */
41
+ service$: BehaviorSubject<string | undefined>;
42
+ get service(): string | undefined;
32
43
  /** Default timeout for requests */
33
44
  defaultTimeout: number;
34
45
  /** Observable for wallet info updates */
@@ -39,7 +50,9 @@ export declare class WalletConnect {
39
50
  protected events$: Observable<NostrEvent>;
40
51
  /** Shared observable for all wallet notifications */
41
52
  notifications$: Observable<WalletNotification>;
42
- constructor(secret: Uint8Array, service: string, relays: string[], options?: WalletConnectOptions);
53
+ /** An internal observable for listening for the wallet service to connect */
54
+ protected waitForService$: Observable<string>;
55
+ constructor(options: WalletConnectOptions);
43
56
  /** Process response events and return WalletResponse or throw error */
44
57
  protected handleResponseEvent(event: NostrEvent, encryption?: EncryptionMethod): Promise<WalletResponse>;
45
58
  /** Handle notification events */
@@ -53,6 +66,10 @@ export declare class WalletConnect {
53
66
  * @returns a method to unsubscribe the listener
54
67
  */
55
68
  notification<T extends WalletNotification>(type: T["notification_type"], listener: (notification: T["notification"]) => any): Subscription;
69
+ /** Gets the nostr+walletauth URI for the connection */
70
+ getAuthURI(parts?: Omit<WalletAuthURI, "client" | "relays">): string;
71
+ /** Wait for the wallet service to connect */
72
+ waitForService(abortSignal?: AbortSignal): Promise<string>;
56
73
  /** Get the wallet support info */
57
74
  getSupport(): Promise<WalletSupport | null>;
58
75
  /** Check if the wallet supports a method */
@@ -105,7 +122,7 @@ export declare class WalletConnect {
105
122
  /** Serialize the WalletConnect instance */
106
123
  toJSON(): SerializedWalletConnect;
107
124
  /** Create a new WalletConnect instance from a serialized object */
108
- static fromJSON(json: SerializedWalletConnect, options?: WalletConnectOptions): WalletConnect;
125
+ static fromJSON(json: SerializedWalletConnect, options?: Omit<WalletConnectOptions, "secret" | "relays" | "service">): WalletConnect;
109
126
  /** Create a new WalletConnect instance from a connection string */
110
- static fromConnectionString(connectionString: string, options?: WalletConnectOptions): WalletConnect;
127
+ static fromConnectURI(connectionString: string, options?: Omit<WalletConnectOptions, "secret" | "relays" | "service">): WalletConnect;
111
128
  }
@@ -2,15 +2,17 @@ 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 { defer, filter, firstValueFrom, from, ignoreElements, lastValueFrom, map, merge, mergeMap, ReplaySubject, share, switchMap, timer, toArray, } from "rxjs";
5
+ import { BehaviorSubject, defer, filter, firstValueFrom, from, fromEvent, identity, ignoreElements, lastValueFrom, map, merge, mergeMap, ReplaySubject, share, switchMap, takeUntil, tap, timer, toArray, } from "rxjs";
6
6
  import { WalletRequestBlueprint } from "./blueprints/index.js";
7
7
  import { createWalletError } from "./helpers/error.js";
8
- import { getPreferredEncryption, getWalletNotification, getWalletRequestEncryption, getWalletResponse, getWalletResponseRequestId, getWalletSupport, isWalletNotificationLocked, isWalletResponseLocked, parseWalletConnectURI, supportsMethod, supportsNotifications, supportsNotificationType, unlockWalletNotification, unlockWalletResponse, WALLET_INFO_KIND, WALLET_LEGACY_NOTIFICATION_KIND, WALLET_NOTIFICATION_KIND, WALLET_RESPONSE_KIND, } from "./helpers/index.js";
8
+ import { createWalletAuthURI, getPreferredEncryption, getWalletNotification, getWalletRequestEncryption, getWalletResponse, getWalletResponseRequestId, getWalletSupport, isWalletNotificationLocked, isWalletResponseLocked, parseWalletConnectURI, supportsMethod, supportsNotifications, supportsNotificationType, unlockWalletNotification, unlockWalletResponse, WALLET_INFO_KIND, WALLET_LEGACY_NOTIFICATION_KIND, WALLET_NOTIFICATION_KIND, WALLET_RESPONSE_KIND, } from "./helpers/index.js";
9
9
  export class WalletConnect {
10
10
  /** A fallback method to use for subscriptionMethod if none is passed in when creating the client */
11
11
  static subscriptionMethod = undefined;
12
12
  /** A fallback method to use for publishMethod if none is passed in when creating the client */
13
13
  static publishMethod = undefined;
14
+ /** A fallback pool to use if none is pass in when creating the signer */
15
+ static pool = undefined;
14
16
  /** A method that is called when an event needs to be published */
15
17
  publishMethod;
16
18
  /** The active nostr subscription method */
@@ -20,8 +22,11 @@ export class WalletConnect {
20
22
  signer;
21
23
  /** The relays to use for the connection */
22
24
  relays;
23
- /** The wallet service public key */
24
- service;
25
+ /** The wallet service public key ( unset if waiting for service ) */
26
+ service$ = new BehaviorSubject(undefined);
27
+ get service() {
28
+ return this.service$.value;
29
+ }
25
30
  /** Default timeout for requests */
26
31
  defaultTimeout;
27
32
  /** Observable for wallet info updates */
@@ -32,10 +37,12 @@ export class WalletConnect {
32
37
  events$;
33
38
  /** Shared observable for all wallet notifications */
34
39
  notifications$;
35
- constructor(secret, service, relays, options = {}) {
36
- this.service = service;
37
- this.secret = secret;
38
- this.relays = relays;
40
+ /** An internal observable for listening for the wallet service to connect */
41
+ waitForService$;
42
+ constructor(options) {
43
+ this.secret = options.secret;
44
+ this.relays = options.relays;
45
+ this.service$.next(options.service);
39
46
  this.defaultTimeout = options.timeout || 30000; // 30 second default timeout
40
47
  // Create a signer for the factory
41
48
  this.signer = {
@@ -50,29 +57,42 @@ export class WalletConnect {
50
57
  decrypt: async (pubkey, ciphertext) => nip44.decrypt(ciphertext, nip44.getConversationKey(this.secret, pubkey)),
51
58
  },
52
59
  };
53
- const subscriptionMethod = options.subscriptionMethod || WalletConnect.subscriptionMethod;
60
+ // Get the subscription and publish methods
61
+ const subscriptionMethod = options.subscriptionMethod ||
62
+ options.pool?.subscription ||
63
+ WalletConnect.subscriptionMethod ||
64
+ WalletConnect.pool?.subscription;
54
65
  if (!subscriptionMethod)
55
66
  throw new Error("Missing subscriptionMethod, either pass a method or set WalletConnect.subscriptionMethod");
56
- const publishMethod = options.publishMethod || WalletConnect.publishMethod;
67
+ const publishMethod = options.publishMethod || options.pool?.publish || WalletConnect.publishMethod || WalletConnect.pool?.publish;
57
68
  if (!publishMethod)
58
69
  throw new Error("Missing publishMethod, either pass a method or set WalletConnect.publishMethod");
59
- this.subscriptionMethod = subscriptionMethod;
60
- this.publishMethod = publishMethod;
61
- // Create shared response observable with ref counting and timer
62
- this.events$ = defer(() => this.signer.getPublicKey()).pipe(switchMap((client) => this.subscriptionMethod(this.relays, [
63
- // Subscribe to response events
64
- {
65
- kinds: [WALLET_RESPONSE_KIND, WALLET_NOTIFICATION_KIND, WALLET_LEGACY_NOTIFICATION_KIND],
66
- "#p": [client],
67
- authors: [this.service],
68
- },
69
- // Subscribe to wallet info events
70
- { kinds: [WALLET_INFO_KIND], authors: [this.service] },
71
- ])),
72
- // Ingore strings (support for applesauce-relay)
73
- filter((event) => typeof event !== "string"),
74
- // Only include events from the wallet service
75
- filter((event) => event.pubkey === this.service),
70
+ // Use arrow functions so "this" isn't bound to the signer
71
+ this.subscriptionMethod = (relays, filters) => subscriptionMethod(relays, filters);
72
+ this.publishMethod = (relays, event) => publishMethod(relays, event);
73
+ // Create shared observable for all wallet events
74
+ this.events$ = this.service$.pipe(switchMap((service) => {
75
+ const client = getPublicKey(this.secret);
76
+ // If the service is not known yet, subscribe to a wallet info event tagging the client
77
+ if (!service)
78
+ return this.subscriptionMethod(this.relays, [{ kinds: [WALLET_INFO_KIND], "#p": [client] }]).pipe(
79
+ // Ignore strings (support for applesauce-relay)
80
+ filter((event) => typeof event !== "string"));
81
+ return this.subscriptionMethod(this.relays, [
82
+ // Subscribe to response events
83
+ {
84
+ kinds: [WALLET_RESPONSE_KIND, WALLET_NOTIFICATION_KIND, WALLET_LEGACY_NOTIFICATION_KIND],
85
+ "#p": [client],
86
+ authors: [service],
87
+ },
88
+ // Subscribe to wallet info events
89
+ { kinds: [WALLET_INFO_KIND], authors: [service] },
90
+ ]).pipe(
91
+ // Ignore strings (support for applesauce-relay)
92
+ filter((event) => typeof event !== "string"),
93
+ // Only include events from the wallet service
94
+ filter((event) => event.pubkey === service));
95
+ }),
76
96
  // Only create a single subscription to the relays
77
97
  share({
78
98
  resetOnRefCountZero: () => timer(60000), // Keep subscription open for 1 minute after last unsubscribe
@@ -83,6 +103,20 @@ export class WalletConnect {
83
103
  }));
84
104
  this.encryption$ = this.support$.pipe(map((info) => (info ? getPreferredEncryption(info) : "nip04")));
85
105
  this.notifications$ = this.events$.pipe(filter((event) => event.kind === WALLET_NOTIFICATION_KIND), mergeMap((event) => this.handleNotificationEvent(event)));
106
+ this.waitForService$ = this.events$.pipe(
107
+ // Complete when the service is set
108
+ takeUntil(this.service$),
109
+ // Only listen for wallet info events
110
+ filter((event) => event.kind === WALLET_INFO_KIND && !this.service),
111
+ // Set the service to the pubkey of the wallet info event
112
+ tap((event) => {
113
+ // Set the service to the pubkey of the wallet info event
114
+ this.service$.next(event.pubkey);
115
+ }),
116
+ // Get the service pubkey from the event
117
+ map((event) => event.pubkey),
118
+ // Only create a single subscription to avoid multiple side effects
119
+ share());
86
120
  }
87
121
  /** Process response events and return WalletResponse or throw error */
88
122
  async handleResponseEvent(event, encryption) {
@@ -115,6 +149,8 @@ export class WalletConnect {
115
149
  }
116
150
  /** Core RPC method that makes a request and returns the response */
117
151
  request(request, options = {}) {
152
+ if (!this.service)
153
+ throw new Error("WalletConnect is not connected to a service");
118
154
  // Create the request evnet
119
155
  return defer(async () => {
120
156
  // Get the preferred encryption method for the wallet
@@ -146,6 +182,18 @@ export class WalletConnect {
146
182
  listener(notification.notification);
147
183
  });
148
184
  }
185
+ /** Gets the nostr+walletauth URI for the connection */
186
+ getAuthURI(parts) {
187
+ return createWalletAuthURI({ ...parts, client: getPublicKey(this.secret), relays: this.relays });
188
+ }
189
+ /** Wait for the wallet service to connect */
190
+ async waitForService(abortSignal) {
191
+ if (this.service)
192
+ return this.service;
193
+ return await firstValueFrom(this.waitForService$.pipe(
194
+ // Listen for abort signal
195
+ abortSignal ? takeUntil(fromEvent(abortSignal, "abort")) : identity));
196
+ }
149
197
  // Convenience methods that return promises for easy API usage
150
198
  /** Get the wallet support info */
151
199
  getSupport() {
@@ -253,6 +301,8 @@ export class WalletConnect {
253
301
  }
254
302
  /** Serialize the WalletConnect instance */
255
303
  toJSON() {
304
+ if (!this.service)
305
+ throw new Error("WalletConnect is not connected to a service");
256
306
  return {
257
307
  secret: bytesToHex(this.secret),
258
308
  service: this.service,
@@ -261,11 +311,21 @@ export class WalletConnect {
261
311
  }
262
312
  /** Create a new WalletConnect instance from a serialized object */
263
313
  static fromJSON(json, options) {
264
- return new WalletConnect(hexToBytes(json.secret), json.service, json.relays, options);
314
+ return new WalletConnect({
315
+ ...options,
316
+ secret: hexToBytes(json.secret),
317
+ service: json.service,
318
+ relays: json.relays,
319
+ });
265
320
  }
266
321
  /** Create a new WalletConnect instance from a connection string */
267
- static fromConnectionString(connectionString, options) {
322
+ static fromConnectURI(connectionString, options) {
268
323
  const { secret, service, relays } = parseWalletConnectURI(connectionString);
269
- return new WalletConnect(hexToBytes(secret), service, relays, options);
324
+ return new WalletConnect({
325
+ ...options,
326
+ secret: hexToBytes(secret),
327
+ service,
328
+ relays,
329
+ });
270
330
  }
271
331
  }
@@ -6,7 +6,8 @@ import { NotificationType, WalletNotification } from "./helpers/notification.js"
6
6
  import { GetBalanceParams, GetInfoParams, ListTransactionsParams, LookupInvoiceParams, MakeInvoiceParams, MultiPayInvoiceParams, MultiPayKeysendParams, PayInvoiceParams, PayKeysendParams, WalletRequest } from "./helpers/request.js";
7
7
  import { GetBalanceResult, GetInfoResult, ListTransactionsResult, LookupInvoiceResult, MakeInvoiceResult, MultiPayInvoiceResult, MultiPayKeysendResult, PayInvoiceResult, PayKeysendResult, WalletResponse } from "./helpers/response.js";
8
8
  import { WalletSupport } from "./helpers/support.js";
9
- import { NostrPublishMethod, NostrSubscriptionMethod } from "./types.js";
9
+ import { NostrPool, NostrPublishMethod, NostrSubscriptionMethod } from "./types.js";
10
+ import { WalletAuthURI } from "./helpers/auth-uri.js";
10
11
  /** Handler function for pay_invoice method */
11
12
  export type PayInvoiceHandler = (params: PayInvoiceParams) => Promise<PayInvoiceResult>;
12
13
  /** Handler function for multi_pay_invoice method */
@@ -37,6 +38,12 @@ export interface WalletServiceHandlers {
37
38
  get_balance?: GetBalanceHandler;
38
39
  get_info?: GetInfoHandler;
39
40
  }
41
+ export type SerializedWalletService = {
42
+ /** The client's public key */
43
+ client: string;
44
+ /** The relays to use for the service */
45
+ relays: string[];
46
+ };
40
47
  /** Options for creating a WalletService */
41
48
  export interface WalletServiceOptions {
42
49
  /** The relays to use for the service */
@@ -45,6 +52,8 @@ export interface WalletServiceOptions {
45
52
  signer: EventSigner;
46
53
  /** The client's secret key */
47
54
  secret?: Uint8Array;
55
+ /** The client's public key (used for restoring the service) */
56
+ client?: string;
48
57
  /** Map of method handlers */
49
58
  handlers: WalletServiceHandlers;
50
59
  /** An array of notifications this wallet supports */
@@ -53,6 +62,8 @@ export interface WalletServiceOptions {
53
62
  subscriptionMethod?: NostrSubscriptionMethod;
54
63
  /** An optional method for publishing events */
55
64
  publishMethod?: NostrPublishMethod;
65
+ /** An optional pool for connection methods */
66
+ pool?: NostrPool;
56
67
  }
57
68
  /** NIP-47 Wallet Service implementation */
58
69
  export declare class WalletService {
@@ -60,6 +71,8 @@ export declare class WalletService {
60
71
  static subscriptionMethod: NostrSubscriptionMethod | undefined;
61
72
  /** A fallback method to use for publishMethod if none is passed in when creating the client */
62
73
  static publishMethod: NostrPublishMethod | undefined;
74
+ /** A fallback pool to use if none is pass in when creating the signer */
75
+ static pool: NostrPool | undefined;
63
76
  /** A method for subscribing to relays */
64
77
  protected readonly subscriptionMethod: NostrSubscriptionMethod;
65
78
  /** A method for publishing events */
@@ -77,14 +90,14 @@ export declare class WalletService {
77
90
  pubkey: string | null;
78
91
  /** The client's secret key */
79
92
  protected secret: Uint8Array;
93
+ /** The client's public key */
94
+ client: string;
80
95
  /** Shared observable for all wallet request events */
81
96
  protected events$: Observable<NostrEvent> | null;
82
97
  /** Subscription to the events observable */
83
98
  protected subscription: Subscription | null;
84
99
  /** Whether the service is currently running */
85
100
  running: boolean;
86
- /** Get the clients public key */
87
- get client(): string;
88
101
  constructor(options: WalletServiceOptions);
89
102
  /** Start the wallet service */
90
103
  start(): Promise<void>;
@@ -92,8 +105,8 @@ export declare class WalletService {
92
105
  stop(): void;
93
106
  /** Check if the service is running */
94
107
  isRunning(): boolean;
95
- /** Get the connection string for the service */
96
- getConnectionString(): string;
108
+ /** Get the connection URI for the service */
109
+ getConnectURI(): string;
97
110
  /** Send a notification to the client */
98
111
  notify<T extends WalletNotification>(type: T["notification_type"], notification: T["notification"], legacy?: boolean): Promise<void>;
99
112
  /** Publish the wallet support event */
@@ -108,4 +121,6 @@ export declare class WalletService {
108
121
  protected sendErrorResponse(requestEvent: NostrEvent, errorType: WalletErrorCode, errorMessage: string): Promise<void>;
109
122
  /** Send a response event */
110
123
  protected sendResponse(requestEvent: NostrEvent, response: WalletResponse): Promise<void>;
124
+ /** Creates a service for a nostr+walletauth URI */
125
+ static fromAuthURI(uri: string | WalletAuthURI, options: Omit<WalletServiceOptions, "relays">): WalletService;
111
126
  }