use borsh::BorshSerialize;
use light_token_interface::instructions::{
create_token_account::CreateTokenAccountInstructionData,
extensions::{CompressToPubkey, CompressibleExtensionInstructionData},
};
use solana_account_info::AccountInfo;
use solana_cpi::{invoke, invoke_signed};
use solana_instruction::{AccountMeta, Instruction};
use solana_program_error::ProgramError;
use solana_pubkey::Pubkey;
use crate::instruction::{compressible::CompressibleParamsCpi, CompressibleParams};
#[derive(Debug, Clone)]
pub struct CreateTokenAccount {
pub payer: Pubkey,
pub account: Pubkey,
pub mint: Pubkey,
pub owner: Pubkey,
pub compressible: CompressibleParams,
}
impl CreateTokenAccount {
pub fn new(payer: Pubkey, account: Pubkey, mint: Pubkey, owner: Pubkey) -> Self {
Self {
payer,
account,
mint,
owner,
compressible: CompressibleParams::default(),
}
}
pub fn with_compressible(mut self, compressible: CompressibleParams) -> Self {
self.compressible = compressible;
self
}
pub fn instruction(self) -> Result<Instruction, ProgramError> {
let instruction_data = CreateTokenAccountInstructionData {
owner: light_compressed_account::Pubkey::from(self.owner.to_bytes()),
compressible_config: Some(CompressibleExtensionInstructionData {
token_account_version: self.compressible.token_account_version as u8,
rent_payment: self.compressible.pre_pay_num_epochs,
compression_only: self.compressible.compression_only as u8,
write_top_up: self.compressible.lamports_per_write.unwrap_or(0),
compress_to_account_pubkey: self.compressible.compress_to_account_pubkey,
}),
};
let mut data = Vec::new();
data.push(18u8); instruction_data
.serialize(&mut data)
.map_err(|e| ProgramError::BorshIoError(e.to_string()))?;
let accounts = vec![
AccountMeta::new(self.account, true),
AccountMeta::new_readonly(self.mint, false),
AccountMeta::new(self.payer, true),
AccountMeta::new_readonly(self.compressible.compressible_config, false),
AccountMeta::new_readonly(Pubkey::default(), false), AccountMeta::new(self.compressible.rent_sponsor, false),
];
Ok(Instruction {
program_id: Pubkey::from(light_token_interface::LIGHT_TOKEN_PROGRAM_ID),
accounts,
data,
})
}
}
pub struct CreateTokenAccountCpi<'info> {
pub payer: AccountInfo<'info>,
pub account: AccountInfo<'info>,
pub mint: AccountInfo<'info>,
pub owner: Pubkey,
}
impl<'info> CreateTokenAccountCpi<'info> {
pub fn rent_free(
self,
config: AccountInfo<'info>,
sponsor: AccountInfo<'info>,
system_program: AccountInfo<'info>,
program_id: &Pubkey,
) -> CreateTokenAccountRentFreeCpi<'info> {
CreateTokenAccountRentFreeCpi {
base: self,
config,
sponsor,
system_program,
program_id: *program_id,
}
}
pub fn invoke_with(
self,
compressible: CompressibleParamsCpi<'info>,
) -> Result<(), ProgramError> {
LegacyCreateTokenAccountCpi {
payer: self.payer,
account: self.account,
mint: self.mint,
owner: self.owner,
compressible,
}
.invoke()
}
pub fn invoke_signed_with(
self,
compressible: CompressibleParamsCpi<'info>,
signer_seeds: &[&[&[u8]]],
) -> Result<(), ProgramError> {
LegacyCreateTokenAccountCpi {
payer: self.payer,
account: self.account,
mint: self.mint,
owner: self.owner,
compressible,
}
.invoke_signed(signer_seeds)
}
}
pub struct CreateTokenAccountRentFreeCpi<'info> {
base: CreateTokenAccountCpi<'info>,
config: AccountInfo<'info>,
sponsor: AccountInfo<'info>,
system_program: AccountInfo<'info>,
program_id: Pubkey,
}
impl<'info> CreateTokenAccountRentFreeCpi<'info> {
pub fn invoke(self) -> Result<(), ProgramError> {
let defaults = CompressibleParams::default();
let cpi = LegacyCreateTokenAccountCpi {
payer: self.base.payer,
account: self.base.account,
mint: self.base.mint,
owner: self.base.owner,
compressible: CompressibleParamsCpi {
compressible_config: self.config,
rent_sponsor: self.sponsor,
system_program: self.system_program,
pre_pay_num_epochs: defaults.pre_pay_num_epochs,
lamports_per_write: defaults.lamports_per_write,
compress_to_account_pubkey: None,
token_account_version: defaults.token_account_version,
compression_only: defaults.compression_only,
},
};
cpi.invoke()
}
pub fn invoke_signed(self, seeds: &[&[u8]]) -> Result<(), ProgramError> {
let defaults = CompressibleParams::default();
let bump = seeds.last().and_then(|s| s.first()).copied().unwrap_or(0);
let seed_vecs: Vec<Vec<u8>> = seeds
.iter()
.take(seeds.len().saturating_sub(1))
.map(|s| s.to_vec())
.collect();
let compress_to = CompressToPubkey {
bump,
program_id: self.program_id.to_bytes(),
seeds: seed_vecs,
};
let cpi = LegacyCreateTokenAccountCpi {
payer: self.base.payer,
account: self.base.account,
mint: self.base.mint,
owner: self.base.owner,
compressible: CompressibleParamsCpi {
compressible_config: self.config,
rent_sponsor: self.sponsor,
system_program: self.system_program,
pre_pay_num_epochs: defaults.pre_pay_num_epochs,
lamports_per_write: defaults.lamports_per_write,
compress_to_account_pubkey: Some(compress_to),
token_account_version: defaults.token_account_version,
compression_only: defaults.compression_only,
},
};
cpi.invoke_signed(&[seeds])
}
}
struct LegacyCreateTokenAccountCpi<'info> {
payer: AccountInfo<'info>,
account: AccountInfo<'info>,
mint: AccountInfo<'info>,
owner: Pubkey,
compressible: CompressibleParamsCpi<'info>,
}
impl<'info> LegacyCreateTokenAccountCpi<'info> {
fn instruction(&self) -> Result<Instruction, ProgramError> {
CreateTokenAccount {
payer: *self.payer.key,
account: *self.account.key,
mint: *self.mint.key,
owner: self.owner,
compressible: CompressibleParams {
compressible_config: *self.compressible.compressible_config.key,
rent_sponsor: *self.compressible.rent_sponsor.key,
pre_pay_num_epochs: self.compressible.pre_pay_num_epochs,
lamports_per_write: self.compressible.lamports_per_write,
compress_to_account_pubkey: self.compressible.compress_to_account_pubkey.clone(),
token_account_version: self.compressible.token_account_version,
compression_only: self.compressible.compression_only,
},
}
.instruction()
}
fn invoke(self) -> Result<(), ProgramError> {
let instruction = self.instruction()?;
let account_infos = [
self.account,
self.mint,
self.payer,
self.compressible.compressible_config,
self.compressible.system_program,
self.compressible.rent_sponsor,
];
invoke(&instruction, &account_infos)
}
fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> {
let instruction = self.instruction()?;
let account_infos = [
self.account,
self.mint,
self.payer,
self.compressible.compressible_config,
self.compressible.system_program,
self.compressible.rent_sponsor,
];
invoke_signed(&instruction, &account_infos, signer_seeds)
}
}