#[cfg(feature = "cpi")]
use crate::instruction::InstructionAccount;
use {
crate::{
account::{AccountView, Ref},
address::ADDRESS_BYTES,
error::ProgramError,
Address,
},
core::{marker::PhantomData, mem::size_of, ops::Deref},
};
pub const INSTRUCTIONS_ID: Address = Address::new_from_array([
0x06, 0xa7, 0xd5, 0x17, 0x18, 0x7b, 0xd1, 0x66, 0x35, 0xda, 0xd4, 0x04, 0x55, 0xfd, 0xc2, 0xc0,
0xc1, 0x24, 0xc6, 0x8f, 0x21, 0x56, 0x75, 0xa5, 0xdb, 0xba, 0xcb, 0x5f, 0x08, 0x00, 0x00, 0x00,
]);
#[derive(Clone, Debug)]
pub struct Instructions<T>
where
T: Deref<Target = [u8]>,
{
data: T,
}
impl<T> Instructions<T>
where
T: Deref<Target = [u8]>,
{
#[inline(always)]
pub unsafe fn new_unchecked(data: T) -> Self {
Instructions { data }
}
#[inline(always)]
pub fn num_instructions(&self) -> usize {
u16::from_le_bytes(unsafe { *(self.data.as_ptr() as *const [u8; 2]) }) as usize
}
#[inline(always)]
pub fn load_current_index(&self) -> u16 {
let len = self.data.len();
unsafe { u16::from_le_bytes(*(self.data.as_ptr().add(len - 2) as *const [u8; 2])) }
}
#[inline(always)]
pub unsafe fn deserialize_instruction_unchecked(
&self,
index: usize,
) -> IntrospectedInstruction<'_> {
let offset = *(self
.data
.as_ptr()
.add(size_of::<u16>() + index * size_of::<u16>()) as *const u16);
IntrospectedInstruction::new_unchecked(self.data.as_ptr().add(offset as usize))
}
#[inline(always)]
pub fn load_instruction_at(
&self,
index: usize,
) -> Result<IntrospectedInstruction<'_>, ProgramError> {
if index >= self.num_instructions() {
return Err(ProgramError::InvalidInstructionData);
}
Ok(unsafe { self.deserialize_instruction_unchecked(index) })
}
#[inline(always)]
pub fn get_instruction_relative(
&self,
index_relative_to_current: i64,
) -> Result<IntrospectedInstruction<'_>, ProgramError> {
let current_index = self.load_current_index() as i64;
let index = current_index.saturating_add(index_relative_to_current);
if index < 0 {
return Err(ProgramError::InvalidInstructionData);
}
self.load_instruction_at(index as usize)
}
}
impl<'a> TryFrom<&'a AccountView> for Instructions<Ref<'a, [u8]>> {
type Error = ProgramError;
#[inline(always)]
fn try_from(account_view: &'a AccountView) -> Result<Self, Self::Error> {
if account_view.address() != &INSTRUCTIONS_ID {
return Err(ProgramError::UnsupportedSysvar);
}
Ok(Instructions {
data: account_view.try_borrow()?,
})
}
}
#[repr(C)]
#[cfg_attr(feature = "copy", derive(Copy))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct IntrospectedInstruction<'a> {
raw: *const u8,
marker: PhantomData<&'a [u8]>,
}
impl IntrospectedInstruction<'_> {
#[inline(always)]
unsafe fn new_unchecked(raw: *const u8) -> Self {
Self {
raw,
marker: PhantomData,
}
}
#[inline(always)]
pub fn num_account_metas(&self) -> usize {
u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) }) as usize
}
#[inline(always)]
pub unsafe fn get_instruction_account_at_unchecked(
&self,
index: usize,
) -> &IntrospectedInstructionAccount {
let offset = core::mem::size_of::<u16>() + (index * IntrospectedInstructionAccount::LEN);
&*(self.raw.add(offset) as *const IntrospectedInstructionAccount)
}
#[inline(always)]
pub fn get_instruction_account_at(
&self,
index: usize,
) -> Result<&IntrospectedInstructionAccount, ProgramError> {
let num_accounts = self.num_account_metas();
if index >= num_accounts {
return Err(ProgramError::InvalidArgument);
}
Ok(unsafe { self.get_instruction_account_at_unchecked(index) })
}
#[inline(always)]
pub fn get_program_id(&self) -> &Address {
let num_accounts = self.num_account_metas();
unsafe {
&*(self
.raw
.add(size_of::<u16>() + num_accounts * size_of::<IntrospectedInstructionAccount>())
as *const Address)
}
}
#[inline(always)]
pub fn get_instruction_data(&self) -> &[u8] {
let offset =
self.num_account_metas() * size_of::<IntrospectedInstructionAccount>() + ADDRESS_BYTES;
let data_len = u16::from_le_bytes(unsafe {
*(self.raw.add(size_of::<u16>() + offset) as *const [u8; 2])
});
unsafe {
core::slice::from_raw_parts(
self.raw.add(size_of::<u16>() + offset + size_of::<u16>()),
data_len as usize,
)
}
}
}
const IS_SIGNER: u8 = 0b00000001;
const IS_WRITABLE: u8 = 0b00000010;
#[repr(C)]
#[cfg_attr(feature = "copy", derive(Copy))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct IntrospectedInstructionAccount {
flags: u8,
pub key: Address,
}
impl IntrospectedInstructionAccount {
const LEN: usize = core::mem::size_of::<Self>();
#[inline(always)]
pub fn is_writable(&self) -> bool {
(self.flags & IS_WRITABLE) != 0
}
#[inline(always)]
pub fn is_signer(&self) -> bool {
(self.flags & IS_SIGNER) != 0
}
#[cfg(feature = "cpi")]
#[inline(always)]
pub fn to_instruction_account(&self) -> InstructionAccount<'_> {
InstructionAccount::new(&self.key, self.is_writable(), self.is_signer())
}
}