1use anchor_lang::prelude::*;
2
3pub const STRM_TREASURY: &str = "5SEpbdjFK5FxwTvfsGMXVQTD2v4M2c5tyRTxhdsPkgDw";
5pub const WITHDRAWOR_ADDRESS: &str = "wdrwhnCv4pzW8beKsbPa4S2UDZrXenjg16KJdKSpb5u";
7pub const FEE_ORACLE_ADDRESS: &str = "B743wFVk2pCYhV91cn287e1xY7f1vt4gdY48hhNiuQmT";
9
10pub const ESCROW_SEED_PREFIX: &[u8] = b"strm";
12pub const METADATA_LEN: usize = 1104;
14
15pub const STREAMFLOW_PROGRAM_ID: &str = "strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m";
17pub const STREAMFLOW_DEVNET_PROGRAM_ID: &str = "HqDGZjaVRXJ9MGRQEw7qDc2rAr6iH1n1kAQdCZaCMfMZ";
18
19pub fn find_escrow_account(seed: &[u8], pid: &Pubkey) -> (Pubkey, u8) {
20 Pubkey::find_program_address(&[ESCROW_SEED_PREFIX, seed], pid)
21}
22
23pub fn calculate_fee_from_amount(amount: u64, percentage: f32) -> u64 {
25 if percentage <= 0.0 {
26 return 0;
27 }
28 let precision_factor: f32 = 1000000.0;
29 let factor = (percentage / 100.0 * precision_factor) as u128;
31
32 (amount as u128 * factor / precision_factor as u128) as u64
34}
35
36#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]
38#[repr(C)]
39pub struct CreateParams {
40 pub start_time: u64,
42 pub net_amount_deposited: u64,
44 pub period: u64,
46 pub amount_per_period: u64,
48 pub cliff: u64,
50 pub cliff_amount: u64,
52 pub cancelable_by_sender: bool,
54 pub cancelable_by_recipient: bool,
56 pub automatic_withdrawal: bool,
58 pub transferable_by_sender: bool,
60 pub transferable_by_recipient: bool,
62 pub can_topup: bool,
64 pub stream_name: [u8; 64],
66 pub withdraw_frequency: u64,
68 pub ghost: u32,
70 pub pausable: bool,
72 pub can_update_rate: bool,
74}
75
76#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]
87#[repr(C)]
88pub struct Contract {
89 pub magic: u64,
91 pub version: u8,
93 pub created_at: u64,
95 pub amount_withdrawn: u64,
97 pub canceled_at: u64,
99 pub end_time: u64,
103 pub last_withdrawn_at: u64,
105 pub sender: Pubkey,
107 pub sender_tokens: Pubkey,
109 pub recipient: Pubkey,
111 pub recipient_tokens: Pubkey,
113 pub mint: Pubkey,
115 pub escrow_tokens: Pubkey,
117 pub streamflow_treasury: Pubkey,
119 pub streamflow_treasury_tokens: Pubkey,
121 pub streamflow_fee_total: u64,
123 pub streamflow_fee_withdrawn: u64,
125 pub streamflow_fee_percent: f32,
127 pub partner: Pubkey,
129 pub partner_tokens: Pubkey,
131 pub partner_fee_total: u64,
133 pub partner_fee_withdrawn: u64,
135 pub partner_fee_percent: f32,
137 pub ix: CreateParams,
139 pub ix_padding: Vec<u8>,
141 pub closed: bool,
143 pub current_pause_start: u64,
145 pub pause_cumulative: u64,
147 pub last_rate_change_time: u64,
150 pub funds_unlocked_at_last_rate_change: u64,
152}
153
154impl Contract {
155 pub fn start_time(&self) -> u64 {
156 if self.ix.cliff > 0 {
157 self.ix.cliff
158 } else {
159 self.ix.start_time
160 }
161 }
162
163 pub fn effective_start_time(&self) -> u64 {
164 std::cmp::max(self.last_rate_change_time, self.start_time())
165 }
166
167 pub fn pause_time(&self, now: u64) -> u64 {
168 if self.current_pause_start > 0 {
169 return self.pause_cumulative + now - self.current_pause_start;
170 }
171 self.pause_cumulative
172 }
173
174 pub fn vested_available(&self, now: u64) -> u64 {
176 let start = self.start_time();
177 if self.current_pause_start < start && self.current_pause_start != 0 {
179 return 0;
180 }
181 let effective_stream_duration = now - self.effective_start_time() - self.pause_time(now);
183 let effective_periods_passed = effective_stream_duration / self.ix.period;
184 let effective_amount_available = effective_periods_passed * self.ix.amount_per_period;
185
186 effective_amount_available + self.funds_unlocked_at_last_rate_change
187 }
188
189 pub fn available_to_claim(&self, now: u64, fee_percentage: f32) -> u64 {
190 if self.start_time() > now
191 || self.ix.net_amount_deposited == 0
192 || self.ix.net_amount_deposited == self.amount_withdrawn
193 {
194 return 0;
195 }
196 if now >= self.end_time && self.current_pause_start == 0 {
197 return self.ix.net_amount_deposited - self.amount_withdrawn;
198 }
199
200 let vested_available =
201 calculate_fee_from_amount(self.vested_available(now), fee_percentage);
202 let cliff_available = calculate_fee_from_amount(self.cliff_available(now), fee_percentage);
203 let sum_available = vested_available + cliff_available;
204 sum_available - self.amount_withdrawn
205 }
206
207 pub fn cliff_available(&self, now: u64) -> u64 {
208 if self.current_pause_start < self.ix.cliff && self.current_pause_start != 0 {
209 return 0;
210 }
211 if now < self.ix.cliff {
212 return 0;
213 }
214 self.ix.cliff_amount
215 }
216}