light_token_interface/instructions/extensions/
compressible.rs1use std::mem::MaybeUninit;
2
3use light_zero_copy::{ZeroCopy, ZeroCopyMut};
4use pinocchio::pubkey::Pubkey;
5use solana_pubkey::MAX_SEEDS;
6use tinyvec::ArrayVec;
7
8use crate::{AnchorDeserialize, AnchorSerialize, TokenError};
9
10#[derive(
11 Debug, Clone, PartialEq, Eq, AnchorSerialize, AnchorDeserialize, ZeroCopy, ZeroCopyMut,
12)]
13#[repr(C)]
14pub struct CompressibleExtensionInstructionData {
15 pub token_account_version: u8,
18 pub rent_payment: u8,
21 pub compression_only: u8,
25 pub write_top_up: u32,
26 pub compress_to_account_pubkey: Option<CompressToPubkey>,
27}
28
29#[derive(
30 Debug, Clone, PartialEq, Eq, AnchorSerialize, AnchorDeserialize, ZeroCopy, ZeroCopyMut,
31)]
32#[repr(C)]
33pub struct CompressToPubkey {
34 pub bump: u8,
35 pub program_id: [u8; 32],
36 pub seeds: Vec<Vec<u8>>,
37}
38
39impl CompressToPubkey {
40 pub fn check_seeds(&self, pubkey: &Pubkey) -> Result<(), TokenError> {
41 if self.seeds.len() >= MAX_SEEDS {
42 return Err(TokenError::TooManySeeds(MAX_SEEDS - 1));
43 }
44 let mut references = ArrayVec::<[&[u8]; MAX_SEEDS]>::new();
45 for seed in self.seeds.iter() {
46 references.push(seed.as_slice());
47 }
48 let derived_pubkey = derive_address(references.as_slice(), self.bump, &self.program_id)?;
49 if derived_pubkey != *pubkey {
50 Err(TokenError::InvalidAccountData)
51 } else {
52 Ok(())
53 }
54 }
55}
56
57pub fn derive_address(
62 seeds: &[&[u8]],
63 bump: u8,
64 program_id: &Pubkey,
65) -> Result<Pubkey, TokenError> {
66 const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
67 if seeds.len() >= MAX_SEEDS {
70 return Err(TokenError::TooManySeeds(MAX_SEEDS - 1));
71 }
72 const UNINIT: MaybeUninit<&[u8]> = MaybeUninit::<&[u8]>::uninit();
73 let mut data = [UNINIT; MAX_SEEDS + 2];
74 let mut i = 0;
75
76 while i < seeds.len() {
77 unsafe {
80 data.get_unchecked_mut(i).write(seeds.get_unchecked(i));
81 }
82 i += 1;
83 }
84
85 let bump_seed = [bump];
88
89 unsafe {
92 data.get_unchecked_mut(i).write(&bump_seed);
93 i += 1;
94
95 data.get_unchecked_mut(i).write(program_id.as_ref());
96 data.get_unchecked_mut(i + 1).write(PDA_MARKER.as_ref());
97 }
98
99 #[cfg(target_os = "solana")]
100 {
101 use pinocchio::syscalls::sol_sha256;
102 let mut pda = MaybeUninit::<[u8; 32]>::uninit();
103
104 unsafe {
106 sol_sha256(
107 data.as_ptr() as *const u8,
108 (i + 2) as u64,
109 pda.as_mut_ptr() as *mut u8,
110 );
111 }
112
113 unsafe { Ok(pda.assume_init()) }
115 }
116
117 #[cfg(not(target_os = "solana"))]
118 unreachable!("deriving a pda is only available on target `solana`");
119}