Skip to content

Panic in wasm environment #1394

Description

@silicology1

Describe the bug

I am using dioxus web and
nostr-sdk = "=0.45.0-alpha.4"

To Reproduce
This is my login page code

#![allow(non_snake_case)]

use dioxus::prelude::*;
use nostr_sdk::prelude::*;

use crate::Session;

const DEFAULT_RELAYS: [&str; 3] = [
    "wss://relay.damus.io",
    "wss://nos.lol",
    "wss://relay.primal.net",
];

#[component]
pub fn LoginPage(mut session: Signal<Option<Session>>) -> Element {
    let mut mode = use_signal(|| "login".to_string()); // "login" | "create"
    let mut key_input = use_signal(String::new);
    let mut error = use_signal(|| Option::<String>::None);
    let mut loading = use_signal(|| false);
    let mut new_keys_preview: Signal<Option<Keys>> = use_signal(|| None);

    // Shared by both flows: spin up a Client for these keys, connect to
    // relays, and hand the resulting Session back up to the app.
    let mut connect_and_enter = move |keys: Keys| {
        loading.set(true);
        error.set(None);
        spawn(async move {
            // Use the new SignerAuthenticator API from the latest nostr-sdk
            let authenticator = SignerAuthenticator::new(keys.clone());
            let client = Client::builder().authenticator(authenticator).build();

            for relay in DEFAULT_RELAYS {
                // Handle the Result explicitly. DO NOT use `?` here,
                // because `spawn` expects an async block that returns `()`.
                if let Err(e) = client.add_relay(relay).await {
                    error.set(Some(format!("Couldn't add relay {relay}: {e}")));
                }
            }

            client.connect().await;

            session.set(Some(Session { keys, client }));
            loading.set(false);
        });
    };

    rsx! {
        div { class: "flex items-center justify-center min-h-screen px-4",
            div { class: "w-full max-w-md bg-gray-900 border border-gray-800 rounded-2xl shadow-xl p-8",

                div { class: "flex flex-col items-center mb-6",
                    div { class: "w-14 h-14 rounded-full bg-purple-600 flex items-center justify-center text-2xl font-bold mb-3",
                        "N"
                    }
                    h1 { class: "text-2xl font-semibold", "Nostr Client" }
                    p { class: "text-gray-400 text-sm mt-1", "Dioxus + nostr-sdk" }
                }

                // Tab switcher
                div { class: "flex mb-6 bg-gray-800 rounded-lg p-1",
                    button {
                        class: format!(
                            "flex-1 py-2 rounded-md text-sm font-medium transition-colors {}",
                            if mode() == "login" { "bg-purple-600 text-white" } else { "text-gray-400 hover:text-gray-200" }
                        ),
                        onclick: move |_| { mode.set("login".to_string()); error.set(None); },
                        "Log In"
                    }
                    button {
                        class: format!(
                            "flex-1 py-2 rounded-md text-sm font-medium transition-colors {}",
                            if mode() == "create" { "bg-purple-600 text-white" } else { "text-gray-400 hover:text-gray-200" }
                        ),
                        onclick: move |_| { mode.set("create".to_string()); error.set(None); new_keys_preview.set(None); },
                        "Create Account"
                    }
                }

                if let Some(msg) = error() {
                    div { class: "mb-4 text-sm text-red-400 bg-red-950 border border-red-900 rounded-lg p-3",
                        "{msg}"
                    }
                }

                if mode() == "login" {
                    div { class: "space-y-4",
                        div {
                            label { class: "block text-sm text-gray-400 mb-1", "Private key" }
                            input {
                                class: "w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-purple-600",
                                r#type: "password",
                                placeholder: "nsec1... or hex secret key",
                                value: "{key_input}",
                                oninput: move |e| key_input.set(e.value()),
                            }
                        }
                        button {
                            class: "w-full bg-purple-600 hover:bg-purple-500 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg py-2.5 font-medium transition-colors",
                            disabled: loading(),
                            onclick: move |_| {
                                let input = key_input().trim().to_string();
                                if input.is_empty() {
                                    error.set(Some("Enter your private key first.".into()));
                                    return;
                                }
                                match Keys::parse(&input) {
                                    Ok(keys) => connect_and_enter(keys),
                                    Err(e) => error.set(Some(format!("That doesn't look like a valid key: {e}"))),
                                }
                            },
                            if loading() { "Connecting..." } else { "Log In" }
                        }
                        p { class: "text-xs text-gray-500 text-center",
                            "Your key never leaves this app — it's only used locally to sign events."
                        }
                    }
                } else {
                    div { class: "space-y-4",
                        if let Some(keys) = new_keys_preview() {
                            div { class: "space-y-3",
                                div { class: "bg-gray-800 rounded-lg p-3",
                                    p { class: "text-xs text-gray-400 mb-1", "Public key (npub) — share this" }
                                    p { class: "text-xs break-all text-green-400 font-mono",
                                        "{keys.public_key().to_bech32().unwrap_or_default()}"
                                    }
                                }
                                div { class: "bg-gray-800 rounded-lg p-3 border border-yellow-800",
                                    p { class: "text-xs text-gray-400 mb-1", "Private key (nsec) — keep this secret and save it now" }
                                    p { class: "text-xs break-all text-yellow-400 font-mono",
                                        "{keys.secret_key().to_bech32().unwrap_or_default()}"
                                    }
                                }
                                button {
                                    class: "w-full bg-purple-600 hover:bg-purple-500 disabled:opacity-50 rounded-lg py-2.5 font-medium transition-colors",
                                    disabled: loading(),
                                    onclick: move |_| connect_and_enter(keys.clone()),
                                    if loading() { "Connecting..." } else { "I've saved it — Continue" }
                                }
                            }
                        } else {
                            div { class: "space-y-4",
                                p { class: "text-sm text-gray-400",
                                    "This generates a brand-new Nostr identity (a keypair) on your device. Nobody can recover your private key if you lose it, so save it somewhere safe on the next screen."
                                }
                                button {
                                    class: "w-full bg-purple-600 hover:bg-purple-500 rounded-lg py-2.5 font-medium transition-colors",
                                    onclick: move |_| new_keys_preview.set(Some(Keys::generate())),
                                    "Generate New Keys"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
[dependencies]
dioxus = { workspace = true, features = ["router"] }
ui = { workspace = true }
nostr-sdk = "=0.45.0-alpha.4"
# Add this line to enable WebAssembly support for getrandom
getrandom = { version = "0.4", features = ["wasm_js"] }

Build environment
nostr-sdk = "=0.45.0-alpha.4"

It gives following error:

21:46:59 [web] panicked at /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/js-sys-0.3.103/src/futures/task/singlethread.rs:142:37:
RefCell already borrowed

Stack:

__wbg_get_imports/__wbg_new_3c07cdd0b848cbc8/<@http://localhost:8080/wasm/web.js:1113:25
logError@http://localhost:8080/wasm/web.js:2037:18
__wbg_new_3c07cdd0b848cbc8@http://localhost:8080/wasm/web.js:1112:57
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[29611]:0x567725
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[17513]:0x4e92fb
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[5363]:0x36ba99
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[22616]:0x52bc3f
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[6333]:0x3a1098
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[8904]:0x412bca
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[30130]:0x56901c
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[22785]:0x52d953
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[22788]:0x52d9dc
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[14978]:0x4bcf70
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[30135]:0x56904f
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[10125]:0x43ed74
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[3504]:0x2e6fd6
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[3314]:0x2d6bbd
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[15326]:0x4c3ae9
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[13819]:0x4a4845
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[13166]:0x49575a
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[18782]:0x4fc539
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[4234]:0x320c15
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[29399]:0x566734
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[24311]:0x53d180
_ZN12wasm_bindgen7convert8closures1_6invoke17h59a5801588d3aaa2E@http://localhost:8080/wasm/web.js:1874:22
real@http://localhost:8080/wasm/web.js:2087:20
VoidFunction*__wbg_get_imports/__wbg_queueMicrotask_6a09b7bc46549209/<@http://localhost:8080/wasm/web.js:1277:27
logError@http://localhost:8080/wasm/web.js:2037:18
__wbg_queueMicrotask_6a09b7bc46549209@http://localhost:8080/wasm/web.js:1276:68
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[30277]:0x5695b1
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[17673]:0x4ebad2
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[6230]:0x39bce2
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[22282]:0x5282ce
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[22279]:0x528244
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[24195]:0x53be4a
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[26823]:0x55439b
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[13517]:0x49dab8
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[20864]:0x517bdc
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[14372]:0x4b08e9
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[10975]:0x45a38a
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[16259]:0x4d4a91
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[20526]:0x513827
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[18437]:0x4f74d6
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[22094]:0x525f71
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[21410]:0x51e1a1
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[22090]:0x525eb3
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[22092]:0x525f0f
@http://localhost:8080/wasm/web_bg.wasm:wasm-function[11867]:0x474320
_ZN12wasm_bindgen7convert8closures1_6invoke17hfda2cc28ca75afffE@http://localhost:8080/wasm/web.js:1819:10
real@http://localhost:8080/wasm/web.js:2087:20

21:46:59 [web] %cERROR%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:799%c Connection failed. url = error = timeout;r.band;
21:46:59 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:636%c Reconnecting to 'wss://relay.nostr.band' relay in 13 secs
21:47:12 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:223%c Connecting to 'wss://relay.nostr.band'
21:47:27 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:636%c Reconnecting to 'wss://relay.nostr.band' relay in 19 secs
21:47:46 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:223%c Connecting to 'wss://relay.nostr.band'
21:48:01 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:636%c Reconnecting to 'wss://relay.nostr.band' relay in 17 secs
21:48:18 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:223%c Connecting to 'wss://relay.nostr.band'
21:48:33 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:636%c Reconnecting to 'wss://relay.nostr.band' relay in 31 secs
21:49:04 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:223%c Connecting to 'wss://relay.nostr.band'
21:49:19 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:636%c Reconnecting to 'wss://relay.nostr.band' relay in 31 secs
21:49:51 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:223%c Connecting to 'wss://relay.nostr.band'
21:50:07 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:636%c Reconnecting to 'wss://relay.nostr.band' relay in 43 secs
21:50:51 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:223%c Connecting to 'wss://relay.nostr.band'
21:51:06 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:636%c Reconnecting to 'wss://relay.nostr.band' relay in 41 secs
21:51:48 [web] %cDEBUG%c /home/amiya/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nostr-sdk-0.45.0-alpha.4/src/relay/inner.rs:223%c Connecting to 'wss://relay.nostr.band'
21:51:53 [dev] Build completed in 5.71s

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions