use hopper_runtime::error::ProgramError;
pub const REWARD_PRECISION: u128 = 1_000_000_000_000;
#[inline(always)]
pub fn update_reward_per_token(
reward_per_token: u128,
rewards_since_last: u64,
total_staked: u64,
) -> Result<u128, ProgramError> {
if total_staked == 0 {
return Ok(reward_per_token);
}
let increment = (rewards_since_last as u128)
.checked_mul(REWARD_PRECISION)
.ok_or(ProgramError::ArithmeticOverflow)?
/ total_staked as u128;
reward_per_token
.checked_add(increment)
.ok_or(ProgramError::ArithmeticOverflow)
}
#[inline(always)]
pub fn pending_rewards(
user_staked: u64,
reward_per_token: u128,
user_reward_debt: u128,
) -> Result<u64, ProgramError> {
let accumulated = (user_staked as u128)
.checked_mul(reward_per_token)
.ok_or(ProgramError::ArithmeticOverflow)?
/ REWARD_PRECISION;
let debt_normalized = user_reward_debt / REWARD_PRECISION;
let pending = accumulated.saturating_sub(debt_normalized);
if pending > u64::MAX as u128 {
return Err(ProgramError::ArithmeticOverflow);
}
Ok(pending as u64)
}
#[inline(always)]
pub fn update_reward_debt(user_staked: u64, reward_per_token: u128) -> u128 {
(user_staked as u128) * reward_per_token
}
#[inline(always)]
pub fn emission_rate(total_rewards: u64, duration_seconds: u64) -> Result<u64, ProgramError> {
if duration_seconds == 0 {
return Err(ProgramError::ArithmeticOverflow);
}
Ok(total_rewards / duration_seconds)
}
#[inline(always)]
pub fn rewards_earned(rate_per_second: u64, elapsed_seconds: u64) -> Result<u64, ProgramError> {
(rate_per_second as u128)
.checked_mul(elapsed_seconds as u128)
.ok_or(ProgramError::ArithmeticOverflow)
.and_then(|v| {
if v > u64::MAX as u128 {
Err(ProgramError::ArithmeticOverflow)
} else {
Ok(v as u64)
}
})
}