use anchor_lang::prelude::*;
pub const STRM_TREASURY: &str = "5SEpbdjFK5FxwTvfsGMXVQTD2v4M2c5tyRTxhdsPkgDw";
pub const WITHDRAWOR_ADDRESS: &str = "wdrwhnCv4pzW8beKsbPa4S2UDZrXenjg16KJdKSpb5u";
pub const FEE_ORACLE_ADDRESS: &str = "B743wFVk2pCYhV91cn287e1xY7f1vt4gdY48hhNiuQmT";
pub const ESCROW_SEED_PREFIX: &[u8] = b"strm";
pub const METADATA_LEN: usize = 1104;
pub const STREAMFLOW_PROGRAM_ID: &str = "strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m";
pub const STREAMFLOW_DEVNET_PROGRAM_ID: &str = "HqDGZjaVRXJ9MGRQEw7qDc2rAr6iH1n1kAQdCZaCMfMZ";
pub fn find_escrow_account(seed: &[u8], pid: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[ESCROW_SEED_PREFIX, seed], pid)
}
pub fn calculate_fee_from_amount(amount: u64, percentage: f32) -> u64 {
if percentage <= 0.0 {
return 0;
}
let precision_factor: f32 = 1000000.0;
let factor = (percentage / 100.0 * precision_factor) as u128;
(amount as u128 * factor / precision_factor as u128) as u64
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]
#[repr(C)]
pub struct CreateParams {
pub start_time: u64,
pub net_amount_deposited: u64,
pub period: u64,
pub amount_per_period: u64,
pub cliff: u64,
pub cliff_amount: u64,
pub cancelable_by_sender: bool,
pub cancelable_by_recipient: bool,
pub automatic_withdrawal: bool,
pub transferable_by_sender: bool,
pub transferable_by_recipient: bool,
pub can_topup: bool,
pub stream_name: [u8; 64],
pub withdraw_frequency: u64,
pub ghost: u32,
pub pausable: bool,
pub can_update_rate: bool,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]
#[repr(C)]
pub struct Contract {
pub magic: u64,
pub version: u8,
pub created_at: u64,
pub amount_withdrawn: u64,
pub canceled_at: u64,
pub end_time: u64,
pub last_withdrawn_at: u64,
pub sender: Pubkey,
pub sender_tokens: Pubkey,
pub recipient: Pubkey,
pub recipient_tokens: Pubkey,
pub mint: Pubkey,
pub escrow_tokens: Pubkey,
pub streamflow_treasury: Pubkey,
pub streamflow_treasury_tokens: Pubkey,
pub streamflow_fee_total: u64,
pub streamflow_fee_withdrawn: u64,
pub streamflow_fee_percent: f32,
pub partner: Pubkey,
pub partner_tokens: Pubkey,
pub partner_fee_total: u64,
pub partner_fee_withdrawn: u64,
pub partner_fee_percent: f32,
pub ix: CreateParams,
pub ix_padding: Vec<u8>,
pub closed: bool,
pub current_pause_start: u64,
pub pause_cumulative: u64,
pub last_rate_change_time: u64,
pub funds_unlocked_at_last_rate_change: u64,
}
impl Contract {
pub fn start_time(&self) -> u64 {
if self.ix.cliff > 0 {
self.ix.cliff
} else {
self.ix.start_time
}
}
pub fn effective_start_time(&self) -> u64 {
std::cmp::max(self.last_rate_change_time, self.start_time())
}
pub fn pause_time(&self, now: u64) -> u64 {
if self.current_pause_start > 0 {
return self.pause_cumulative + now - self.current_pause_start;
}
self.pause_cumulative
}
pub fn vested_available(&self, now: u64) -> u64 {
let start = self.start_time();
if self.current_pause_start < start && self.current_pause_start != 0 {
return 0;
}
let effective_stream_duration = now - self.effective_start_time() - self.pause_time(now);
let effective_periods_passed = effective_stream_duration / self.ix.period;
let effective_amount_available = effective_periods_passed * self.ix.amount_per_period;
effective_amount_available + self.funds_unlocked_at_last_rate_change
}
pub fn available_to_claim(&self, now: u64, fee_percentage: f32) -> u64 {
if self.start_time() > now
|| self.ix.net_amount_deposited == 0
|| self.ix.net_amount_deposited == self.amount_withdrawn
{
return 0;
}
if now >= self.end_time && self.current_pause_start == 0 {
return self.ix.net_amount_deposited - self.amount_withdrawn;
}
let vested_available =
calculate_fee_from_amount(self.vested_available(now), fee_percentage);
let cliff_available = calculate_fee_from_amount(self.cliff_available(now), fee_percentage);
let sum_available = vested_available + cliff_available;
sum_available - self.amount_withdrawn
}
pub fn cliff_available(&self, now: u64) -> u64 {
if self.current_pause_start < self.ix.cliff && self.current_pause_start != 0 {
return 0;
}
if now < self.ix.cliff {
return 0;
}
self.ix.cliff_amount
}
}