jito_jsm_core/
lib.rs

1use error::CoreError;
2use solana_program::{
3    account_info::AccountInfo,
4    entrypoint::ProgramResult,
5    program::{invoke, invoke_signed},
6    program_error::ProgramError,
7    pubkey::Pubkey,
8    rent::Rent,
9    system_instruction,
10};
11
12pub mod error;
13pub mod loader;
14pub mod slot_toggle;
15
16/// Creates a new account or initializes an existing account
17/// # Arguments
18/// * `payer` - The account that will pay for the lamports
19/// * `new_account` - The account to create or initialize
20/// * `system_program` - The system program account
21/// * `program_owner` - The owner of the program
22/// * `rent` - The rent sysvar
23/// * `space` - The space to allocate
24/// * `seeds` - The seeds to use for the PDA
25/// # Returns
26/// * `ProgramResult` - The result of the operation
27#[inline(always)]
28pub fn create_account<'a, 'info>(
29    payer: &'a AccountInfo<'info>,
30    new_account: &'a AccountInfo<'info>,
31    system_program: &'a AccountInfo<'info>,
32    program_owner: &Pubkey,
33    rent: &Rent,
34    space: u64,
35    seeds: &[Vec<u8>],
36) -> ProgramResult {
37    let current_lamports = **new_account.try_borrow_lamports()?;
38    if current_lamports == 0 {
39        // If there are no lamports in the new account, we create it with the create_account instruction
40        invoke_signed(
41            &system_instruction::create_account(
42                payer.key,
43                new_account.key,
44                rent.minimum_balance(space as usize),
45                space,
46                program_owner,
47            ),
48            &[payer.clone(), new_account.clone(), system_program.clone()],
49            &[seeds
50                .iter()
51                .map(|seed| seed.as_slice())
52                .collect::<Vec<&[u8]>>()
53                .as_slice()],
54        )
55    } else {
56        // someone can transfer lamports to accounts before they're initialized
57        // in that case, creating the account won't work.
58        // in order to get around it, you need to find the account with enough lamports to be rent exempt,
59        // then allocate the required space and set the owner to the current program
60        let required_lamports = rent
61            .minimum_balance(space as usize)
62            .max(1)
63            .saturating_sub(current_lamports);
64        if required_lamports > 0 {
65            invoke(
66                &system_instruction::transfer(payer.key, new_account.key, required_lamports),
67                &[payer.clone(), new_account.clone(), system_program.clone()],
68            )?;
69        }
70        // Allocate space.
71        invoke_signed(
72            &system_instruction::allocate(new_account.key, space),
73            &[new_account.clone(), system_program.clone()],
74            &[seeds
75                .iter()
76                .map(|seed| seed.as_slice())
77                .collect::<Vec<&[u8]>>()
78                .as_slice()],
79        )?;
80        // Assign to the specified program
81        invoke_signed(
82            &system_instruction::assign(new_account.key, program_owner),
83            &[new_account.clone(), system_program.clone()],
84            &[seeds
85                .iter()
86                .map(|seed| seed.as_slice())
87                .collect::<Vec<&[u8]>>()
88                .as_slice()],
89        )
90    }
91}
92
93/// Closes the program account
94pub fn close_program_account<'a>(
95    program_id: &Pubkey,
96    account_to_close: &AccountInfo<'a>,
97    destination_account: &AccountInfo<'a>,
98) -> ProgramResult {
99    // Check if the account is owned by the program
100    if account_to_close.owner != program_id {
101        return Err(ProgramError::IllegalOwner);
102    }
103
104    **destination_account.lamports.borrow_mut() = destination_account
105        .lamports()
106        .checked_add(account_to_close.lamports())
107        .ok_or(ProgramError::ArithmeticOverflow)?;
108    **account_to_close.lamports.borrow_mut() = 0;
109
110    account_to_close.assign(&solana_program::system_program::id());
111    account_to_close.realloc(0, false)?;
112
113    Ok(())
114}
115
116pub fn realloc<'a, 'info>(
117    account: &'a AccountInfo<'info>,
118    new_size: usize,
119    payer: &'a AccountInfo<'info>,
120    rent: &Rent,
121) -> ProgramResult {
122    let new_minimum_balance = rent.minimum_balance(new_size);
123
124    let lamports_diff = new_minimum_balance.saturating_sub(account.lamports());
125    invoke(
126        &system_instruction::transfer(payer.key, account.key, lamports_diff),
127        &[payer.clone(), account.clone()],
128    )?;
129    account.realloc(new_size, false)?;
130    Ok(())
131}
132
133pub fn get_epoch(slot: u64, epoch_length: u64) -> Result<u64, CoreError> {
134    let epoch = slot
135        .checked_div(epoch_length)
136        .ok_or(CoreError::BadEpochLength)?;
137
138    Ok(epoch)
139}