import { nip19, verifyEvent } from 'nostr-tools' import { SignedEvent } from '../types' import axios from 'axios' import { NIP05_REGEX } from '../constants' /** * @param hexKey hex private or public key * @returns whether or not is key valid */ const validateHex = (hexKey: string) => { return hexKey.match(/^[a-f0-9]{64}$/) } /** * NPUB provided - it will convert NPUB to HEX * HEX provided - it will return HEX * * @param pubKey in NPUB, HEX format * @returns HEX format */ export const npubToHex = (pubKey: string): string | null => { // If key is NPUB if (pubKey.startsWith('npub1')) { try { return nip19.decode(pubKey).data as string } catch (error) { return null } } // valid hex key if (validateHex(pubKey)) return pubKey // Not a valid hex key return null } /** * If NSEC key is provided function will convert it to HEX * If HEX key Is provided function will validate the HEX format and return * * @param nsec or private key in HEX format */ export const nsecToHex = (nsec: string): string | null => { // If key is NSEC if (nsec.startsWith('nsec')) { try { return nip19.decode(nsec).data as string } catch (error) { return null } } // since it's not NSEC key we check if it's a valid hex key if (validateHex(nsec)) return nsec return null } export const hexToNpub = (hexPubkey: string): `npub1${string}` => { if (hexPubkey.startsWith('npub1')) return hexPubkey as `npub1${string}` return nip19.npubEncode(hexPubkey) } export const verifySignedEvent = (event: SignedEvent) => { const isGood = verifyEvent(event) if (!isGood) { throw new Error( 'Signed event did not pass verification. Check sig, id and pubkey.' ) } } /** * Function to query NIP-05 data and return the public key and relays. * * @param {string} nip05 - The NIP-05 identifier in the format "name@domain". * @returns {Promise<{ pubkey: string, relays: string[] }>} - The public key and an array of relay URLs. * @throws Will throw an error if the NIP-05 identifier is invalid or if there is an issue with the network request. */ export const queryNip05 = async ( nip05: string ): Promise<{ pubkey: string relays: string[] }> => { const match = nip05.match(NIP05_REGEX) // Throw an error if the NIP-05 identifier is invalid if (!match) throw new Error('Invalid nip05') // Destructure the match result, assigning default value '_' to name if not provided const [_, name = '_', domain] = match // Construct the URL to query the NIP-05 data const url = `https://${domain}/.well-known/nostr.json?name=${name}` // Perform the network request to get the NIP-05 data const res = await axios(url) .then((res) => { return res.data }) .catch((err) => { console.log('err :>> ', err) throw err }) // Extract the public key from the response data const pubkey = res.names[name] const relays: string[] = [] // If a public key is found if (pubkey) { // Function to add relays if they exist and are not empty const addRelays = (relayList?: string[]) => { if (relayList && relayList.length > 0) { relays.push(...relayList) } } // Check for user-specific relays in the NIP-46 section of the response data if (res.nip46) { addRelays(res.nip46[pubkey] as string[]) } // Check for user-specific relays in the relays section of the response data if not found in NIP-46 if (relays.length === 0 && res.relays) { addRelays(res.relays[pubkey] as string[]) } // If no user-specific relays are found, check for root user relays if (relays.length === 0) { const root = res.names['_'] if (root) { // Check for root user relays in both NIP-46 and relays sections addRelays(res.nip46?.[root] as string[]) addRelays(res.relays?.[root] as string[]) } } } // Return the public key and the array of relays return { pubkey, relays } } export const base64EncodeSignedEvent = (event: SignedEvent) => { try { const authEventSerialized = JSON.stringify(event) const token = btoa(authEventSerialized) return token } catch (error) { throw new Error('An error occurred in JSON.stringify of signedAuthEvent') } } export const base64DecodeAuthToken = (authToken: string): SignedEvent => { const decodedToken = atob(authToken) try { const signedEvent = JSON.parse(decodedToken) return signedEvent } catch (error) { throw new Error('An error occurred in JSON.parse of the auth token') } } /** * @param pubkey in hex or npub format * @returns robohash.org url for the avatar */ export const getRoboHashPicture = ( pubkey?: string, set: number = 1 ): string => { if (!pubkey) return '' const npub = hexToNpub(pubkey) return `https://robohash.org/${npub}.png?set=set${set}` }