@konemono/nostr-login 1.7.69 → 1.8.0

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.
@@ -1,197 +1,190 @@
1
- import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk';
2
- import { validateEvent, verifySignature } from 'nostr-tools';
1
+ import { EventEmitter } from 'tseep';
2
+ import { validateEvent, verifySignature, nip04, getEventHash, getSignature } from 'nostr-tools';
3
+ import { SimplePool } from 'nostr-tools';
3
4
  import { PrivateKeySigner } from './Signer';
4
5
 
5
- class NostrRpc extends NDKNostrRpc {
6
- protected _ndk: NDK;
7
- protected _signer: PrivateKeySigner;
8
- protected requests: Set<string> = new Set();
9
- private sub?: NDKSubscription;
10
- protected _useNip44: boolean = false;
6
+ // Lightweight Nostr RPC for NIP-46 that does not rely on NDK
11
7
 
12
- public constructor(ndk: NDK, signer: PrivateKeySigner) {
13
- super(ndk, signer, ndk.debug.extend('nip46:signer:rpc'));
14
- this._ndk = ndk;
15
- this._signer = signer;
16
- }
8
+ export type RpcRequest = { id: string; pubkey: string; method: string; params: any[]; event?: any };
9
+ export type RpcResponse = { id: string; result?: any; error?: any; event?: any };
17
10
 
18
- public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
19
- // NOTE: fixing ndk
20
- filter.kinds = filter.kinds?.filter(k => k === 24133);
21
- this.sub = await super.subscribe(filter);
22
- return this.sub;
23
- }
11
+ export class NostrRpc extends EventEmitter {
12
+ protected localSigner: PrivateKeySigner;
13
+ protected localPubkey: string;
14
+ protected localPrivateKey: string;
15
+ protected remotePubkey: string = '';
16
+ protected pool: SimplePool;
17
+ protected relays: string[] = [];
18
+ protected subscription: any;
19
+ protected isSubscribed: boolean = false;
20
+ protected useNip44: boolean = false;
24
21
 
25
- public stop() {
26
- if (this.sub) {
27
- this.sub.stop();
28
- this.sub = undefined;
29
- }
22
+ protected requests: Set<string> = new Set();
23
+
24
+ constructor(localSigner: PrivateKeySigner, relays: string[] = []) {
25
+ super();
26
+ this.localSigner = localSigner;
27
+ this.localPubkey = localSigner.pubkey;
28
+ this.localPrivateKey = (localSigner as any).privateKey;
29
+ this.pool = new SimplePool();
30
+ this.relays = relays && relays.length ? relays : [];
30
31
  }
31
32
 
32
- public setUseNip44(useNip44: boolean) {
33
- this._useNip44 = useNip44;
33
+ public setUseNip44(use: boolean) {
34
+ this.useNip44 = use;
34
35
  }
35
36
 
36
- private isNip04(ciphertext: string) {
37
+ protected isNip04(ciphertext: string) {
37
38
  const l = ciphertext.length;
38
39
  if (l < 28) return false;
39
40
  return ciphertext[l - 28] === '?' && ciphertext[l - 27] === 'i' && ciphertext[l - 26] === 'v' && ciphertext[l - 25] === '=';
40
41
  }
41
42
 
42
- // override to auto-decrypt nip04/nip44
43
- public async parseEvent(event: NDKEvent): Promise<NDKRpcRequest | NDKRpcResponse> {
44
- const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
45
- remoteUser.ndk = this._ndk;
46
- const decrypt = this.isNip04(event.content) ? this._signer.decrypt : this._signer.decryptNip44;
47
- const decryptedContent = await decrypt.call(this._signer, remoteUser, event.content);
48
- const parsedContent = JSON.parse(decryptedContent);
49
- const { id, method, params, result, error } = parsedContent;
50
-
51
- if (method) {
52
- return { id, pubkey: event.pubkey, method, params, event };
53
- } else {
54
- return { id, result, error, event };
43
+ protected async decryptEventContent(event: any) {
44
+ const decrypt = this.isNip04(event.content)
45
+ ? this.localSigner.decrypt.bind(this.localSigner)
46
+ : this.localSigner.decryptNip44?.bind(this.localSigner) ?? this.localSigner.decrypt.bind(this.localSigner);
47
+ try {
48
+ const decrypted = await decrypt(event.pubkey || event.pubkey, event.content);
49
+ return JSON.parse(decrypted);
50
+ } catch (e) {
51
+ throw e;
55
52
  }
56
53
  }
57
54
 
58
- public async parseNostrConnectReply(reply: any, secret: string) {
59
- const event = new NDKEvent(this._ndk, reply);
60
- const parsedEvent = await this.parseEvent(event);
61
- console.log('nostr connect parsedEvent', parsedEvent);
62
- if (!(parsedEvent as NDKRpcRequest).method) {
63
- const response = parsedEvent as NDKRpcResponse;
64
- if (response.result !== secret) throw new Error(response.error);
65
- return event.pubkey;
55
+ protected async parseEvent(event: any): Promise<RpcRequest | RpcResponse> {
56
+ const parsed = await this.decryptEventContent(event);
57
+ const { id, method, params, result, error } = parsed;
58
+ if (method) {
59
+ return { id, pubkey: event.pubkey, method, params, event } as RpcRequest;
66
60
  } else {
67
- throw new Error('Bad nostr connect reply');
61
+ return { id, result, error, event } as RpcResponse;
68
62
  }
69
63
  }
70
64
 
71
- // ndk doesn't support nostrconnect:
72
- // we just listed to an unsolicited reply to
73
- // our pubkey and if it's ack/secret - we're fine
74
- public async listen(nostrConnectSecret: string): Promise<string> {
75
- const pubkey = this._signer.pubkey;
76
- console.log('nostr-login listening for conn to', pubkey);
77
- const sub = await this.subscribe({
78
- 'kinds': [24133],
79
- '#p': [pubkey],
80
- });
81
- return new Promise<string>((ok, err) => {
82
- sub.on('event', async (event: NDKEvent) => {
83
- try {
84
- const parsedEvent = await this.parseEvent(event);
85
- // console.log('ack parsedEvent', parsedEvent);
86
- if (!(parsedEvent as NDKRpcRequest).method) {
87
- const response = parsedEvent as NDKRpcResponse;
88
-
89
- // ignore
90
- if (response.result === 'auth_url') return;
91
-
92
- // FIXME for now accept 'ack' replies, later on only
93
- // accept secrets
94
- if (response.result === 'ack' || response.result === nostrConnectSecret) {
95
- ok(event.pubkey);
96
- } else {
97
- err(response.error);
98
- }
99
- }
100
- } catch (e) {
101
- console.log('error parsing event', e, event.rawEvent());
102
- }
103
- // done
104
- this.stop();
105
- });
106
- });
107
- }
108
-
109
- // since ndk doesn't yet support perms param
110
- // we reimplement the 'connect' call here
111
- // instead of await signer.blockUntilReady();
112
- public async connect(pubkey: string, token?: string, perms?: string) {
113
- return new Promise<void>((ok, err) => {
114
- const connectParams = [pubkey!, token || '', perms || ''];
115
- this.sendRequest(pubkey!, 'connect', connectParams, 24133, (response: NDKRpcResponse) => {
116
- if (response.result === 'ack') {
117
- ok();
65
+ public subscribe(relays: string[], filter: any) {
66
+ if (this.isSubscribed) return;
67
+ this.relays = relays && relays.length ? relays : this.relays;
68
+ const since = Math.floor(Date.now() / 1000) - 60;
69
+ const filters = [{ ...filter, since }];
70
+ this.subscription = this.pool.sub(this.relays, filters);
71
+ this.subscription.on('event', async (ev: any) => {
72
+ try {
73
+ const parsed = await this.parseEvent(ev);
74
+ if (!(parsed as RpcRequest).method) {
75
+ this.emit(`response-${(parsed as RpcResponse).id}`, parsed as RpcResponse);
118
76
  } else {
119
- err(response.error);
77
+ this.emit('request', parsed as RpcRequest);
120
78
  }
121
- });
79
+ } catch (e) {
80
+ // ignore parse errors
81
+ }
82
+ });
83
+ this.subscription.on('eose', () => {
84
+ /* noop */
122
85
  });
86
+ this.isSubscribed = true;
123
87
  }
124
88
 
125
- protected getId(): string {
126
- return Math.random().toString(36).substring(7);
89
+ public stop() {
90
+ if (this.subscription && this.subscription.unsub) {
91
+ try {
92
+ this.subscription.unsub();
93
+ } catch (e) {}
94
+ }
95
+ this.isSubscribed = false;
127
96
  }
128
97
 
129
- public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
130
- const id = this.getId();
131
-
132
- // response handler will deduplicate auth urls and responses
133
- this.setResponseHandler(id, cb);
134
-
135
- // create and sign request
136
- const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
137
- console.log("sendRequest", { event, method, remotePubkey, params });
138
-
139
- // send to relays
140
- await event.publish();
141
-
142
- // NOTE: ndk returns a promise that never resolves and
143
- // in fact REQUIRES cb to be provided (otherwise no way
144
- // to consume the result), we've already stepped on the bug
145
- // of waiting for this unresolvable result, so now we return
146
- // undefined to make sure waiters fail, not hang.
147
- // @ts-ignore
148
- return undefined as NDKRpcResponse;
98
+ protected getId() {
99
+ return Math.random().toString(36).substring(7);
149
100
  }
150
101
 
151
- protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void) {
102
+ protected setResponseHandler(id: string, cb?: (res: RpcResponse) => void) {
152
103
  let authUrlSent = false;
153
104
  const now = Date.now();
154
- return new Promise<NDKRpcResponse>(() => {
155
- const responseHandler = (response: NDKRpcResponse) => {
156
- if (response.result === 'auth_url') {
157
- this.once(`response-${id}`, responseHandler);
158
- if (!authUrlSent) {
159
- authUrlSent = true;
160
- this.emit('authUrl', response.error);
161
- }
162
- } else if (cb) {
163
- if (this.requests.has(id)) {
164
- this.requests.delete(id);
165
- console.log('nostr-login processed nip46 request in', Date.now() - now, 'ms');
166
- cb(response);
167
- }
105
+
106
+ const responseHandler = (response: RpcResponse) => {
107
+ if (response.result === 'auth_url') {
108
+ // reattach for auth_url so we can get final response later
109
+ this.once(`response-${id}`, responseHandler);
110
+ if (!authUrlSent) {
111
+ authUrlSent = true;
112
+ this.emit('authUrl', response.error);
168
113
  }
169
- };
114
+ } else if (cb) {
115
+ if (this.requests.has(id)) {
116
+ this.requests.delete(id);
117
+ cb(response);
118
+ }
119
+ }
120
+ };
170
121
 
171
- this.once(`response-${id}`, responseHandler);
172
- });
122
+ this.once(`response-${id}`, responseHandler);
173
123
  }
174
124
 
175
- protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133) {
125
+ protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: any[] = [], kind = 24133) {
176
126
  this.requests.add(id);
177
- const localUser = await this._signer.user();
178
- const remoteUser = this._ndk.getUser({ pubkey: remotePubkey });
179
127
  const request = { id, method, params };
128
+ // encrypt
129
+ const content =
130
+ this.useNip44 && method !== 'create_account' && this.localSigner.encryptNip44
131
+ ? await this.localSigner.encryptNip44(remotePubkey, JSON.stringify(request))
132
+ : await this.localSigner.encrypt(remotePubkey, JSON.stringify(request));
180
133
 
181
- const event = new NDKEvent(this._ndk, {
134
+ const event: any = {
182
135
  kind,
183
- content: JSON.stringify(request),
136
+ content,
184
137
  tags: [['p', remotePubkey]],
185
- pubkey: localUser.pubkey,
186
- } as NostrEvent);
138
+ pubkey: this.localPubkey,
139
+ created_at: Math.floor(Date.now() / 1000),
140
+ };
187
141
 
188
- const useNip44 = this._useNip44 && method !== 'create_account';
189
- const encrypt = useNip44 ? this._signer.encryptNip44 : this._signer.encrypt;
190
- event.content = await encrypt.call(this._signer, remoteUser, event.content);
191
- await event.sign(this._signer);
142
+ // sign using signer
143
+ await this.localSigner.sign(event as any);
192
144
 
193
145
  return event;
194
146
  }
147
+
148
+ protected async publishRequest(event: any) {
149
+ try {
150
+ await Promise.any(this.pool.publish(this.relays, event));
151
+ } catch (e) {
152
+ // swallow publish errors
153
+ }
154
+ }
155
+
156
+ public async sendRequest(remotePubkey: string, method: string, params: any[] = [], kind = 24133, cb?: (res: RpcResponse) => void): Promise<RpcResponse | undefined> {
157
+ const id = this.getId();
158
+ this.setResponseHandler(id, cb);
159
+ const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
160
+ await this.publishRequest(event);
161
+ return undefined as any;
162
+ }
163
+
164
+ public async listen(nostrConnectSecret: string, relays?: string[]) {
165
+ const pubkey = this.localPubkey;
166
+ this.subscribe(relays || this.relays, { 'kinds': [24133], '#p': [pubkey] });
167
+ return new Promise<string>((ok, err) => {
168
+ const handler = async (event: any) => {
169
+ try {
170
+ const parsed = await this.parseEvent(event);
171
+ if ((parsed as RpcResponse).result === 'auth_url') return; // ignore
172
+ const response = parsed as RpcResponse;
173
+ if (response.result === 'ack' || response.result === nostrConnectSecret) {
174
+ ok(event.pubkey);
175
+ this.stop();
176
+ } else {
177
+ err(response.error);
178
+ this.stop();
179
+ }
180
+ } catch (e) {
181
+ // ignore
182
+ }
183
+ };
184
+
185
+ this.once('request', handler);
186
+ });
187
+ }
195
188
  }
196
189
 
197
190
  export class IframeNostrRpc extends NostrRpc {
@@ -199,92 +192,66 @@ export class IframeNostrRpc extends NostrRpc {
199
192
  private iframePort?: MessagePort;
200
193
  private iframeRequests = new Map<string, { id: string; pubkey: string }>();
201
194
 
202
- public constructor(ndk: NDK, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
203
- super(ndk, localSigner);
204
- this._ndk = ndk;
195
+ constructor(localSigner: PrivateKeySigner, iframePeerOrigin?: string, relays: string[] = []) {
196
+ super(localSigner, relays);
205
197
  this.peerOrigin = iframePeerOrigin;
206
198
  }
207
199
 
208
- public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
209
- if (!this.peerOrigin) return super.subscribe(filter);
210
- return new NDKSubscription(
211
- this._ndk,
212
- {},
213
- {
214
- // don't send to relay
215
- closeOnEose: true,
216
- cacheUsage: NDKSubscriptionCacheUsage.ONLY_CACHE,
217
- },
218
- );
219
- }
220
-
221
200
  public setWorkerIframePort(port: MessagePort) {
222
201
  if (!this.peerOrigin) throw new Error('Unexpected iframe port');
223
-
224
202
  this.iframePort = port;
225
203
 
226
- // to make sure Chrome doesn't terminate the channel
204
+ // keep the channel alive
227
205
  setInterval(() => {
228
- console.log('iframe-nip46 ping');
229
- this.iframePort!.postMessage('ping');
206
+ try {
207
+ this.iframePort!.postMessage('ping');
208
+ } catch (e) {}
230
209
  }, 5000);
231
210
 
232
- port.onmessage = async ev => {
233
- console.log('iframe-nip46 got response', ev.data);
211
+ this.iframePort.onmessage = async ev => {
212
+ // handle special error reply
234
213
  if (typeof ev.data === 'string' && ev.data.startsWith('errorNoKey')) {
235
214
  const event_id = ev.data.split(':')[1];
236
- const { id = '', pubkey = '' } = this.iframeRequests.get(event_id) || {};
215
+ const entry = this.iframeRequests.get(event_id) || { id: '', pubkey: '' };
216
+ const { id = '', pubkey = '' } = entry;
237
217
  if (id && pubkey && this.requests.has(id)) this.emit(`iframeRestart-${pubkey}`);
238
218
  return;
239
219
  }
240
220
 
241
- // a copy-paste from rpc.subscribe
242
221
  try {
243
222
  const event = ev.data;
244
-
245
223
  if (!validateEvent(event)) throw new Error('Invalid event from iframe');
246
224
  if (!verifySignature(event)) throw new Error('Invalid event signature from iframe');
247
- const nevent = new NDKEvent(this._ndk, event);
248
- const parsedEvent = await this.parseEvent(nevent);
249
- // we're only implementing client-side rpc
250
- if (!(parsedEvent as NDKRpcRequest).method) {
251
- console.log('parsed response', parsedEvent);
252
- this.emit(`response-${parsedEvent.id}`, parsedEvent);
225
+ const parsed = await this.parseEvent(event);
226
+ if (!(parsed as RpcRequest).method) {
227
+ this.emit(`response-${(parsed as RpcResponse).id}`, parsed as RpcResponse);
253
228
  }
254
229
  } catch (e) {
255
- console.log('error parsing event', e, ev.data);
230
+ // ignore parse errors
256
231
  }
257
232
  };
258
233
  }
259
234
 
260
- public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
235
+ public async sendRequest(remotePubkey: string, method: string, params: any[] = [], kind = 24133, cb?: (res: RpcResponse) => void): Promise<RpcResponse | undefined> {
261
236
  const id = this.getId();
262
-
263
- // create and sign request event
237
+ this.setResponseHandler(id, cb);
264
238
  const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
265
239
 
266
- // set response handler, it will dedup auth urls,
267
- // and also dedup response handlers - we're sending
268
- // to relays and to iframe
269
- this.setResponseHandler(id, cb);
240
+ // map request event id -> id for iframe restarts
241
+ this.iframeRequests.set(event.id, { id, pubkey: remotePubkey });
270
242
 
271
243
  if (this.iframePort) {
272
- // map request event id to request id, if iframe
273
- // has no key it will reply with error:event_id (it can't
274
- // decrypt the request id without keys)
275
- this.iframeRequests.set(event.id, { id, pubkey: remotePubkey });
276
-
277
- // send to iframe
278
- console.log('iframe-nip46 sending request to', this.peerOrigin, event.rawEvent());
279
- this.iframePort.postMessage(event.rawEvent());
244
+ try {
245
+ this.iframePort.postMessage(event);
246
+ } catch (e) {
247
+ // fallthrough to publish to relays as well
248
+ await this.publishRequest(event);
249
+ }
280
250
  } else {
281
- // send to relays
282
- await event.publish();
251
+ await this.publishRequest(event);
283
252
  }
284
253
 
285
- // see notes in 'super'
286
- // @ts-ignore
287
- return undefined as NDKRpcResponse;
254
+ return undefined as any;
288
255
  }
289
256
  }
290
257
 
@@ -297,20 +264,14 @@ export class ReadyListener {
297
264
  this.origin = origin;
298
265
  this.messages = messages;
299
266
  this.promise = new Promise<any>(ok => {
300
- console.log(new Date(), 'started listener for', this.messages);
301
-
302
- // ready message handler
303
267
  const onReady = async (e: MessageEvent) => {
304
268
  const originHostname = new URL(origin!).hostname;
305
269
  const messageHostname = new URL(e.origin).hostname;
306
- // same host or subdomain
307
270
  const validHost = messageHostname === originHostname || messageHostname.endsWith('.' + originHostname);
308
271
  if (!validHost || !Array.isArray(e.data) || !e.data.length || !this.messages.includes(e.data[0])) {
309
- // console.log(new Date(), 'got invalid ready message', e.origin, e.data);
310
272
  return;
311
273
  }
312
274
 
313
- console.log(new Date(), 'got ready message from', e.origin, e.data);
314
275
  window.removeEventListener('message', onReady);
315
276
  ok(e.data);
316
277
  };
@@ -319,39 +280,32 @@ export class ReadyListener {
319
280
  }
320
281
 
321
282
  async wait(): Promise<any> {
322
- console.log(new Date(), 'waiting for', this.messages);
323
283
  const r = await this.promise;
324
- // NOTE: timer here doesn't help bcs it must be activated when
325
- // user "confirms", but that's happening on a different
326
- // origin and we can't really know.
327
- // await new Promise<any>((ok, err) => {
328
- // // 10 sec should be more than enough
329
- // setTimeout(() => err(new Date() + ' timeout for ' + this.message), 10000);
330
-
331
- // // if promise already resolved or will resolve in the future
332
- // this.promise.then(ok);
333
- // });
334
-
335
- console.log(new Date(), 'finished waiting for', this.messages, r);
336
284
  return r;
337
285
  }
338
286
  }
339
287
 
340
- export class Nip46Signer extends NDKNip46Signer {
288
+ export class Nip46Signer extends EventEmitter {
341
289
  private _userPubkey: string = '';
342
- private _rpc: IframeNostrRpc;
290
+ public remotePubkey: string = '';
291
+ public rpc: IframeNostrRpc | NostrRpc;
292
+ private localSigner: PrivateKeySigner;
343
293
 
344
- constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
345
- super(ndk, signerPubkey, localSigner);
294
+ constructor(localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string, relays: string[] = []) {
295
+ super();
296
+ this.remotePubkey = signerPubkey;
297
+ this.localSigner = localSigner;
298
+
299
+ if (iframeOrigin) {
300
+ this.rpc = new IframeNostrRpc(localSigner, iframeOrigin, relays);
301
+ } else {
302
+ this.rpc = new NostrRpc(localSigner, relays);
303
+ }
304
+ (this.rpc as any).setUseNip44(true);
346
305
 
347
- // override with our own rpc implementation
348
- this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
349
- this._rpc.setUseNip44(true); // !!this.params.optionsModal.dev);
350
- this._rpc.on('authUrl', (url: string) => {
306
+ this.rpc.on('authUrl', (url: string) => {
351
307
  this.emit('authUrl', url);
352
308
  });
353
-
354
- this.rpc = this._rpc;
355
309
  }
356
310
 
357
311
  get userPubkey() {
@@ -359,17 +313,12 @@ export class Nip46Signer extends NDKNip46Signer {
359
313
  }
360
314
 
361
315
  private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
362
- console.log("setSignerPubkey", signerPubkey);
363
-
364
- // ensure it's set
365
316
  this.remotePubkey = signerPubkey;
366
317
 
367
- // when we're sure it's known
368
- this._rpc.on(`iframeRestart-${signerPubkey}`, () => {
318
+ this.rpc.on(`iframeRestart-${signerPubkey}`, () => {
369
319
  this.emit('iframeRestart');
370
320
  });
371
321
 
372
- // now call getPublicKey and swap remotePubkey w/ that
373
322
  await this.initUserPubkey(sameAsUser ? signerPubkey : '');
374
323
  }
375
324
 
@@ -383,47 +332,49 @@ export class Nip46Signer extends NDKNip46Signer {
383
332
 
384
333
  this._userPubkey = await new Promise<string>((ok, err) => {
385
334
  if (!this.remotePubkey) throw new Error('Signer pubkey not set');
386
-
387
- console.log("get_public_key", this.remotePubkey);
388
- this._rpc.sendRequest(this.remotePubkey, 'get_public_key', [], 24133, (response: NDKRpcResponse) => {
335
+ this.rpc.sendRequest(this.remotePubkey, 'get_public_key', [], 24133, (response: RpcResponse) => {
336
+ if (response.error) return err(response.error);
389
337
  ok(response.result);
390
338
  });
391
339
  });
392
340
  }
393
341
 
394
342
  public async listen(nostrConnectSecret: string) {
395
- const signerPubkey = await (this.rpc as IframeNostrRpc).listen(nostrConnectSecret);
343
+ const signerPubkey = await (this.rpc as any).listen(nostrConnectSecret, (this.rpc as any).relays);
396
344
  await this.setSignerPubkey(signerPubkey);
397
345
  }
398
346
 
399
347
  public async connect(token?: string, perms?: string) {
400
348
  if (!this.remotePubkey) throw new Error('No signer pubkey');
401
- await this._rpc.connect(this.remotePubkey, token, perms);
402
- await this.setSignerPubkey(this.remotePubkey);
349
+ return new Promise<void>((ok, err) => {
350
+ const params = [this.localSigner.pubkey, token || '', perms || ''];
351
+ this.rpc.sendRequest(this.remotePubkey, 'connect', params, 24133, (response: RpcResponse) => {
352
+ if (response.result === 'ack') ok();
353
+ else err(response.error);
354
+ });
355
+ });
403
356
  }
404
357
 
405
- public async setListenReply(reply: any, nostrConnectSecret: string) {
406
- const signerPubkey = await this._rpc.parseNostrConnectReply(reply, nostrConnectSecret);
407
- await this.setSignerPubkey(signerPubkey, true);
358
+ // convenience wrappers
359
+ public async createAccount2(params: any) {
360
+ return new Promise((ok, err) => {
361
+ this.rpc.sendRequest(this.remotePubkey, 'create_account', [params], 24133, (response: RpcResponse) => {
362
+ if (response.error) err(response.error);
363
+ else ok(response.result);
364
+ });
365
+ });
408
366
  }
409
367
 
410
- public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
411
- const params = [
412
- name,
413
- domain,
414
- '', // email
415
- perms,
416
- ];
417
-
418
- const r = await new Promise<NDKRpcResponse>(ok => {
419
- this.rpc.sendRequest(bunkerPubkey, 'create_account', params, undefined, ok);
420
- });
368
+ public async encrypt(pubkey: string, plaintext: string) {
369
+ return this.localSigner.encrypt(pubkey, plaintext);
370
+ }
421
371
 
422
- console.log('create_account pubkey', r);
423
- if (r.result === 'error') {
424
- throw new Error(r.error);
425
- }
372
+ public async decrypt(pubkey: string, ciphertext: string) {
373
+ return this.localSigner.decrypt(pubkey, ciphertext);
374
+ }
426
375
 
427
- return r.result;
376
+ public async sign(event: any) {
377
+ await this.localSigner.sign(event);
378
+ return event.sig;
428
379
  }
429
380
  }
@@ -1,6 +1,12 @@
1
1
  import { Nostr, NostrParams } from './';
2
2
  import { EventEmitter } from 'tseep';
3
3
 
4
+ declare global {
5
+ interface Window {
6
+ nostr?: any;
7
+ }
8
+ }
9
+
4
10
  class NostrExtensionService extends EventEmitter {
5
11
  private params: NostrParams;
6
12
  private nostrExtension: any | undefined;