1#![cfg_attr(test, allow(missing_docs))]
12#![cfg_attr(not(test), warn(missing_docs))]
13#![warn(rustdoc::bare_urls)]
14#![doc = include_str!("../README.md")]
15#![cfg(target_family = "wasm")]
17
18use std::future::Future;
19use std::pin::Pin;
20use std::str::FromStr;
21use std::task::{Context, Poll};
22
23use js_sys::{Array, Function, JsString, Object, Promise, Reflect};
24use nostr::prelude::*;
25use wasm_bindgen::prelude::*;
26use wasm_bindgen_futures::JsFuture;
27use web_sys::Window;
28
29pub mod error;
30pub mod prelude;
31
32use self::error::Error;
33
34const GET_PUBLIC_KEY: &str = "getPublicKey";
35const SIGN_EVENT: &str = "signEvent";
36const NIP04: &str = "nip04";
37const NIP44: &str = "nip44";
38const ENCRYPT: &str = "encrypt";
39const DECRYPT: &str = "decrypt";
40
41enum CallFunc<'a> {
42 Call0,
43 Call1(&'a JsValue),
44 Call2(&'a JsValue, &'a JsValue),
45}
46
47struct SendWasmFuture<F> {
48 inner: F,
49}
50
51unsafe impl<F> Send for SendWasmFuture<F> {}
54
55impl<F> Future for SendWasmFuture<F>
56where
57 F: Future,
58{
59 type Output = F::Output;
60
61 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
62 unsafe { self.map_unchecked_mut(|this| &mut this.inner) }.poll(cx)
64 }
65}
66
67fn boxed_send<'a, F>(future: F) -> Pin<Box<dyn Future<Output = F::Output> + Send + 'a>>
68where
69 F: Future + 'a,
70{
71 Box::pin(SendWasmFuture { inner: future })
72}
73
74#[derive(Debug, Clone)]
80pub struct BrowserSigner {
81 nostr_obj: Object,
83}
84
85unsafe impl Send for BrowserSigner {}
86
87unsafe impl Sync for BrowserSigner {}
88
89impl BrowserSigner {
90 pub fn new() -> Result<Self, Error> {
92 let window: Window = web_sys::window().ok_or_else(Error::no_global_window_object)?;
93 let namespace: JsValue = Reflect::get(&window, &JsValue::from_str("nostr"))
94 .map_err(|_| Error::namespace_not_found("nostr"))?;
95 let nostr_obj: Object = namespace
96 .dyn_into()
97 .map_err(|_| Error::namespace_not_found("nostr"))?;
98 Ok(Self { nostr_obj })
99 }
100
101 fn get_func(&self, obj: &Object, name: &str) -> Result<Function, Error> {
102 let val: JsValue = Reflect::get(obj, &JsValue::from_str(name))
103 .map_err(|_| Error::namespace_not_found(name))?;
104 val.dyn_into().map_err(|_| Error::namespace_not_found(name))
105 }
106
107 fn get_sub_obj(&self, super_obj: &Object, name: &str) -> Result<Object, Error> {
108 let namespace: JsValue = Reflect::get(super_obj, &JsValue::from_str(name))
109 .map_err(|_| Error::namespace_not_found(name))?;
110 namespace
111 .dyn_into()
112 .map_err(|_| Error::namespace_not_found(name))
113 }
114
115 #[inline]
117 fn get_value_by_key(&self, obj: &Object, key: &str) -> Result<JsValue, Error> {
118 Reflect::get(obj, &JsValue::from_str(key)).map_err(|_| Error::object_key_not_found(key))
119 }
120
121 async fn call_func<T>(&self, obj: &Object, name: &str, args: CallFunc<'_>) -> Result<T, Error>
122 where
123 T: JsCast,
124 {
125 let func: Function = self.get_func(obj, name)?;
126 let temp: JsValue = match args {
127 CallFunc::Call0 => func.call0(obj)?,
128 CallFunc::Call1(arg) => func.call1(obj, arg)?,
129 CallFunc::Call2(arg1, arg2) => func.call2(obj, arg1, arg2)?,
130 };
131 let promise: Promise = Promise::resolve(&temp);
132 let result: JsValue = JsFuture::from(promise).await?;
133
134 result.dyn_into().map_err(|_| Error::type_mismatch())
135 }
136
137 async fn _get_public_key(&self) -> Result<PublicKey, Error> {
139 let public_key: JsString = self
140 .call_func(&self.nostr_obj, GET_PUBLIC_KEY, CallFunc::Call0)
141 .await?;
142 let public_key: String = public_key.into();
143 Ok(PublicKey::from_hex(&public_key)?)
144 }
145
146 async fn _sign_event(&self, unsigned: UnsignedEvent) -> Result<Event, Error> {
147 let unsigned_obj = Object::new();
148
149 let tags: Array = unsigned
150 .tags
151 .iter()
152 .map(|t| {
153 t.as_slice()
154 .iter()
155 .map(|v| JsValue::from_str(v))
156 .collect::<Array>()
157 })
158 .collect();
159
160 if let Some(id) = unsigned.id {
161 Reflect::set(&unsigned_obj, &JsValue::from_str("id"), &id.to_hex().into())?;
162 }
163
164 Reflect::set(
165 &unsigned_obj,
166 &JsValue::from_str("pubkey"),
167 &unsigned.pubkey.to_hex().into(),
168 )?;
169
170 Reflect::set(
171 &unsigned_obj,
172 &JsValue::from_str("created_at"),
173 &(unsigned.created_at.as_secs() as f64).into(),
174 )?;
175
176 Reflect::set(
177 &unsigned_obj,
178 &JsValue::from_str("kind"),
179 &(unsigned.kind.as_u16() as f64).into(),
180 )?;
181
182 Reflect::set(&unsigned_obj, &JsValue::from_str("tags"), &tags.into())?;
183
184 Reflect::set(
185 &unsigned_obj,
186 &JsValue::from_str("content"),
187 &unsigned.content.as_str().into(),
188 )?;
189
190 let event_obj: Object = self
191 .call_func(&self.nostr_obj, SIGN_EVENT, CallFunc::Call1(&unsigned_obj))
192 .await?;
193
194 let sig: String = self
196 .get_value_by_key(&event_obj, "sig")?
197 .as_string()
198 .ok_or_else(Error::type_mismatch)?;
199 let sig: Signature = Signature::from_str(&sig)?;
200
201 Ok(unsigned.add_signature(sig)?)
203 }
204
205 async fn encryption_decryption(
206 &self,
207 namespace: &str,
208 func_name: &str,
209 public_key: &PublicKey,
210 content: &str,
211 ) -> Result<String, Error> {
212 let sub_obj: Object = self.get_sub_obj(&self.nostr_obj, namespace)?;
213 let pk: JsValue = JsValue::from_str(&public_key.to_hex());
214 let content: JsValue = JsValue::from_str(content);
215 let result: JsString = self
216 .call_func(&sub_obj, func_name, CallFunc::Call2(&pk, &content))
217 .await?;
218 Ok(result.into())
219 }
220}
221
222impl AsyncGetPublicKey for BrowserSigner {
223 type Error = Error;
224
225 #[inline]
226 fn get_public_key_async(
227 &self,
228 ) -> Pin<Box<dyn Future<Output = Result<PublicKey, Self::Error>> + Send + '_>> {
229 boxed_send(async move { self._get_public_key().await })
230 }
231}
232
233impl AsyncSignEvent for BrowserSigner {
234 type Error = Error;
235
236 #[inline]
237 fn sign_event_async(
238 &self,
239 unsigned: UnsignedEvent,
240 ) -> Pin<Box<dyn Future<Output = Result<Event, Self::Error>> + Send + '_>> {
241 boxed_send(async move { self._sign_event(unsigned).await })
242 }
243}
244
245impl AsyncNip04 for BrowserSigner {
246 type Error = Error;
247
248 fn nip04_encrypt_async<'a>(
249 &'a self,
250 public_key: &'a PublicKey,
251 content: &'a str,
252 ) -> Pin<Box<dyn Future<Output = Result<String, Self::Error>> + Send + 'a>> {
253 boxed_send(async move {
254 self.encryption_decryption(NIP04, ENCRYPT, public_key, content)
255 .await
256 })
257 }
258
259 fn nip04_decrypt_async<'a>(
260 &'a self,
261 public_key: &'a PublicKey,
262 encrypted_content: &'a str,
263 ) -> Pin<Box<dyn Future<Output = Result<String, Self::Error>> + Send + 'a>> {
264 boxed_send(async move {
265 self.encryption_decryption(NIP04, DECRYPT, public_key, encrypted_content)
266 .await
267 })
268 }
269}
270
271impl AsyncNip44 for BrowserSigner {
272 type Error = Error;
273
274 fn nip44_encrypt_async<'a>(
275 &'a self,
276 public_key: &'a PublicKey,
277 content: &'a str,
278 ) -> Pin<Box<dyn Future<Output = Result<String, Self::Error>> + Send + 'a>> {
279 boxed_send(async move {
280 self.encryption_decryption(NIP44, ENCRYPT, public_key, content)
281 .await
282 })
283 }
284
285 fn nip44_decrypt_async<'a>(
286 &'a self,
287 public_key: &'a PublicKey,
288 payload: &'a str,
289 ) -> Pin<Box<dyn Future<Output = Result<String, Self::Error>> + Send + 'a>> {
290 boxed_send(async move {
291 self.encryption_decryption(NIP44, DECRYPT, public_key, payload)
292 .await
293 })
294 }
295}