Skip to content

Commit

Permalink
feat: add become summer user eligibility check (#289)
Browse files Browse the repository at this point in the history
  • Loading branch information
halaprix authored May 23, 2024
1 parent 9c081c0 commit 9ea9e41
Showing 1 changed file with 115 additions and 2 deletions.
117 changes: 115 additions & 2 deletions background-jobs/update-rays-cron-function/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -491,7 +495,7 @@ async function checkMigrationEligibility(db: Kysely<Database>, positionPoints: P
.updateTable('pointsDistribution')
.set({ eligibilityConditionId: null })
.where('id', '=', point.id)
.execute();
.execute()
await db
.deleteFrom('eligibilityCondition')
.where('id', '=', point.eligibilityConditionId)
Expand All @@ -501,3 +505,112 @@ async function checkMigrationEligibility(db: Kysely<Database>, positionPoints: P
})
}
}

/**
* This function checks the eligibility of opened positions.
*
* @param {Kysely<Database>} 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<Database>,
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
}

0 comments on commit 9ea9e41

Please sign in to comment.