use hopper_runtime::{error::ProgramError, AccountView, Address, ProgramResult};
use crate::constants::{SYSVAR_INSTRUCTIONS_ID, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID};
#[inline(always)]
pub fn current_instruction_index(sysvar_data: &[u8]) -> Result<u16, ProgramError> {
let len = sysvar_data.len();
if len < 2 {
return Err(ProgramError::InvalidAccountData);
}
Ok(u16::from_le_bytes([
sysvar_data[len - 2],
sysvar_data[len - 1],
]))
}
#[inline(always)]
pub fn instruction_count(sysvar_data: &[u8]) -> Result<u16, ProgramError> {
if sysvar_data.len() < 2 {
return Err(ProgramError::InvalidAccountData);
}
Ok(u16::from_le_bytes([sysvar_data[0], sysvar_data[1]]))
}
#[inline(always)]
pub fn assert_no_cpi(instructions_sysvar: &AccountView, our_program_id: &Address) -> ProgramResult {
if *instructions_sysvar.address() != SYSVAR_INSTRUCTIONS_ID {
return Err(ProgramError::InvalidArgument);
}
let data = instructions_sysvar.try_borrow()?;
if data.len() < 4 {
return Err(ProgramError::InvalidAccountData);
}
let current_idx = current_instruction_index(&data)?;
let num_instructions = instruction_count(&data)?;
if current_idx >= num_instructions {
return Err(ProgramError::InvalidAccountData);
}
let offset_entry = 2 + (current_idx as usize) * 2;
if offset_entry + 2 > data.len() {
return Err(ProgramError::InvalidAccountData);
}
let ix_offset = u16::from_le_bytes([data[offset_entry], data[offset_entry + 1]]) as usize;
if ix_offset + 2 > data.len() {
return Err(ProgramError::InvalidAccountData);
}
let num_accounts = u16::from_le_bytes([data[ix_offset], data[ix_offset + 1]]) as usize;
let program_id_offset = ix_offset + 2 + num_accounts * 33;
if program_id_offset + 32 > data.len() {
return Err(ProgramError::InvalidAccountData);
}
if data[program_id_offset..program_id_offset + 32] != *our_program_id.as_array() {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn check_token_program_owner(account: &AccountView) -> ProgramResult {
if !account.owned_by(&TOKEN_PROGRAM_ID) {
return Err(ProgramError::IncorrectProgramId);
}
Ok(())
}
#[inline(always)]
pub fn check_token_2022_program_owner(account: &AccountView) -> ProgramResult {
if !account.owned_by(&TOKEN_2022_PROGRAM_ID) {
return Err(ProgramError::IncorrectProgramId);
}
Ok(())
}
#[inline(always)]
pub fn check_any_token_program_owner(account: &AccountView) -> ProgramResult {
if check_token_program_owner(account).is_ok() || check_token_2022_program_owner(account).is_ok()
{
return Ok(());
}
Err(ProgramError::IncorrectProgramId)
}