use crate::*;
use num_traits::ToPrimitive;
#[account]
#[derive(Copy, Debug, Default)]
pub struct Stream {
pub mint: Pubkey,
pub bump: u8,
pub revoker: Pubkey,
pub crate_token: Pubkey,
pub underlying_mint: Pubkey,
pub underlying_tokens: Pubkey,
pub initial_amount: u64,
pub redeemed_amount: u64,
pub start_ts: i64,
pub cliff_ts: i64,
pub end_ts: i64,
}
impl Stream {
pub fn available_for_withdrawal(&self, current_ts: i64, remaining_amount: u64) -> Option<u64> {
Some(self.outstanding_released(current_ts)?.min(remaining_amount))
}
pub fn outstanding_released(&self, current_ts: i64) -> Option<u64> {
self.total_released(current_ts)?
.checked_sub(self.redeemed_amount)
}
pub fn total_released(&self, current_ts: i64) -> Option<u64> {
if current_ts <= self.cliff_ts {
return Some(0);
}
if current_ts >= self.end_ts {
return Some(self.initial_amount);
}
let current_ts = current_ts.to_u64()?;
let start_ts = self.start_ts.to_u64()?;
let end_ts = self.end_ts.to_u64()?;
if current_ts <= start_ts {
return Some(0);
}
if current_ts >= end_ts {
return Some(self.initial_amount);
}
(current_ts.checked_sub(start_ts)? as u128)
.checked_mul(self.initial_amount.into())?
.checked_div(end_ts.checked_sub(start_ts)?.into())?
.to_u64()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_linear_unlock_not_started() {
let release = &mut Stream::default();
release.start_ts = 100_000;
release.end_ts = 200_000;
release.initial_amount = 1_000_000;
let amt = release.total_released(90_000).unwrap();
assert_eq!(amt, 0);
}
#[test]
fn test_linear_unlock_finished() {
let release = &mut Stream::default();
release.start_ts = 100_000;
release.end_ts = 200_000;
release.initial_amount = 1_000_000;
let amt = release.total_released(290_000).unwrap();
assert_eq!(amt, 1_000_000);
}
#[test]
fn test_linear_unlock_halfway() {
let release = &mut Stream::default();
release.start_ts = 100_000;
release.end_ts = 200_000;
release.initial_amount = 1_000_000;
let amt = release.total_released(150_000).unwrap();
assert_eq!(amt, 500_000);
}
}