1use anchor_lang::{prelude::*, solana_program::pubkey::PUBKEY_BYTES};
2use num_traits::ToPrimitive;
3use vipers::unwrap_int;
4
5#[account]
8#[derive(Copy, Debug, Default, PartialEq, Eq)]
9pub struct CrateToken {
10 pub mint: Pubkey,
12 pub bump: u8,
14
15 pub fee_setter_authority: Pubkey,
17 pub fee_to_setter: Pubkey,
19 pub issue_authority: Pubkey,
23 pub withdraw_authority: Pubkey,
26
27 pub author_fee_to: Pubkey,
30
31 pub issue_fee_bps: u16,
34 pub withdraw_fee_bps: u16,
37}
38
39impl CrateToken {
40 pub const LEN: usize = PUBKEY_BYTES + 1 + PUBKEY_BYTES * 5 + 2 + 2;
41}
42
43#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
44pub struct Fees {
45 pub amount: u64,
46 pub author_fee: u64,
48 pub protocol_fee: u64,
50}
51
52fn apply_bps(amount: u64, bps: u16) -> Result<(u64, u64)> {
53 let bps = unwrap_int!((amount)
54 .checked_mul(bps.into())
55 .and_then(|v| v.checked_div(10_000))
56 .and_then(|v| v.to_u64()));
57 Ok((unwrap_int!(amount.checked_sub(bps)), bps))
58}
59
60impl CrateToken {
61 pub fn apply_issue_fee(&self, amount: u64) -> Result<Fees> {
63 let (amount, issue_fee) = apply_bps(amount, self.issue_fee_bps)?;
64 let (author_fee, protocol_fee) = apply_bps(issue_fee, crate::ISSUE_FEE_BPS)?;
65 Ok(Fees {
66 amount,
67 author_fee,
68 protocol_fee,
69 })
70 }
71
72 pub fn apply_withdraw_fee(&self, amount: u64) -> Result<Fees> {
74 let (amount, withdraw_fee) = apply_bps(amount, self.withdraw_fee_bps)?;
75 let (author_fee, protocol_fee) = apply_bps(withdraw_fee, crate::WITHDRAW_FEE_BPS)?;
76 Ok(Fees {
77 amount,
78 author_fee,
79 protocol_fee,
80 })
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_crate_token_len() {
90 use crate::CrateToken;
91 assert_eq!(
92 CrateToken::LEN,
93 CrateToken::default().try_to_vec().unwrap().len()
94 );
95 }
96}