Skip to main content

tengu_api/
vesting.rs

1//! Fren Pet [Stake-to-Play (S2P)](https://docs.frenpet.xyz/stake): staked DOJO refunds linearly over 30 days (per slot).
2
3use crate::consts::STAKE_VESTING_SLOTS;
4use crate::state::VestingTranche;
5
6/// Linear unlocked amount from `start_slot` over [`STAKE_VESTING_SLOTS`].
7pub 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
23/// Unlocked minus already withdrawn from this tranche.
24pub 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
40/// Apply a withdrawal against tranches in order (FIFO). Clears tranches when fully withdrawn.
41pub 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
64/// Append a new vesting tranche. Returns `Err` if all slots are full.
65pub 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}