1use crate::consts::STAKE_VESTING_SLOTS;
4use crate::state::VestingTranche;
5
6pub fn stake_unlocked_amount(amount: u64, start_slot: u64, now: u64) -> u64 {
8 if amount == 0 {
9 return 0;
10 }
11 let duration = STAKE_VESTING_SLOTS;
12 if now <= start_slot {
13 return 0;
14 }
15 let end = start_slot.saturating_add(duration);
16 if now >= end {
17 return amount;
18 }
19 let elapsed = now.saturating_sub(start_slot);
20 ((amount as u128) * (elapsed as u128) / (duration as u128)) as u64
21}
22
23pub fn stake_claimable_from_tranche(t: &VestingTranche, now: u64) -> u64 {
25 if t.amount == 0 {
26 return 0;
27 }
28 let unlocked = stake_unlocked_amount(t.amount, t.start_slot, now);
29 unlocked.saturating_sub(t.withdrawn)
30}
31
32pub fn stake_total_claimable(vesting: &[VestingTranche; 8], now: u64) -> u64 {
33 let mut sum = 0u64;
34 for t in vesting.iter() {
35 sum = sum.saturating_add(stake_claimable_from_tranche(t, now));
36 }
37 sum
38}
39
40pub fn stake_apply_withdraw(vesting: &mut [VestingTranche; 8], now: u64, mut amount: u64) {
42 for t in vesting.iter_mut() {
43 if amount == 0 {
44 break;
45 }
46 if t.amount == 0 {
47 continue;
48 }
49 let claimable = stake_claimable_from_tranche(t, now);
50 let take = amount.min(claimable);
51 t.withdrawn = t.withdrawn.saturating_add(take);
52 amount = amount.saturating_sub(take);
53 if t.withdrawn >= t.amount {
54 *t = VestingTranche {
55 amount: 0,
56 start_slot: 0,
57 withdrawn: 0,
58 _pad: 0,
59 };
60 }
61 }
62}
63
64pub fn push_stake_tranche(
66 vesting: &mut [VestingTranche; 8],
67 amount: u64,
68 now: u64,
69) -> Result<(), ()> {
70 if amount == 0 {
71 return Ok(());
72 }
73 for t in vesting.iter_mut() {
74 if t.amount == 0 {
75 *t = VestingTranche {
76 amount,
77 start_slot: now,
78 withdrawn: 0,
79 _pad: 0,
80 };
81 return Ok(());
82 }
83 }
84 Err(())
85}