Delegator_delegatorCron.js

import Delegator from '../models/Delegator.js'
import schedule from 'node-schedule'
import { fetchLatestEpoch } from '../repository/network.repository.js'
import { findAPRValue, findDelegators } from '../utils/index.js'
import { createDelegateTransaction } from './utils.js'
import logger from '../logger/logger.js'
import Transaction from '../models/Transaction.js'

/**
 * Scheduled job to manage delegators.
 */
const delegatorCron = async () => {
    logger.info('Delegator cron started')

    // Schedule a job to run every 30 minutes
    return schedule.scheduleJob('*/30 * * * *', async () => {
        await delegatorJob()
    })
}

/**
 * Job to create new delegators and update already populated ones.
 */
const delegatorJob = async () => {
    try {
        const delegators = await findDelegators()
        const latestEpoch = await fetchLatestEpoch()
        console.table(delegators)

        // Loop over each delegator create or update their entry
        for (const delegator of delegators) {
            await processDelegator(delegator, latestEpoch)
        }
        await processUnstaking(delegators, latestEpoch)

        logger.info('Delegator cron job successfully executed')
    } catch (e) {
        logger.error(
            `Delegator cron job failed [${new Date().getTime()}]: ${e.message}`
        )
    }
}

/**
 * Process individual delegator.
 */
const processDelegator = async (delegator, latestEpoch) => {
    const { pubkey, activationEpoch, deactivationEpoch, stake } = delegator

    const storedDelegator = await Delegator.findOne({
        delegatorId: pubkey,
    })

    if (!storedDelegator) {
        const unstaked = latestEpoch >= deactivationEpoch
        const apr = unstaked ? 0 : await findAPRValue(pubkey, latestEpoch)
        await Delegator.create({
            delegatorId: pubkey,
            timestamp: new Date().getTime(),
            unstaked,
            apr,
            stakedAmount: stake,
            activationEpoch,
            unstakedEpoch: deactivationEpoch,
        })

        logger.info(`Created delegator: ${pubkey}`)
        await createDelegateTransaction(pubkey, stake)
        return
    }

    if (!(await Transaction.findOne({ delegatorId: pubkey })))
        await createDelegateTransaction(pubkey, stake)

    if (latestEpoch > deactivationEpoch) {
        if (
            storedDelegator.unstaked &&
            storedDelegator.unstakedEpoch === deactivationEpoch
        )
            return
        storedDelegator.unstaked = true
        storedDelegator.unstakedEpoch = deactivationEpoch
        await storedDelegator.save()

        logger.info(`Unstaked delegator: ${pubkey}`)
        return
    }

    const apr = await findAPRValue(pubkey, latestEpoch)
    storedDelegator.apr = apr
    await storedDelegator.save()
    logger.info(`APR updated for delegator: ${pubkey}`)
}

/**
 * Handle unstaking based on epoch conditions.
 */
const processUnstaking = async (delegators, latestEpoch) => {
    const delegatorPubKeys = delegators.map((delegator) => delegator.pubkey)

    await Delegator.updateMany(
        { delegatorId: { $nin: delegatorPubKeys } },
        {
            unstaked: true,
            unstakedTimestamp: new Date().getTime(),
            unstakedEpoch: latestEpoch - 1,
        }
    )
    logger.info(`Unstaking processed for delegators removed from the API`)
}

export default delegatorCron