1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Setup fee split

use crate::state::CentralStateV2;
use bonfida_utils::{BorshSize, InstructionsAccount};
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::clock::Clock;

use solana_program::sysvar::Sysvar;
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint::ProgramResult,
    msg,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_program,
};


use crate::state::{
    FeeRecipient, MAX_FEE_RECIPIENTS, MAX_FEE_SPLIT_SETUP_DELAY,
};
use crate::utils::{check_account_key, check_account_owner, check_signer};
use crate::{error::AccessError};
use crate::instruction::ProgramInstruction::AdminSetupFeeSplit;

#[derive(BorshDeserialize, BorshSerialize, BorshSize)]
/// The required parameters for the `admin_setup_fee_split` instruction
pub struct Params {
    pub recipients: Vec<FeeRecipient>,
}

#[derive(InstructionsAccount)]
/// The required accounts for the `admin_setup_fee_split` instruction
pub struct Accounts<'a, T> {
    /// The central state authority
    #[cons(signer)]
    pub authority: &'a T,

    /// The central state account
    #[cons(writable)]
    pub central_state: &'a T,

    /// The system program account
    pub system_program: &'a T,
}

impl<'a, 'b: 'a> Accounts<'a, AccountInfo<'b>> {
    pub fn parse(
        accounts: &'a [AccountInfo<'b>],
        program_id: &Pubkey,
    ) -> Result<Self, ProgramError> {
        let accounts_iter = &mut accounts.iter();
        let accounts = Accounts {
            authority: next_account_info(accounts_iter)?,
            central_state: next_account_info(accounts_iter)?,
            system_program: next_account_info(accounts_iter)?,
        };

        // Check keys
        check_account_key(
            accounts.system_program,
            &system_program::ID,
            AccessError::WrongSystemProgram,
        )?;

        // Check ownership
        check_account_owner(accounts.central_state, program_id, AccessError::WrongOwner)?;

        // Check signer
        check_signer(
            accounts.authority,
            AccessError::CentralStateAuthorityMustSign,
        )?;

        Ok(accounts)
    }
}

pub fn process_admin_setup_fee_split(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    params: Params,
) -> ProgramResult {
    let Params { recipients } = params;
    let accounts = Accounts::parse(accounts, program_id)?;
    let mut central_state = CentralStateV2::from_account_info(accounts.central_state)?;
    central_state.assert_instruction_allowed(&AdminSetupFeeSplit)?;

    check_account_key(
        accounts.authority,
        &central_state.authority,
        AccessError::WrongCentralStateAuthority,
    )?;

    // Check if right number of recipients
    if recipients.len() > MAX_FEE_RECIPIENTS {
        msg!("Too many recipients");
        return Err(AccessError::TooManyRecipients.into());
    }

    // Check recipients
    let mut percentage_sum: u64 = 0;
    recipients.iter().try_for_each(|r| -> ProgramResult {
        if r.percentage == 0 {
            msg!("Recipient percentage 0 not allowed");
            return Err(AccessError::InvalidPercentages.into());
        }
        percentage_sum = percentage_sum
            .checked_add(r.percentage)
            .ok_or(AccessError::Overflow)?;
        if percentage_sum > 100 {
            msg!("Percentages add up to more than 100");
            return Err(AccessError::InvalidPercentages.into());
        }
        Ok(())
    })?;

    // The recipient setup must be done within 5 minutes after the fee distribution
    let current_time = Clock::get()?.unix_timestamp as u64;
    if current_time - central_state.last_fee_distribution_time as u64 > MAX_FEE_SPLIT_SETUP_DELAY {
        msg!("Delay between fee distribution and fee split setup too long");
        return Err(AccessError::DelayTooLong.into());
    }

    central_state.recipients = recipients;

    // replace the recipients
    central_state.save(&mut accounts.central_state.data.borrow_mut())?;
    Ok(())
}