import { EventTemplate, Filter, kinds, nip19 } from 'nostr-tools' import { compareObjects, queryNip05, unixNow } from '.' import { MetadataController, NostrController, relayController } from '../controllers' import { NostrJoiningBlock, RelayInfoObject } from '../types' import NDK, { NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk' import store from '../store/store' import { setRelayInfoAction } from '../store/actions' export const getNostrJoiningBlockNumber = async ( hexKey: string ): Promise => { const metadataController = new MetadataController() const relaySet = await metadataController.findRelayListMetadata(hexKey) const userRelays: string[] = [] // find user's relays if (relaySet.write.length > 0) { userRelays.push(...relaySet.write) } else { const metadata = await metadataController.findMetadata(hexKey) if (!metadata) return null const metadataContent = metadataController.extractProfileMetadataContent(metadata) if (metadataContent?.nip05) { const nip05Profile = await queryNip05(metadataContent.nip05) if (nip05Profile && nip05Profile.pubkey === hexKey) { userRelays.push(...nip05Profile.relays) } } } if (userRelays.length === 0) return null // filter for finding user's first kind 0 event const eventFilter: Filter = { kinds: [kinds.Metadata], authors: [hexKey] } // find user's kind 0 event published on user's relays const event = await relayController.fetchEvent(eventFilter, userRelays) if (event) { const { created_at } = event // initialize job request const jobEventTemplate: EventTemplate = { content: '', created_at: unixNow(), kind: 68001, tags: [ ['i', `${created_at * 1000}`], ['j', 'blockChain-block-number'] ] } const nostrController = NostrController.getInstance() // sign job request event const jobSignedEvent = await nostrController.signEvent(jobEventTemplate) const relays = [ 'wss://relay.damus.io', 'wss://relay.primal.net', 'wss://relayable.org' ] await relayController.publish(jobSignedEvent, relays).catch((err) => { console.error( 'Error occurred in publish blockChain-block-number DVM job', err ) }) const subscribeWithTimeout = ( subscription: NDKSubscription, timeoutMs: number ): Promise => { return new Promise((resolve, reject) => { const eventHandler = (event: NDKEvent) => { subscription.stop() resolve(event.content) } subscription.on('event', eventHandler) // Set up a timeout to stop the subscription after a specified time const timeout = setTimeout(() => { subscription.stop() // Stop the subscription reject(new Error('Subscription timed out')) // Reject the promise with a timeout error }, timeoutMs) // Handle subscription close event subscription.on('close', () => clearTimeout(timeout)) }) } const dvmNDK = new NDK({ explicitRelayUrls: relays }) await dvmNDK.connect(2000) // filter for getting DVM job's result const sub = dvmNDK.subscribe({ kinds: [68002 as number], '#e': [jobSignedEvent.id], '#p': [jobSignedEvent.pubkey] }) // asynchronously get block number from dvm job with 20 seconds timeout const dvmJobResult = await subscribeWithTimeout(sub, 20000) const encodedEventPointer = nip19.neventEncode({ id: event.id, relays: userRelays, author: event.pubkey, kind: event.kind }) return { block: parseInt(dvmJobResult), encodedEventPointer } } return null } /** * Sets information about relays into relays.info app state. * @param relayURIs - relay URIs to get information about */ export const getRelayInfo = async (relayURIs: string[]) => { // initialize job request const jobEventTemplate: EventTemplate = { content: '', created_at: unixNow(), kind: 68001, tags: [ ['i', `${JSON.stringify(relayURIs)}`], ['j', 'relay-info'] ] } const nostrController = NostrController.getInstance() // sign job request event const jobSignedEvent = await nostrController.signEvent(jobEventTemplate) const relays = [ 'wss://relay.damus.io', 'wss://relay.primal.net', 'wss://relayable.org' ] // publish job request await relayController.publish(jobSignedEvent, relays) console.log('jobSignedEvent :>> ', jobSignedEvent) const subscribeWithTimeout = ( subscription: NDKSubscription, timeoutMs: number ): Promise => { return new Promise((resolve, reject) => { const eventHandler = (event: NDKEvent) => { subscription.stop() resolve(event.content) } subscription.on('event', eventHandler) // Set up a timeout to stop the subscription after a specified time const timeout = setTimeout(() => { subscription.stop() // Stop the subscription reject(new Error('Subscription timed out')) // Reject the promise with a timeout error }, timeoutMs) // Handle subscription close event subscription.on('close', () => clearTimeout(timeout)) }) } const dvmNDK = new NDK({ explicitRelayUrls: relays }) await dvmNDK.connect(2000) // filter for getting DVM job's result const sub = dvmNDK.subscribe({ kinds: [68002 as number], '#e': [jobSignedEvent.id], '#p': [jobSignedEvent.pubkey] }) // asynchronously get block number from dvm job with 20 seconds timeout const dvmJobResult = await subscribeWithTimeout(sub, 20000) if (!dvmJobResult) { return Promise.reject(`Relay(s) information wasn't received`) } let relaysInfo: RelayInfoObject try { relaysInfo = JSON.parse(dvmJobResult) } catch (error) { return Promise.reject(`Invalid relay(s) information.`) } if ( relaysInfo && !compareObjects(store.getState().relays?.info, relaysInfo) ) { store.dispatch(setRelayInfoAction(relaysInfo)) } }