import browser from 'webextension-polyfill' import React, { useState, useEffect, useContext } from 'react' import { useNavigate } from 'react-router-dom'; import { toast } from 'react-toastify' import * as nip19 from 'nostr-tools/nip19' import * as nip49 from 'nostr-tools/nip49' import { privateKeyFromSeedWords, validateWords } from 'nostr-tools/nip06' import { bytesToHex, hexToBytes } from 'nostr-tools/utils' import { getPublicKey, finalizeEvent } from 'nostr-tools/pure' import { getSessionVault, setSessionVault } from '../common' import Modal from './Modal' import MainContext from '../contexts/MainContext' import { ensureRelayListExists } from '../helpers/outbox' const ImportAccountModal = ({ isOpen, onClose, callBack }) => { const { updateAccounts } = useContext(MainContext) const navigate = useNavigate() const [showModal, setShowModal] = useState(isOpen) const [prvKey, setPrvKey] = useState('') const [ncryptsecPassword, setNcryptsecPassword] = useState('') const [passphrase, setPassphrase] = useState('') const [isNcryptsec, setIsNcryptsec] = useState(false) const [isMnemonic, setIsMnemonic] = useState(false) useEffect(() => { setShowModal(isOpen) }, [isOpen]) const closeModal = () => { setShowModal(false) setPrvKey('') setNcryptsecPassword('') setPassphrase('') setIsNcryptsec(false) setIsMnemonic(false) onClose() } const handlePrvKeyChange = (e) => { const value = e.target.value setPrvKey(value) setIsNcryptsec(/^ncryptsec/.test(value)) setIsMnemonic(validateWords(value)) } // Helper to save vault via background encryption const saveVaultWithKey = async (vault, prvKeyHex, prvKeyBytes) => { const response = await browser.runtime.sendMessage({ type: 'ENCRYPT_VAULT', data: vault }) if (!response.success) { toast.error('Failed to save vault. Please unlock again.') return false } await browser.storage.local.set({ encryptedVault: response.encryptedData }) await setSessionVault(vault) await updateAccounts() // Publish default relay list if account doesn't have one const pubkey = getPublicKey(prvKeyBytes || hexToBytes(prvKeyHex)) ensureRelayListExists(pubkey, prvKeyBytes || hexToBytes(prvKeyHex), finalizeEvent) toast.success('Account imported successfully') callBack() navigate('/vault') return true } const importAccount = async (e) => { e.preventDefault() const vault = await getSessionVault() if (!vault) { toast.error('Session expired. Please unlock your vault again.') return } if (/^ncryptsec/.test(prvKey)) { try { if (!ncryptsecPassword) { toast.error('Please enter the decryption password') return false } const prvKeyBytes = await nip49.decrypt(prvKey, ncryptsecPassword) const prvKeyHex = bytesToHex(prvKeyBytes) const prvKeyExist = vault.importedAccounts.find(obj => obj['prvKey'] === prvKeyHex) const prvKeyExistInDerived = vault.accounts.find(obj => obj['prvKey'] === prvKeyHex) if (prvKeyExist || prvKeyExistInDerived) { toast.error('This private key already exists') setPrvKey('') setNcryptsecPassword('') setIsNcryptsec(false) return false } vault.importedAccounts.push({ prvKey: prvKeyHex }) vault.accountDefault = prvKeyHex await saveVaultWithKey(vault, prvKeyHex, prvKeyBytes) } catch (err) { toast.error('Invalid ncryptsec or wrong password') setNcryptsecPassword('') } } else if (/^nsec/.test(prvKey)) { try { let {type, data} = nip19.decode(prvKey) if (type === 'nsec') { const prvKeyHex = bytesToHex(data) const prvKeyExist = vault.importedAccounts.find(obj => obj['prvKey'] === prvKeyHex) const prvKeyExistInDerived = vault.accounts.find(obj => obj['prvKey'] === prvKeyHex) if (prvKeyExist || prvKeyExistInDerived) { toast.error('This private key already exists') setPrvKey('') return false } vault.importedAccounts.push({ prvKey: prvKeyHex }) vault.accountDefault = prvKeyHex await saveVaultWithKey(vault, prvKeyHex, data) } } catch (e) { toast.error('Invalid private key format') setPrvKey('') } } else if (validateWords(prvKey)) { try { const prvKeyBytes = privateKeyFromSeedWords(prvKey.trim(), passphrase) const prvKeyHex = bytesToHex(prvKeyBytes) const prvKeyExist = vault.importedAccounts.find(obj => obj['prvKey'] === prvKeyHex) const prvKeyExistInDerived = vault.accounts.find(obj => obj['prvKey'] === prvKeyHex) if (prvKeyExist || prvKeyExistInDerived) { toast.error('This private key already exists') setPrvKey('') setPassphrase('') setIsMnemonic(false) return false } vault.importedAccounts.push({ prvKey: prvKeyHex }) vault.accountDefault = prvKeyHex await saveVaultWithKey(vault, prvKeyHex, prvKeyBytes) } catch (e) { toast.error('Invalid mnemonic phrase') setPrvKey('') setPassphrase('') setIsMnemonic(false) } } else if (/^[0-9a-fA-F]+$/.test(prvKey)) { try { let prvKeyBytes = hexToBytes(prvKey) let prvKeyHex = bytesToHex(prvKeyBytes) const prvKeyExist = vault.importedAccounts.find(obj => obj['prvKey'] === prvKeyHex) const prvKeyExistInDerived = vault.accounts.find(obj => obj['prvKey'] === prvKeyHex) if (prvKeyExist || prvKeyExistInDerived) { toast.error('This private key already exists') setPrvKey('') return false } vault.importedAccounts.push({ prvKey: prvKeyHex }) vault.accountDefault = prvKeyHex await saveVaultWithKey(vault, prvKeyHex, prvKeyBytes) } catch (e) { toast.error('Invalid private key format') setPrvKey('') } } else { toast.error('Invalid format. Use nsec, ncryptsec, hex, or mnemonic phrase.') } setPrvKey('') } return (