pub use light_compressed_account::LightInstructionData;
use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, LIGHT_SYSTEM_PROGRAM_ID};
use solana_account_info::AccountInfo;
use solana_cpi::invoke_signed;
#[cfg(feature = "cpi-context")]
use solana_instruction::AccountMeta;
use solana_instruction::Instruction;
use solana_program_error::ProgramError;
use crate::{
cpi::{account::CpiAccountsTrait, instruction::LightCpiInstruction},
error::LightSdkError,
};
pub trait InvokeLightSystemProgram {
fn invoke<'info>(self, accounts: impl CpiAccountsTrait<'info>) -> Result<(), ProgramError>;
#[cfg(feature = "cpi-context")]
fn invoke_write_to_cpi_context_first<'info>(
self,
accounts: impl CpiAccountsTrait<'info>,
) -> Result<(), ProgramError>;
#[cfg(feature = "cpi-context")]
fn invoke_write_to_cpi_context_set<'info>(
self,
accounts: impl CpiAccountsTrait<'info>,
) -> Result<(), ProgramError>;
#[cfg(feature = "cpi-context")]
fn invoke_execute_cpi_context<'info>(
self,
accounts: impl CpiAccountsTrait<'info>,
) -> Result<(), ProgramError>;
}
impl<T> InvokeLightSystemProgram for T
where
T: LightInstructionData + LightCpiInstruction,
{
fn invoke<'info>(self, accounts: impl CpiAccountsTrait<'info>) -> Result<(), ProgramError> {
#[cfg(feature = "cpi-context")]
{
use light_compressed_account::instruction_data::cpi_context::CompressedCpiContext;
if self.get_with_cpi_context()
|| *self.get_cpi_context() == CompressedCpiContext::set()
|| *self.get_cpi_context() == CompressedCpiContext::first()
{
solana_msg::msg!(
"CPI context operations not supported in invoke(). Use invoke_write_to_cpi_context_first(), invoke_write_to_cpi_context_set(), or invoke_execute_cpi_context() instead"
);
return Err(ProgramError::InvalidInstructionData);
}
}
if let Some(account_mode) = accounts.get_mode() {
if account_mode != self.get_mode() {
solana_msg::msg!(
"Mode mismatch: accounts have mode {} but instruction data has mode {}",
account_mode,
self.get_mode()
);
return Err(ProgramError::InvalidInstructionData);
}
}
let data = self
.data()
.map_err(LightSdkError::from)
.map_err(ProgramError::from)?;
let account_infos = accounts.to_account_infos();
let account_metas = accounts.to_account_metas()?;
let instruction = Instruction {
program_id: LIGHT_SYSTEM_PROGRAM_ID.into(),
accounts: account_metas,
data,
};
invoke_light_system_program(&account_infos, instruction, self.get_bump())
}
#[cfg(feature = "cpi-context")]
fn invoke_write_to_cpi_context_first<'info>(
self,
accounts: impl CpiAccountsTrait<'info>,
) -> Result<(), ProgramError> {
let instruction_data = self.write_to_cpi_context_first();
inner_invoke_write_to_cpi_context_typed(instruction_data, accounts)
}
#[cfg(feature = "cpi-context")]
fn invoke_write_to_cpi_context_set<'info>(
self,
accounts: impl CpiAccountsTrait<'info>,
) -> Result<(), ProgramError> {
let instruction_data = self.write_to_cpi_context_set();
inner_invoke_write_to_cpi_context_typed(instruction_data, accounts)
}
#[cfg(feature = "cpi-context")]
fn invoke_execute_cpi_context<'info>(
self,
accounts: impl CpiAccountsTrait<'info>,
) -> Result<(), ProgramError> {
let instruction_data = self.execute_with_cpi_context();
let data = instruction_data
.data()
.map_err(LightSdkError::from)
.map_err(ProgramError::from)?;
let account_infos = accounts.to_account_infos();
let account_metas = accounts.to_account_metas()?;
let instruction = Instruction {
program_id: LIGHT_SYSTEM_PROGRAM_ID.into(),
accounts: account_metas,
data,
};
invoke_light_system_program(&account_infos, instruction, instruction_data.get_bump())
}
}
#[cfg(feature = "cpi-context")]
#[inline(always)]
fn inner_invoke_write_to_cpi_context_typed<'info, T>(
instruction_data: T,
accounts: impl CpiAccountsTrait<'info>,
) -> Result<(), ProgramError>
where
T: LightInstructionData + LightCpiInstruction,
{
if instruction_data.has_read_only_accounts() {
solana_msg::msg!(
"Read-only accounts are not supported in write_to_cpi_context operations. Use invoke_execute_cpi_context() instead."
);
return Err(LightSdkError::ReadOnlyAccountsNotSupportedInCpiContext.into());
}
let data = instruction_data
.data()
.map_err(LightSdkError::from)
.map_err(ProgramError::from)?;
let account_infos = accounts.to_account_infos();
if account_infos.len() < 3 {
return Err(ProgramError::NotEnoughAccountKeys);
}
let instruction = Instruction {
program_id: LIGHT_SYSTEM_PROGRAM_ID.into(),
accounts: vec![
AccountMeta {
pubkey: *account_infos[0].key, is_writable: true,
is_signer: true,
},
AccountMeta {
pubkey: *account_infos[1].key, is_writable: false,
is_signer: true,
},
AccountMeta {
pubkey: *account_infos[2].key, is_writable: true,
is_signer: false,
},
],
data,
};
invoke_light_system_program(&account_infos, instruction, instruction_data.get_bump())
}
#[inline(always)]
pub fn invoke_light_system_program(
account_infos: &[AccountInfo],
instruction: Instruction,
bump: u8,
) -> Result<(), ProgramError> {
let signer_seeds = [CPI_AUTHORITY_PDA_SEED, &[bump]];
invoke_signed(&instruction, account_infos, &[signer_seeds.as_slice()])
}