1use bytemuck::{Pod, Zeroable};
2use light_account_checks::discriminator::Discriminator;
3use solana_pubkey::{pubkey, Pubkey};
4
5use crate::{error::CompressibleError, rent::RentConfig, AnchorDeserialize, AnchorSerialize};
6
7pub const COMPRESSIBLE_CONFIG_SEED: &[u8] = b"compressible_config";
8
9#[derive(Debug, PartialEq)]
10#[repr(u8)]
11pub enum CompressibleConfigState {
12 Inactive,
13 Active,
14 Deprecated,
15}
16
17impl TryFrom<u8> for CompressibleConfigState {
18 type Error = CompressibleError;
19
20 fn try_from(value: u8) -> Result<Self, Self::Error> {
21 match value {
22 0 => Ok(CompressibleConfigState::Inactive),
23 1 => Ok(CompressibleConfigState::Active),
24 2 => Ok(CompressibleConfigState::Deprecated),
25 _ => Err(CompressibleError::InvalidState(value)),
26 }
27 }
28}
29
30#[derive(Clone, Debug, AnchorDeserialize, PartialEq, AnchorSerialize, Copy, Pod, Zeroable)]
31#[repr(C)]
32pub struct CompressibleConfig {
33 pub version: u16,
35 pub state: u8,
40 pub bump: u8,
42 pub update_authority: Pubkey,
44 pub withdrawal_authority: Pubkey,
46 pub rent_sponsor: Pubkey,
51 pub compression_authority: Pubkey,
53 pub rent_sponsor_bump: u8,
54 pub compression_authority_bump: u8,
55 pub rent_config: RentConfig,
58
59 pub address_space: [Pubkey; 4],
61 pub _place_holder: [u8; 32],
62}
63
64impl CompressibleConfig {
65 pub fn validate_active(&self) -> Result<(), CompressibleError> {
67 let state = CompressibleConfigState::try_from(self.state)?;
68 if state != CompressibleConfigState::Active {
69 return Err(CompressibleError::InvalidState(self.state));
70 }
71 Ok(())
72 }
73
74 pub fn validate_not_inactive(&self) -> Result<(), CompressibleError> {
76 let state = CompressibleConfigState::try_from(self.state)?;
77 if state == CompressibleConfigState::Inactive {
78 return Err(CompressibleError::InvalidState(self.state));
79 }
80 Ok(())
81 }
82}
83
84#[cfg(feature = "anchor")]
85impl anchor_lang::Discriminator for CompressibleConfig {
86 const DISCRIMINATOR: &'static [u8] = &[180, 4, 231, 26, 220, 144, 55, 168];
87}
88
89#[cfg(feature = "anchor")]
90impl anchor_lang::AccountDeserialize for CompressibleConfig {
91 fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
92 let mut data: &[u8] = &buf[8..];
94 Self::deserialize(&mut data)
95 .map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into())
96 }
97
98 fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
99 use anchor_lang::Discriminator;
100
101 if buf.len() < 8 {
103 return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorNotFound.into());
104 }
105
106 let given_disc = &buf[..8];
107 if given_disc != Self::DISCRIMINATOR {
108 return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch.into());
109 }
110
111 Self::try_deserialize_unchecked(buf)
112 }
113}
114
115#[cfg(feature = "anchor")]
116impl anchor_lang::AccountSerialize for CompressibleConfig {
117 fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> anchor_lang::Result<()> {
118 use anchor_lang::Discriminator;
119
120 if writer.write_all(Self::DISCRIMINATOR).is_err() {
122 return Err(anchor_lang::error::ErrorCode::AccountDidNotSerialize.into());
123 }
124
125 if self.serialize(writer).is_err() {
127 return Err(anchor_lang::error::ErrorCode::AccountDidNotSerialize.into());
128 }
129 Ok(())
130 }
131}
132
133#[cfg(feature = "anchor")]
134impl anchor_lang::Owner for CompressibleConfig {
135 fn owner() -> anchor_lang::prelude::Pubkey {
136 pubkey!("Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX")
137 }
138}
139
140#[cfg(feature = "anchor")]
141impl anchor_lang::Space for CompressibleConfig {
142 const INIT_SPACE: usize = 8 + std::mem::size_of::<Self>(); }
144
145impl Discriminator for CompressibleConfig {
146 const LIGHT_DISCRIMINATOR: [u8; 8] = [180, 4, 231, 26, 220, 144, 55, 168];
147 const LIGHT_DISCRIMINATOR_SLICE: &'static [u8] = Self::LIGHT_DISCRIMINATOR.as_slice();
148}
149
150impl CompressibleConfig {
151 pub const LEN: usize = std::mem::size_of::<Self>();
152
153 pub fn light_token_v1(update_authority: Pubkey, withdrawal_authority: Pubkey) -> Self {
154 Self::new_light_token(
155 1,
156 true,
157 update_authority,
158 withdrawal_authority,
159 RentConfig::default(),
160 )
161 }
162
163 pub fn new_light_token(
164 version: u16,
165 active: bool,
166 update_authority: Pubkey,
167 withdrawal_authority: Pubkey,
168 rent_config: RentConfig,
169 ) -> Self {
170 let mut address_space = [Pubkey::default(); 4];
171 address_space[0] = pubkey!("amt2kaJA14v3urZbZvnc5v2np8jqvc4Z8zDep5wbtzx");
172 Self::new(
173 version,
174 active,
175 update_authority,
176 withdrawal_authority,
177 &pubkey!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"),
178 &pubkey!("Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX"),
179 address_space,
180 rent_config,
181 )
182 }
183
184 pub fn get_compression_authority_seeds(version: u16) -> [Vec<u8>; 2] {
185 [
186 b"compression_authority".to_vec(),
187 version.to_le_bytes().to_vec(),
188 ]
189 }
190
191 pub fn get_rent_sponsor_seeds(version: u16) -> [Vec<u8>; 2] {
192 [b"rent_sponsor".to_vec(), version.to_le_bytes().to_vec()]
193 }
194
195 #[allow(clippy::too_many_arguments)]
196 pub fn new(
197 version: u16,
198 active: bool,
199 update_authority: Pubkey,
200 withdrawal_authority: Pubkey,
201 rent_sponsor_program_id: &Pubkey,
202 owner_program_id: &Pubkey,
203 address_space: [Pubkey; 4],
204 rent_config: RentConfig,
205 ) -> Self {
206 let version_bytes = version.to_le_bytes();
207 let compression_authority_seeds = [
208 b"compression_authority".as_slice(),
209 version_bytes.as_slice(),
210 ];
211 let rent_sponsor_seeds = [b"rent_sponsor".as_slice(), version_bytes.as_slice()];
212 let (compression_authority, compression_authority_bump) =
213 solana_pubkey::Pubkey::find_program_address(
214 compression_authority_seeds.as_slice(),
215 owner_program_id,
216 );
217 let (rent_sponsor, rent_sponsor_bump) = solana_pubkey::Pubkey::find_program_address(
218 rent_sponsor_seeds.as_slice(),
219 rent_sponsor_program_id,
220 );
221 let (_, bump) = Self::derive_pda(owner_program_id, version);
222
223 Self {
224 version,
225 state: active as u8,
226 bump,
227 update_authority,
228 withdrawal_authority,
229 rent_sponsor,
230 compression_authority,
231 rent_sponsor_bump,
232 compression_authority_bump,
233 rent_config,
234 address_space,
235 _place_holder: [0u8; 32],
236 }
237 }
238
239 pub fn derive_pda(program_id: &Pubkey, config_bump: u16) -> (Pubkey, u8) {
241 Pubkey::find_program_address(
242 &[COMPRESSIBLE_CONFIG_SEED, &config_bump.to_le_bytes()],
243 program_id,
244 )
245 }
246
247 pub fn light_token_v1_config_pda() -> Pubkey {
249 Self::derive_pda(&pubkey!("Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX"), 1).0
250 }
251
252 pub fn derive_v1_config_pda(program_id: &Pubkey) -> (Pubkey, u8) {
254 Self::derive_pda(program_id, 1)
255 }
256
257 pub fn derive_default_pda(program_id: &Pubkey) -> (Pubkey, u8) {
259 Self::derive_pda(program_id, 0)
260 }
261
262 pub fn derive_compression_authority_pda(program_id: &Pubkey, version: u16) -> (Pubkey, u8) {
263 let seeds = Self::get_compression_authority_seeds(version);
264 let seeds_refs: [&[u8]; 2] = [seeds[0].as_slice(), seeds[1].as_slice()];
265 Pubkey::find_program_address(&seeds_refs, program_id)
266 }
267
268 pub fn derive_rent_sponsor_pda(program_id: &Pubkey, version: u16) -> (Pubkey, u8) {
269 let seeds = Self::get_rent_sponsor_seeds(version);
270 let seeds_refs: [&[u8]; 2] = [seeds[0].as_slice(), seeds[1].as_slice()];
271 Pubkey::find_program_address(&seeds_refs, program_id)
272 }
273
274 pub fn light_token_v1_compression_authority_pda() -> Pubkey {
276 Self::derive_compression_authority_pda(
277 &pubkey!("Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX"),
278 1,
279 )
280 .0
281 }
282 pub fn light_token_v1_rent_sponsor_pda() -> Pubkey {
284 Self::derive_rent_sponsor_pda(&pubkey!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"), 1).0
285 }
286}