From 9ea9e41463b8d4e1d617665c276dfe51b4abd2af Mon Sep 17 00:00:00 2001 From: Halaprix Date: Thu, 23 May 2024 13:38:15 +0200 Subject: [PATCH] feat: add become summer user eligibility check (#289) --- .../update-rays-cron-function/src/index.ts | 117 +++++++++++++++++- 1 file changed, 115 insertions(+), 2 deletions(-) diff --git a/background-jobs/update-rays-cron-function/src/index.ts b/background-jobs/update-rays-cron-function/src/index.ts index 19acc2b234..7e57fc403b 100644 --- a/background-jobs/update-rays-cron-function/src/index.ts +++ b/background-jobs/update-rays-cron-function/src/index.ts @@ -13,6 +13,10 @@ const logger = new Logger({ serviceName: 'update-rays-cron-function' }) const LOCK_ID = 'update_points_lock' const LAST_RUN_ID = 'update_points_last_run' +const FOURTEEN_DAYS_IN_MILLISECONDS = 14 * 24 * 60 * 60 * 1000 +const SIXTY_DAYS_IN_MILLISECONDS = 60 * 24 * 60 * 60 +const THIRTY_DAYS_IN_MILLISECONDS = 30 * 24 * 60 * 60 * 1000 + enum EligibilityConditionType { POSITION_OPEN_TIME = 'POSITION_OPEN_TIME', POINTS_EXPIRED = 'POINTS_EXPIRED', @@ -168,7 +172,7 @@ export const handler = async ( const sortedPoints = points.sort((a, b) => a.positionId.localeCompare(b.positionId)) await checkMigrationEligibility(db, sortedPoints) - + await checkOpenedPositionEligibility(db, sortedPoints) // split points by 30 item chunks const chunkedPoints = [] for (let i = 0; i < sortedPoints.length; i += 30) { @@ -491,7 +495,7 @@ async function checkMigrationEligibility(db: Kysely, positionPoints: P .updateTable('pointsDistribution') .set({ eligibilityConditionId: null }) .where('id', '=', point.id) - .execute(); + .execute() await db .deleteFrom('eligibilityCondition') .where('id', '=', point.eligibilityConditionId) @@ -501,3 +505,112 @@ async function checkMigrationEligibility(db: Kysely, positionPoints: P }) } } + +/** + * This function checks the eligibility of opened positions. + * + * @param {Kysely} db - The Kysely database instance. + * @param {PositionPoints[]} positionPoints - An array of position points. + * + * The function performs the following steps: + * 1. Fetches all points distributions that have an associated eligibility condition but no associated position id. + * 2. For each user with such a points distribution: + * a. If the due date has not passed and the type of the eligibility condition is `BECOME_SUMMER_USER`: + * i. Fetches all positions of the user that are eligible for a check. A position is eligible if its net value is greater than or equal to 500, it was created before 14 days ago, and it belongs to the current user. + * ii. If there are no eligible positions, the function returns. + * iii. Otherwise, it fetches user points distributions of certain types. + * iv. Updates each of them by setting the `eligibilityConditionId` to `null` and multiplying the points by a multiplier that depends on when the oldest eligible position was created. + * b. If the due date has passed and the type of the eligibility condition is `BECOME_SUMMER_USER`, it deletes all points distributions and the eligibility condition associated with the user. + */ +async function checkOpenedPositionEligibility( + db: Kysely, + positionPoints: PositionPoints, +) { + // get all points distributions without an associated position id but with an eligibility condition + const existingUsersWithEligibilityCondition = await db + .selectFrom('pointsDistribution') + .where('eligibilityConditionId', '!=', null) + .where('positionId', '==', null) + .leftJoin( + 'eligibilityCondition', + 'eligibilityCondition.id', + 'pointsDistribution.eligibilityConditionId', + ) + .leftJoin('userAddress', 'userAddress.id', 'pointsDistribution.userAddressId') + .selectAll() + .execute() + + if (existingUsersWithEligibilityCondition.length > 0) { + existingUsersWithEligibilityCondition.forEach(async (user) => { + if ( + user.dueDate && + user.type == eligibilityConditions.BECOME_SUMMER_USER.type && + user.dueDate >= new Date() + ) { + // get all the positions of the user that are eligible for a check (exist in current points distribution) + const eligiblePositionsFromPointsAccrual = positionPoints + .filter( + (p) => + p.netValue >= 500 && + p.positionCreated * 1000 < Date.now() - FOURTEEN_DAYS_IN_MILLISECONDS, + ) + .filter((p) => p.user === user.address) + .sort((a, b) => a.positionCreated - b.positionCreated) + if (eligiblePositionsFromPointsAccrual.length == 0) { + return + } else { + const oldestEligiblePosition = eligiblePositionsFromPointsAccrual[0] + const becomeSummerUserMultiplier = getBecomeSummerUserMultiplier( + oldestEligiblePosition.positionCreated, + ) + + const pointsDistributions = await db + .selectFrom('pointsDistribution') + .where('userAddressId', '=', user.id) + .where((eb) => eb('type', '=', 'Snapshot_General').or('type', '=', 'Snapshot_Defi')) + .selectAll() + .execute() + pointsDistributions.forEach(async (pointsDistribution) => { + await db + .updateTable('pointsDistribution') + .set({ + eligibilityConditionId: null, + points: +pointsDistribution.points * becomeSummerUserMultiplier, + }) + .where('id', '=', pointsDistribution.id) + .execute() + }) + } + } else if ( + user.dueDate && + user.type == eligibilityConditions.BECOME_SUMMER_USER.type && + user.dueDate < new Date() + ) { + // if the due date is exceeded we delete all the points distribution and the eligibility condition + await db + .deleteFrom('pointsDistribution') + .where('eligibilityConditionId', '=', user.eligibilityConditionId) + .execute() + await db + .deleteFrom('eligibilityCondition') + .where('id', '=', user.eligibilityConditionId) + .execute() + } + }) + } +} + +/** + * Calculates the multiplier for becoming a summer user based on the position creation date. + * @param positionCreated The timestamp (in seconds) of when the position was created. + * @returns The multiplier value. + */ +function getBecomeSummerUserMultiplier(positionCreated: number) { + let multiplier = 1 + if (positionCreated * 1000 > Date.now() - THIRTY_DAYS_IN_MILLISECONDS) { + multiplier = 3 + } else if (positionCreated * 1000 > Date.now() - SIXTY_DAYS_IN_MILLISECONDS) { + multiplier = 2 + } + return multiplier +}