diff --git a/src/providers/NostrProvider/index.tsx b/src/providers/NostrProvider/index.tsx
index d5ab46a..5eb7649 100644
--- a/src/providers/NostrProvider/index.tsx
+++ b/src/providers/NostrProvider/index.tsx
@@ -12,10 +12,13 @@ import storage from '@/services/storage.service'
import { ISigner, TAccount, TAccountPointer, TDraftEvent, TProfile, TRelayList } from '@/types'
import dayjs from 'dayjs'
import { Event, kinds } from 'nostr-tools'
+import * as nip19 from 'nostr-tools/nip19'
+import * as nip49 from 'nostr-tools/nip49'
import { createContext, useContext, useEffect, useState } from 'react'
import { BunkerSigner } from './bunker.signer'
import { Nip07Signer } from './nip-07.signer'
import { NsecSigner } from './nsec.signer'
+import { useTranslation } from 'react-i18next'
type TNostrContext = {
pubkey: string | null
@@ -26,8 +29,10 @@ type TNostrContext = {
account: TAccountPointer | null
accounts: TAccountPointer[]
nsec: string | null
+ ncryptsec: string | null
switchAccount: (account: TAccountPointer | null) => Promise
- nsecLogin: (nsec: string) => Promise
+ nsecLogin: (nsec: string, password?: string) => Promise
+ ncryptsecLogin: (ncryptsec: string) => Promise
nip07Login: () => Promise
bunkerLogin: (bunker: string) => Promise
removeAccount: (account: TAccountPointer) => void
@@ -56,9 +61,11 @@ export const useNostr = () => {
}
export function NostrProvider({ children }: { children: React.ReactNode }) {
+ const { t } = useTranslation()
const { toast } = useToast()
const [account, setAccount] = useState(null)
const [nsec, setNsec] = useState(null)
+ const [ncryptsec, setNcryptsec] = useState(null)
const [signer, setSigner] = useState(null)
const [openLoginDialog, setOpenLoginDialog] = useState(false)
const [profile, setProfile] = useState(null)
@@ -90,6 +97,14 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
const storedNsec = storage.getAccountNsec(account.pubkey)
if (storedNsec) {
setNsec(storedNsec)
+ } else {
+ setNsec(null)
+ }
+ const storedNcryptsec = storage.getAccountNcryptsec(account.pubkey)
+ if (storedNcryptsec) {
+ setNcryptsec(storedNcryptsec)
+ } else {
+ setNcryptsec(null)
}
const storedRelayListEvent = storage.getAccountRelayListEvent(account.pubkey)
if (storedRelayListEvent) {
@@ -171,12 +186,31 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
await loginWithAccountPointer(act)
}
- const nsecLogin = async (nsec: string) => {
+ const nsecLogin = async (nsec: string, password?: string) => {
const browserNsecSigner = new NsecSigner()
- const pubkey = browserNsecSigner.login(nsec)
+ const { type, data: privkey } = nip19.decode(nsec)
+ if (type !== 'nsec') {
+ throw new Error('invalid nsec')
+ }
+ const pubkey = browserNsecSigner.login(privkey)
+ if (password) {
+ const ncryptsec = nip49.encrypt(privkey, password)
+ return login(browserNsecSigner, { pubkey, signerType: 'ncryptsec', ncryptsec })
+ }
return login(browserNsecSigner, { pubkey, signerType: 'nsec', nsec })
}
+ const ncryptsecLogin = async (ncryptsec: string) => {
+ const password = prompt(t('Enter the password to decrypt your ncryptsec'))
+ if (!password) {
+ throw new Error('Password is required')
+ }
+ const privkey = nip49.decrypt(ncryptsec, password)
+ const browserNsecSigner = new NsecSigner()
+ const pubkey = browserNsecSigner.login(privkey)
+ return login(browserNsecSigner, { pubkey, signerType: 'ncryptsec', ncryptsec })
+ }
+
const nip07Login = async () => {
try {
const nip07Signer = new Nip07Signer()
@@ -228,6 +262,17 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
}
return login(browserNsecSigner, account)
}
+ } else if (account.signerType === 'ncryptsec') {
+ if (account.ncryptsec) {
+ const password = prompt(t('Enter the password to decrypt your ncryptsec'))
+ if (!password) {
+ return null
+ }
+ const privkey = nip49.decrypt(account.ncryptsec, password)
+ const browserNsecSigner = new NsecSigner()
+ browserNsecSigner.login(privkey)
+ return login(browserNsecSigner, account)
+ }
} else if (account.signerType === 'nip-07') {
const nip07Signer = new Nip07Signer()
return login(nip07Signer, account)
@@ -334,8 +379,10 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
.getAccounts()
.map((act) => ({ pubkey: act.pubkey, signerType: act.signerType })),
nsec,
+ ncryptsec,
switchAccount,
nsecLogin,
+ ncryptsecLogin,
nip07Login,
bunkerLogin,
removeAccount,
diff --git a/src/providers/NostrProvider/nsec.signer.ts b/src/providers/NostrProvider/nsec.signer.ts
index 1f8c07b..519c3e4 100644
--- a/src/providers/NostrProvider/nsec.signer.ts
+++ b/src/providers/NostrProvider/nsec.signer.ts
@@ -5,14 +5,20 @@ export class NsecSigner implements ISigner {
private privkey: Uint8Array | null = null
private pubkey: string | null = null
- login(nsec: string) {
- const { type, data } = nip19.decode(nsec)
- if (type !== 'nsec') {
- throw new Error('invalid nsec')
+ login(nsecOrPrivkey: string | Uint8Array) {
+ let privkey
+ if (typeof nsecOrPrivkey === 'string') {
+ const { type, data } = nip19.decode(nsecOrPrivkey)
+ if (type !== 'nsec') {
+ throw new Error('invalid nsec')
+ }
+ privkey = data
+ } else {
+ privkey = nsecOrPrivkey
}
- this.privkey = data
- this.pubkey = nGetPublicKey(data)
+ this.privkey = privkey
+ this.pubkey = nGetPublicKey(privkey)
return this.pubkey
}
diff --git a/src/services/storage.service.ts b/src/services/storage.service.ts
index 637d01b..322de21 100644
--- a/src/services/storage.service.ts
+++ b/src/services/storage.service.ts
@@ -156,6 +156,13 @@ class StorageService {
return account?.nsec
}
+ getAccountNcryptsec(pubkey: string) {
+ const account = this.accounts.find(
+ (act) => act.pubkey === pubkey && act.signerType === 'ncryptsec'
+ )
+ return account?.ncryptsec
+ }
+
addAccount(account: TAccount) {
if (this.accounts.find((act) => isSameAccount(act, account))) {
return
diff --git a/src/types.ts b/src/types.ts
index c0dc785..ae51a35 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -53,11 +53,12 @@ export interface ISigner {
signEvent: (draftEvent: TDraftEvent) => Promise
}
-export type TSignerType = 'nsec' | 'nip-07' | 'bunker' | 'browser-nsec'
+export type TSignerType = 'nsec' | 'nip-07' | 'bunker' | 'browser-nsec' | 'ncryptsec'
export type TAccount = {
pubkey: string
signerType: TSignerType
+ ncryptsec?: string
nsec?: string
bunker?: string
bunkerClientSecretKey?: string