applesauce-wallet-connect 0.0.0-next-20250808173123

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 (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/dist/actions/index.d.ts +1 -0
  4. package/dist/actions/index.js +1 -0
  5. package/dist/actions/tokens.d.ts +17 -0
  6. package/dist/actions/tokens.js +110 -0
  7. package/dist/actions/wallet.d.ts +13 -0
  8. package/dist/actions/wallet.js +64 -0
  9. package/dist/actions/zap-info.d.ts +22 -0
  10. package/dist/actions/zap-info.js +83 -0
  11. package/dist/actions/zaps.d.ts +8 -0
  12. package/dist/actions/zaps.js +30 -0
  13. package/dist/blueprints/history.d.ts +4 -0
  14. package/dist/blueprints/history.js +11 -0
  15. package/dist/blueprints/index.d.ts +4 -0
  16. package/dist/blueprints/index.js +4 -0
  17. package/dist/blueprints/info.d.ts +4 -0
  18. package/dist/blueprints/info.js +8 -0
  19. package/dist/blueprints/notification.d.ts +15 -0
  20. package/dist/blueprints/notification.js +21 -0
  21. package/dist/blueprints/request.d.ts +9 -0
  22. package/dist/blueprints/request.js +12 -0
  23. package/dist/blueprints/response.d.ts +5 -0
  24. package/dist/blueprints/response.js +10 -0
  25. package/dist/blueprints/support.d.ts +4 -0
  26. package/dist/blueprints/support.js +8 -0
  27. package/dist/blueprints/tokens.d.ts +7 -0
  28. package/dist/blueprints/tokens.js +11 -0
  29. package/dist/blueprints/wallet.d.ts +5 -0
  30. package/dist/blueprints/wallet.js +11 -0
  31. package/dist/blueprints/zaps.d.ts +8 -0
  32. package/dist/blueprints/zaps.js +12 -0
  33. package/dist/helpers/animated-qr.d.ts +30 -0
  34. package/dist/helpers/animated-qr.js +71 -0
  35. package/dist/helpers/connect-uri.d.ts +12 -0
  36. package/dist/helpers/connect-uri.js +23 -0
  37. package/dist/helpers/encryption.d.ts +2 -0
  38. package/dist/helpers/encryption.js +1 -0
  39. package/dist/helpers/error.d.ts +55 -0
  40. package/dist/helpers/error.js +81 -0
  41. package/dist/helpers/history.d.ts +26 -0
  42. package/dist/helpers/history.js +47 -0
  43. package/dist/helpers/index.d.ts +7 -0
  44. package/dist/helpers/index.js +7 -0
  45. package/dist/helpers/info.d.ts +34 -0
  46. package/dist/helpers/info.js +97 -0
  47. package/dist/helpers/methods.d.ts +1 -0
  48. package/dist/helpers/methods.js +1 -0
  49. package/dist/helpers/notification.d.ts +28 -0
  50. package/dist/helpers/notification.js +28 -0
  51. package/dist/helpers/nutzap.d.ts +27 -0
  52. package/dist/helpers/nutzap.js +66 -0
  53. package/dist/helpers/request.d.ts +131 -0
  54. package/dist/helpers/request.js +51 -0
  55. package/dist/helpers/response.d.ts +138 -0
  56. package/dist/helpers/response.js +35 -0
  57. package/dist/helpers/support.d.ts +34 -0
  58. package/dist/helpers/support.js +97 -0
  59. package/dist/helpers/tokens.d.ts +58 -0
  60. package/dist/helpers/tokens.js +162 -0
  61. package/dist/helpers/wallet.d.ts +15 -0
  62. package/dist/helpers/wallet.js +41 -0
  63. package/dist/helpers/zap-info.d.ts +19 -0
  64. package/dist/helpers/zap-info.js +42 -0
  65. package/dist/index.d.ts +5 -0
  66. package/dist/index.js +5 -0
  67. package/dist/interface.d.ts +6 -0
  68. package/dist/interface.js +1 -0
  69. package/dist/models/history.d.ts +6 -0
  70. package/dist/models/history.js +21 -0
  71. package/dist/models/index.d.ts +4 -0
  72. package/dist/models/index.js +4 -0
  73. package/dist/models/nutzap.d.ts +6 -0
  74. package/dist/models/nutzap.js +16 -0
  75. package/dist/models/tokens.d.ts +6 -0
  76. package/dist/models/tokens.js +58 -0
  77. package/dist/models/wallet.d.ts +13 -0
  78. package/dist/models/wallet.js +18 -0
  79. package/dist/operations/history.d.ts +7 -0
  80. package/dist/operations/history.js +34 -0
  81. package/dist/operations/index.d.ts +5 -0
  82. package/dist/operations/index.js +5 -0
  83. package/dist/operations/nutzap.d.ts +14 -0
  84. package/dist/operations/nutzap.js +33 -0
  85. package/dist/operations/tokens.d.ts +4 -0
  86. package/dist/operations/tokens.js +24 -0
  87. package/dist/operations/wallet.d.ts +8 -0
  88. package/dist/operations/wallet.js +30 -0
  89. package/dist/operations/zap-info.d.ts +10 -0
  90. package/dist/operations/zap-info.js +17 -0
  91. package/dist/types.d.ts +6 -0
  92. package/dist/types.js +1 -0
  93. package/dist/wallet-connect.d.ts +111 -0
  94. package/dist/wallet-connect.js +271 -0
  95. package/dist/wallet-service.d.ts +111 -0
  96. package/dist/wallet-service.js +270 -0
  97. package/package.json +83 -0
@@ -0,0 +1,270 @@
1
+ import { logger } from "applesauce-core";
2
+ import { create } from "applesauce-factory";
3
+ import { generateSecretKey, getPublicKey, verifyEvent } from "nostr-tools";
4
+ import { filter, mergeMap, share } from "rxjs";
5
+ import { WalletLegacyNotificationBlueprint, WalletNotificationBlueprint } from "./blueprints/notification.js";
6
+ import { WalletResponseBlueprint } from "./blueprints/response.js";
7
+ import { WalletSupportBlueprint } from "./blueprints/support.js";
8
+ import { WalletBaseError } from "./helpers/error.js";
9
+ import { getWalletRequest, isWalletRequestExpired, isWalletRequestLocked, unlockWalletRequest, WALLET_REQUEST_KIND, } from "./helpers/request.js";
10
+ import { bytesToHex } from "@noble/hashes/utils";
11
+ /** NIP-47 Wallet Service implementation */
12
+ export class WalletService {
13
+ /** A fallback method to use for subscriptionMethod if none is passed in when creating the client */
14
+ static subscriptionMethod = undefined;
15
+ /** A fallback method to use for publishMethod if none is passed in when creating the client */
16
+ static publishMethod = undefined;
17
+ /** A method for subscribing to relays */
18
+ subscriptionMethod;
19
+ /** A method for publishing events */
20
+ publishMethod;
21
+ log = logger.extend("WalletService");
22
+ /** The relays to use for the service */
23
+ relays;
24
+ /** The signer used for creating and unlocking events */
25
+ signer;
26
+ /** Map of method handlers */
27
+ handlers;
28
+ /** Wallet support information */
29
+ support;
30
+ /** The service's public key */
31
+ pubkey = null;
32
+ /** The client's secret key */
33
+ secret;
34
+ /** Shared observable for all wallet request events */
35
+ events$ = null;
36
+ /** Subscription to the events observable */
37
+ subscription = null;
38
+ /** Whether the service is currently running */
39
+ running = false;
40
+ /** Get the clients public key */
41
+ get client() {
42
+ return getPublicKey(this.secret);
43
+ }
44
+ constructor(options) {
45
+ this.relays = options.relays;
46
+ this.signer = options.signer;
47
+ this.handlers = options.handlers;
48
+ this.secret = options.secret ?? generateSecretKey();
49
+ const subscriptionMethod = options.subscriptionMethod || WalletService.subscriptionMethod;
50
+ if (!subscriptionMethod)
51
+ throw new Error("Missing subscriptionMethod, either pass a method or set WalletService.subscriptionMethod");
52
+ const publishMethod = options.publishMethod || WalletService.publishMethod;
53
+ if (!publishMethod)
54
+ throw new Error("Missing publishMethod, either pass a method or set WalletService.publishMethod");
55
+ this.subscriptionMethod = subscriptionMethod;
56
+ this.publishMethod = publishMethod;
57
+ const encryption = [];
58
+ if (options.signer.nip04)
59
+ encryption.push("nip04");
60
+ if (options.signer.nip44)
61
+ encryption.push("nip44_v2");
62
+ // Ensure there is at least one encryption method
63
+ if (!encryption.length)
64
+ throw new Error("No encryption methods supported by signer");
65
+ // Build the support infomation based on options
66
+ this.support = {
67
+ methods: Object.keys(options.handlers),
68
+ notifications: options.notifications,
69
+ encryption,
70
+ };
71
+ }
72
+ /** Start the wallet service */
73
+ async start() {
74
+ if (this.running)
75
+ return;
76
+ this.running = true;
77
+ // Get our public key
78
+ this.pubkey = await this.signer.getPublicKey();
79
+ // Create shared request observable with ref counting and timer
80
+ this.events$ = this.subscriptionMethod(this.relays, [
81
+ {
82
+ kinds: [WALLET_REQUEST_KIND],
83
+ "#p": [this.pubkey], // Only requests directed to us
84
+ authors: [this.client], // Only requests from the client
85
+ },
86
+ ]).pipe(
87
+ // Ignore strings (support for applesauce-relay)
88
+ filter((event) => typeof event !== "string"),
89
+ // Only include valid wallet request events
90
+ filter((event) => event.kind === WALLET_REQUEST_KIND && event.pubkey === this.client),
91
+ // Verify event signature
92
+ filter((event) => verifyEvent(event)),
93
+ // Only create a single subscription to the relays
94
+ share());
95
+ // Subscribe to request events and handle them
96
+ this.subscription = this.events$.pipe(mergeMap((requestEvent) => this.handleRequestEvent(requestEvent))).subscribe({
97
+ error: (error) => {
98
+ this.log("Error handling wallet request:", error);
99
+ },
100
+ });
101
+ // Publish wallet support event
102
+ await this.publishSupportEvent();
103
+ }
104
+ /** Stop the wallet service */
105
+ stop() {
106
+ if (!this.running)
107
+ return;
108
+ this.running = false;
109
+ if (this.subscription) {
110
+ this.subscription.unsubscribe();
111
+ this.subscription = null;
112
+ }
113
+ this.events$ = null;
114
+ }
115
+ /** Check if the service is running */
116
+ isRunning() {
117
+ return this.running;
118
+ }
119
+ /** Get the connection string for the service */
120
+ getConnectionString() {
121
+ if (!this.pubkey)
122
+ throw new Error("Service is not running");
123
+ if (!this.relays.length)
124
+ throw new Error("No relays configured");
125
+ const url = new URL(`nostr+walletconnect://${this.pubkey}`);
126
+ for (const relay of this.relays) {
127
+ url.searchParams.append("relay", relay);
128
+ }
129
+ url.searchParams.set("secret", bytesToHex(this.secret));
130
+ return url.toString();
131
+ }
132
+ /** Send a notification to the client */
133
+ async notify(type, notification, legacy = false) {
134
+ const draft = await create({ signer: this.signer }, legacy ? WalletLegacyNotificationBlueprint : WalletNotificationBlueprint, this.client, {
135
+ notification_type: type,
136
+ notification,
137
+ });
138
+ const event = await this.signer.signEvent(draft);
139
+ await this.publishMethod(this.relays, event);
140
+ }
141
+ /** Publish the wallet support event */
142
+ async publishSupportEvent() {
143
+ try {
144
+ const draft = await create({ signer: this.signer }, WalletSupportBlueprint, this.support);
145
+ const event = await this.signer.signEvent(draft);
146
+ await this.publishMethod(this.relays, event);
147
+ }
148
+ catch (error) {
149
+ this.log("Failed to publish wallet support event:", error);
150
+ throw error;
151
+ }
152
+ }
153
+ /** Handle a wallet request event */
154
+ async handleRequestEvent(requestEvent) {
155
+ try {
156
+ // Check if the request has expired
157
+ if (isWalletRequestExpired(requestEvent))
158
+ return await this.sendErrorResponse(requestEvent, "OTHER", "Request has expired");
159
+ // Unlock the request if needed
160
+ let request;
161
+ if (isWalletRequestLocked(requestEvent)) {
162
+ request = await unlockWalletRequest(requestEvent, this.signer);
163
+ }
164
+ else {
165
+ request = getWalletRequest(requestEvent);
166
+ }
167
+ if (!request)
168
+ return await this.sendErrorResponse(requestEvent, "OTHER", "Failed to decrypt or parse request");
169
+ // Handle the request based on its method
170
+ await this.processRequest(requestEvent, request);
171
+ }
172
+ catch (error) {
173
+ this.log("Error processing wallet request:", error);
174
+ await this.sendErrorResponse(requestEvent, "INTERNAL", "Internal server error");
175
+ }
176
+ }
177
+ /** Process a decrypted wallet request */
178
+ async processRequest(requestEvent, request) {
179
+ const handler = this.handlers[request.method];
180
+ if (!handler) {
181
+ await this.sendErrorResponse(requestEvent, "NOT_IMPLEMENTED", `Method ${request.method} not supported`);
182
+ return;
183
+ }
184
+ try {
185
+ let result;
186
+ const method = request.method; // Store method for use in catch block
187
+ switch (method) {
188
+ case "pay_invoice":
189
+ result = await handler(request.params);
190
+ break;
191
+ case "multi_pay_invoice":
192
+ result = await handler(request.params);
193
+ break;
194
+ case "pay_keysend":
195
+ result = await handler(request.params);
196
+ break;
197
+ case "multi_pay_keysend":
198
+ result = await handler(request.params);
199
+ break;
200
+ case "make_invoice":
201
+ result = await handler(request.params);
202
+ break;
203
+ case "lookup_invoice":
204
+ result = await handler(request.params);
205
+ break;
206
+ case "list_transactions":
207
+ result = await handler(request.params);
208
+ break;
209
+ case "get_balance":
210
+ result = await handler(request.params);
211
+ break;
212
+ case "get_info":
213
+ result = await handler(request.params);
214
+ break;
215
+ }
216
+ // Send success response
217
+ await this.sendSuccessResponse(requestEvent, method, result);
218
+ }
219
+ catch (error) {
220
+ this.log(`Error executing ${request.method}:`, error);
221
+ // Determine error type and message
222
+ let errorCode = "OTHER";
223
+ let errorMessage = "Unknown error";
224
+ if (error instanceof WalletBaseError) {
225
+ errorCode = error.code;
226
+ errorMessage = error.message;
227
+ }
228
+ else if (error instanceof Error) {
229
+ errorMessage = error.message;
230
+ }
231
+ await this.sendErrorResponse(requestEvent, errorCode, errorMessage);
232
+ }
233
+ }
234
+ /** Send a success response */
235
+ async sendSuccessResponse(requestEvent, method, result) {
236
+ const response = {
237
+ result_type: method,
238
+ error: null,
239
+ result,
240
+ };
241
+ await this.sendResponse(requestEvent, response);
242
+ }
243
+ /** Send an error response */
244
+ async sendErrorResponse(requestEvent, errorType, errorMessage) {
245
+ const request = getWalletRequest(requestEvent);
246
+ if (!request)
247
+ throw new Error("Cant respond to a locked request");
248
+ const response = {
249
+ result_type: request.method,
250
+ error: {
251
+ type: errorType,
252
+ message: errorMessage,
253
+ },
254
+ result: null,
255
+ };
256
+ await this.sendResponse(requestEvent, response);
257
+ }
258
+ /** Send a response event */
259
+ async sendResponse(requestEvent, response) {
260
+ try {
261
+ const draft = await create({ signer: this.signer }, WalletResponseBlueprint, requestEvent, response);
262
+ const event = await this.signer.signEvent(draft);
263
+ await this.publishMethod(this.relays, event);
264
+ }
265
+ catch (error) {
266
+ this.log("Failed to send response:", error);
267
+ throw error;
268
+ }
269
+ }
270
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "applesauce-wallet-connect",
3
+ "version": "0.0.0-next-20250808173123",
4
+ "description": "",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "keywords": [
9
+ "nostr",
10
+ "applesauce",
11
+ "nwc"
12
+ ],
13
+ "author": "hzrd149",
14
+ "license": "MIT",
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "exports": {
19
+ ".": {
20
+ "import": "./dist/index.js",
21
+ "require": "./dist/index.js",
22
+ "types": "./dist/index.d.ts"
23
+ },
24
+ "./wallet-connect": {
25
+ "import": "./dist/wallet-connect.js",
26
+ "require": "./dist/wallet-connect.js",
27
+ "types": "./dist/wallet-connect.d.ts"
28
+ },
29
+ "./wallet-service": {
30
+ "import": "./dist/wallet-service.js",
31
+ "require": "./dist/wallet-service.js",
32
+ "types": "./dist/wallet-service.d.ts"
33
+ },
34
+ "./types": {
35
+ "import": "./dist/types.js",
36
+ "require": "./dist/types.js",
37
+ "types": "./dist/types.d.ts"
38
+ },
39
+ "./helpers": {
40
+ "import": "./dist/helpers/index.js",
41
+ "require": "./dist/helpers/index.js",
42
+ "types": "./dist/helpers/index.d.ts"
43
+ },
44
+ "./helpers/*": {
45
+ "import": "./dist/helpers/*.js",
46
+ "require": "./dist/helpers/*.js",
47
+ "types": "./dist/helpers/*.d.ts"
48
+ },
49
+ "./blueprints": {
50
+ "import": "./dist/blueprints/index.js",
51
+ "require": "./dist/blueprints/index.js",
52
+ "types": "./dist/blueprints/index.d.ts"
53
+ },
54
+ "./blueprints/*": {
55
+ "import": "./dist/blueprints/*.js",
56
+ "require": "./dist/blueprints/*.js",
57
+ "types": "./dist/blueprints/*.d.ts"
58
+ }
59
+ },
60
+ "dependencies": {
61
+ "applesauce-core": "0.0.0-next-20250808173123",
62
+ "applesauce-factory": "0.0.0-next-20250808173123",
63
+ "nostr-tools": "^2.13",
64
+ "@noble/hashes": "^1.7.1",
65
+ "rxjs": "^7.8.1"
66
+ },
67
+ "devDependencies": {
68
+ "@hirez_io/observer-spy": "^2.2.0",
69
+ "@types/debug": "^4.1.12",
70
+ "typescript": "^5.8.3",
71
+ "vitest": "^3.2.3"
72
+ },
73
+ "funding": {
74
+ "type": "lightning",
75
+ "url": "lightning:nostrudel@geyser.fund"
76
+ },
77
+ "scripts": {
78
+ "build": "tsc",
79
+ "watch:build": "tsc --watch > /dev/null",
80
+ "test": "vitest run --passWithNoTests",
81
+ "watch:test": "vitest"
82
+ }
83
+ }