import type { Payroll } from "@quarryprotocol/quarry-sdk"; import { SECONDS_PER_YEAR } from "@quarryprotocol/quarry-sdk"; import { MAX_U64 } from "@saberhq/token-utils"; import type BN from "bn.js"; const maxU64 = parseFloat(MAX_U64.toString()); const secondsPerYear = parseFloat(SECONDS_PER_YEAR.toString()); /** * {@link Payroll} using floats instead of BN. Not precise. */ export class FastPayroll { constructor( readonly famineTs: number, readonly lastCheckpointTs: number, readonly annualRewardsRate: number, readonly rewardsPerTokenStored: number, readonly totalTokensDeposited: number ) {} static fromPayroll(payroll: Payroll): FastPayroll { return new FastPayroll( parseFloat(payroll.famineTs.toString()), parseFloat(payroll.lastCheckpointTs.toString()), parseFloat(payroll.annualRewardsRate.toString()), parseFloat(payroll.rewardsPerTokenStored.toString()), parseFloat(payroll.totalTokensDeposited.toString()) ); } /** * Calculates the amount of tokens that this user can receive. * @param currentTs * @returns */ calculateRewardPerToken(currentTs: number): number { if (this.totalTokensDeposited === 0) { return this.rewardsPerTokenStored; } const lastTimeRewardsApplicable = Math.min(currentTs, this.famineTs); const timeWorked = Math.max( 0, lastTimeRewardsApplicable - this.lastCheckpointTs ); const reward = (((timeWorked * maxU64) / secondsPerYear) * this.annualRewardsRate) / this.totalTokensDeposited; return this.rewardsPerTokenStored + reward; } /** * Calculates the amount of tokens that this user can claim. * @param currentTs * @param tokensDeposited * @param rewardsPerTokenPaid * @param rewardsEarned * @returns */ calculateRewardsEarned( currentTs: number, tokensDeposited: BN, rewardsPerTokenPaid: BN, rewardsEarned: BN ): number { const netNewRewards = this.calculateRewardPerToken(currentTs) - parseFloat(rewardsPerTokenPaid.toString()); const earnedRewards = (parseFloat(tokensDeposited.toString()) * netNewRewards) / maxU64; return Math.floor(earnedRewards + parseFloat(rewardsEarned.toString())); } }