use anchor_lang::prelude::*;
pub mod constants;
pub mod process_compress_spl_token_account;
pub mod process_mint;
pub mod process_transfer;
use process_compress_spl_token_account::process_compress_spl_token_account;
pub mod spl_compression;
pub use process_mint::*;
pub mod token_data;
pub use token_data::TokenData;
pub mod delegation;
pub mod freeze;
pub mod instructions;
pub use instructions::*;
pub mod burn;
pub use burn::*;
pub mod batch_compress;
use light_compressed_account::instruction_data::cpi_context::CompressedCpiContext;
use crate::process_transfer::CompressedTokenInstructionDataTransfer;
declare_id!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m");
#[cfg(not(feature = "no-entrypoint"))]
solana_security_txt::security_txt! {
name: "light-compressed-token",
project_url: "lightprotocol.com",
contacts: "email:security@lightprotocol.com",
policy: "https://github.com/Lightprotocol/light-protocol/blob/main/SECURITY.md",
source_code: "https://github.com/Lightprotocol/light-protocol"
}
#[program]
pub mod light_compressed_token {
use constants::{NOT_FROZEN, NUM_MAX_POOL_ACCOUNTS};
use light_zero_copy::borsh::Deserialize;
use spl_compression::check_spl_token_pool_derivation_with_index;
use super::*;
pub fn create_token_pool<'info>(
ctx: Context<'_, '_, '_, 'info, CreateTokenPoolInstruction<'info>>,
) -> Result<()> {
create_token_pool::assert_mint_extensions(
&ctx.accounts.mint.to_account_info().try_borrow_data()?,
)
}
pub fn add_token_pool<'info>(
ctx: Context<'_, '_, '_, 'info, AddTokenPoolInstruction<'info>>,
token_pool_index: u8,
) -> Result<()> {
if token_pool_index >= NUM_MAX_POOL_ACCOUNTS {
return err!(ErrorCode::InvalidTokenPoolBump);
}
check_spl_token_pool_derivation_with_index(
&ctx.accounts.mint.key().to_bytes(),
&ctx.accounts.existing_token_pool_pda.key(),
&[token_pool_index.saturating_sub(1)],
)
}
pub fn mint_to<'info>(
ctx: Context<'_, '_, '_, 'info, MintToInstruction<'info>>,
public_keys: Vec<Pubkey>,
amounts: Vec<u64>,
lamports: Option<u64>,
) -> Result<()> {
process_mint_to_or_compress::<MINT_TO>(
ctx,
public_keys.as_slice(),
amounts.as_slice(),
lamports,
None,
None,
)
}
pub fn batch_compress<'info>(
ctx: Context<'_, '_, '_, 'info, MintToInstruction<'info>>,
inputs: Vec<u8>,
) -> Result<()> {
let (inputs, _) = batch_compress::BatchCompressInstructionData::zero_copy_at(&inputs)
.map_err(ProgramError::from)?;
if inputs.amounts.is_some() && inputs.amount.is_some() {
return Err(crate::ErrorCode::AmountsAndAmountProvided.into());
}
let amounts = if let Some(amount) = inputs.amount {
vec![*amount; inputs.pubkeys.len()]
} else if let Some(amounts) = inputs.amounts {
amounts.to_vec()
} else {
return Err(crate::ErrorCode::NoAmount.into());
};
process_mint_to_or_compress::<COMPRESS>(
ctx,
inputs.pubkeys.as_slice(),
amounts.as_slice(),
inputs.lamports.map(|x| (*x).into()),
Some(inputs.index),
Some(inputs.bump),
)
}
pub fn compress_spl_token_account<'info>(
ctx: Context<'_, '_, '_, 'info, TransferInstruction<'info>>,
owner: Pubkey,
remaining_amount: Option<u64>,
cpi_context: Option<CompressedCpiContext>,
) -> Result<()> {
process_compress_spl_token_account(ctx, owner, remaining_amount, cpi_context)
}
pub fn transfer<'info>(
ctx: Context<'_, '_, '_, 'info, TransferInstruction<'info>>,
inputs: Vec<u8>,
) -> Result<()> {
let mut inputs = inputs;
inputs.extend_from_slice(&[0u8; 1]);
let inputs: CompressedTokenInstructionDataTransfer =
CompressedTokenInstructionDataTransfer::deserialize(&mut inputs.as_slice())?;
process_transfer::process_transfer(ctx, inputs)
}
pub fn approve<'info>(
ctx: Context<'_, '_, '_, 'info, GenericInstruction<'info>>,
inputs: Vec<u8>,
) -> Result<()> {
delegation::process_approve(ctx, inputs)
}
pub fn revoke<'info>(
ctx: Context<'_, '_, '_, 'info, GenericInstruction<'info>>,
inputs: Vec<u8>,
) -> Result<()> {
delegation::process_revoke(ctx, inputs)
}
pub fn freeze<'info>(
ctx: Context<'_, '_, '_, 'info, FreezeInstruction<'info>>,
inputs: Vec<u8>,
) -> Result<()> {
freeze::process_freeze_or_thaw::<NOT_FROZEN, true>(ctx, inputs)
}
pub fn thaw<'info>(
ctx: Context<'_, '_, '_, 'info, FreezeInstruction<'info>>,
inputs: Vec<u8>,
) -> Result<()> {
freeze::process_freeze_or_thaw::<true, NOT_FROZEN>(ctx, inputs)
}
pub fn burn<'info>(
ctx: Context<'_, '_, '_, 'info, BurnInstruction<'info>>,
inputs: Vec<u8>,
) -> Result<()> {
burn::process_burn(ctx, inputs)
}
#[cfg(feature = "idl-build")]
pub fn stub_idl_build<'info>(
_ctx: Context<'_, '_, '_, 'info, TransferInstruction<'info>>,
_inputs1: CompressedTokenInstructionDataTransfer,
_inputs2: TokenData,
) -> Result<()> {
Err(ErrorCode::InstructionNotCallable.into())
}
}
#[error_code]
pub enum ErrorCode {
#[msg("public keys and amounts must be of same length")]
PublicKeyAmountMissmatch,
#[msg("ComputeInputSumFailed")]
ComputeInputSumFailed,
#[msg("ComputeOutputSumFailed")]
ComputeOutputSumFailed,
#[msg("ComputeCompressSumFailed")]
ComputeCompressSumFailed,
#[msg("ComputeDecompressSumFailed")]
ComputeDecompressSumFailed,
#[msg("SumCheckFailed")]
SumCheckFailed,
#[msg("DecompressRecipientUndefinedForDecompress")]
DecompressRecipientUndefinedForDecompress,
#[msg("CompressedPdaUndefinedForDecompress")]
CompressedPdaUndefinedForDecompress,
#[msg("DeCompressAmountUndefinedForDecompress")]
DeCompressAmountUndefinedForDecompress,
#[msg("CompressedPdaUndefinedForCompress")]
CompressedPdaUndefinedForCompress,
#[msg("DeCompressAmountUndefinedForCompress")]
DeCompressAmountUndefinedForCompress,
#[msg("DelegateSignerCheckFailed")]
DelegateSignerCheckFailed,
#[msg("Minted amount greater than u64::MAX")]
MintTooLarge,
#[msg("SplTokenSupplyMismatch")]
SplTokenSupplyMismatch,
#[msg("HeapMemoryCheckFailed")]
HeapMemoryCheckFailed,
#[msg("The instruction is not callable")]
InstructionNotCallable,
#[msg("ArithmeticUnderflow")]
ArithmeticUnderflow,
#[msg("HashToFieldError")]
HashToFieldError,
#[msg("Expected the authority to be also a mint authority")]
InvalidAuthorityMint,
#[msg("Provided authority is not the freeze authority")]
InvalidFreezeAuthority,
InvalidDelegateIndex,
TokenPoolPdaUndefined,
#[msg("Compress or decompress recipient is the same account as the token pool pda.")]
IsTokenPoolPda,
InvalidTokenPoolPda,
NoInputTokenAccountsProvided,
NoInputsProvided,
MintHasNoFreezeAuthority,
MintWithInvalidExtension,
#[msg("The token account balance is less than the remaining amount.")]
InsufficientTokenAccountBalance,
#[msg("Max number of token pools reached.")]
InvalidTokenPoolBump,
FailedToDecompress,
FailedToBurnSplTokensFromTokenPool,
NoMatchingBumpFound,
NoAmount,
AmountsAndAmountProvided,
}