You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.3 KiB
85 lines
2.3 KiB
// Shared signer manager for Nostr signers (remote and extension) |
|
import { SimplePool } from 'nostr-tools'; |
|
import { BunkerSigner } from 'nostr-tools/nip46'; |
|
|
|
const REMOTE_SIGNER_KEY = 'amber_remote_signer'; |
|
|
|
let remoteSigner = null; |
|
let remoteSignerPromise = null; |
|
let remoteSignerPool = null; |
|
|
|
export async function getSigner() { |
|
// If remote signer session is active, use it |
|
const session = getRemoteSignerSession(); |
|
if (session) { |
|
if (remoteSigner) return remoteSigner; |
|
if (remoteSignerPromise) return remoteSignerPromise; |
|
|
|
remoteSignerPromise = createRemoteSigner(session) |
|
.then(signer => { |
|
remoteSigner = signer; |
|
return signer; |
|
}) |
|
.catch(error => { |
|
// Reset promise on failure so next call can retry |
|
remoteSignerPromise = null; |
|
throw error; |
|
}); |
|
return remoteSignerPromise; |
|
} |
|
// Fallback to browser extension |
|
if (window.nostr && typeof window.nostr.signEvent === 'function') { |
|
return window.nostr; |
|
} |
|
throw new Error('No signer available'); |
|
} |
|
|
|
export function setRemoteSignerSession(session) { |
|
localStorage.setItem(REMOTE_SIGNER_KEY, JSON.stringify(session)); |
|
} |
|
|
|
export function clearRemoteSignerSession() { |
|
localStorage.removeItem(REMOTE_SIGNER_KEY); |
|
remoteSigner = null; |
|
remoteSignerPromise = null; |
|
if (remoteSignerPool) { |
|
try { remoteSignerPool.close?.([]); } catch (_) {} |
|
remoteSignerPool = null; |
|
} |
|
} |
|
|
|
export function getRemoteSignerSession() { |
|
const raw = localStorage.getItem(REMOTE_SIGNER_KEY); |
|
if (!raw) return null; |
|
try { |
|
return JSON.parse(raw); |
|
} catch { |
|
return null; |
|
} |
|
} |
|
|
|
async function createRemoteSigner(session) { |
|
remoteSignerPool = new SimplePool(); |
|
|
|
// Add timeout to prevent hanging indefinitely |
|
const timeoutPromise = new Promise((_, reject) => { |
|
setTimeout(() => reject(new Error('Remote signer connection timeout')), 10000); |
|
}); |
|
|
|
try { |
|
return await Promise.race([ |
|
BunkerSigner.fromURI(session.privkey, session.uri, { pool: remoteSignerPool }), |
|
timeoutPromise |
|
]); |
|
} catch (error) { |
|
// Clean up on error |
|
if (remoteSignerPool) { |
|
try { remoteSignerPool.close?.([]); } catch (_) {} |
|
remoteSignerPool = null; |
|
} |
|
remoteSigner = null; |
|
remoteSignerPromise = null; |
|
throw error; |
|
} |
|
} |
|
|
|
|