light_account/lib.rs
1//! # Light Accounts
2//!
3//! Rent-free Light Accounts and Light Token Accounts for Anchor programs.
4//!
5//! ## How It Works
6//!
7//! **Light Accounts (PDAs)**
8//! 1. Create a Solana PDA normally (Anchor `init`)
9//! 2. Add `#[light_account(init)]` - becomes a Light Account
10//! 3. Use it as normal Solana account
11//! 4. When rent runs out, account compresses (cold state)
12//! 5. State preserved on-chain, client loads when needed (hot state)
13//! 6. When account is hot, use it as normal Solana account
14//!
15//! **Light Token Accounts (associated token accounts, Vaults)**
16//! - Use `#[light_account(init, associated_token, ...)]` for associated token accounts
17//! - Use `#[light_account(init, token, ...)]` for program-owned vaults
18//! - Cold/hot lifecycle
19//!
20//! **Light Mints**
21//! - Created via `CreateMintsCpi`
22//! - Cold/hot lifecycle
23//!
24//! ## Quick Start
25//!
26//! ### 1. Program Setup
27//!
28//! ```rust,ignore
29//! use light_account::{derive_light_cpi_signer, light_program, CpiSigner};
30//!
31//! declare_id!("Your11111111111111111111111111111111111111");
32//!
33//! pub const LIGHT_CPI_SIGNER: CpiSigner =
34//! derive_light_cpi_signer!("Your11111111111111111111111111111111111111");
35//!
36//! #[light_program]
37//! #[program]
38//! pub mod my_program {
39//! // ...
40//! }
41//! ```
42//!
43//! ### 2. State Definition
44//!
45//! ```rust,ignore
46//! use light_account::{CompressionInfo, LightAccount};
47//!
48//! #[derive(Default, LightAccount)]
49//! #[account]
50//! pub struct UserRecord {
51//! pub compression_info: CompressionInfo, // Required field
52//! pub owner: Pubkey,
53//! pub data: u64,
54//! }
55//! ```
56//!
57//! ### 3. Accounts Struct
58//!
59//! ```rust,ignore
60//! use light_account::{CreateAccountsProof, LightAccounts};
61//!
62//! #[derive(AnchorSerialize, AnchorDeserialize)]
63//! pub struct CreateParams {
64//! pub create_accounts_proof: CreateAccountsProof,
65//! pub owner: Pubkey,
66//! }
67//!
68//! #[derive(Accounts, LightAccounts)]
69//! #[instruction(params: CreateParams)]
70//! pub struct CreateRecord<'info> {
71//! #[account(mut)]
72//! pub fee_payer: Signer<'info>,
73//!
74//! /// CHECK: Compression config
75//! pub compression_config: AccountInfo<'info>,
76//!
77//! /// CHECK: Rent sponsor
78//! #[account(mut)]
79//! pub pda_rent_sponsor: AccountInfo<'info>,
80//!
81//! #[account(init, payer = fee_payer, space = 8 + UserRecord::INIT_SPACE, seeds = [b"record", params.owner.as_ref()], bump)]
82//! #[light_account(init)]
83//! pub record: Account<'info, UserRecord>,
84//!
85//! pub system_program: Program<'info, System>,
86//! }
87//! ```
88//!
89//! ## Account Types
90//!
91//! ### 1. Light Account (PDA)
92//!
93//! ```rust,ignore
94//! #[account(init, payer = fee_payer, space = 8 + MyRecord::INIT_SPACE, seeds = [...], bump)]
95//! #[light_account(init)]
96//! pub record: Account<'info, MyRecord>,
97//! ```
98//!
99//! ### 2. Light Account (zero-copy)
100//!
101//! ```rust,ignore
102//! #[account(init, payer = fee_payer, space = 8 + size_of::<MyZcRecord>(), seeds = [...], bump)]
103//! #[light_account(init, zero_copy)]
104//! pub record: AccountLoader<'info, MyZcRecord>,
105//! ```
106//!
107//! ### 3. Light Token Account (vault)
108//!
109//! **With `init` (Anchor-created):**
110//! ```rust,ignore
111//! #[account(mut, seeds = [b"vault", mint.key().as_ref()], bump)]
112//! #[light_account(init, token::seeds = [b"vault", self.mint.key()], token::owner_seeds = [b"vault_authority"])]
113//! pub vault: UncheckedAccount<'info>,
114//! ```
115//!
116//! **Without `init` (manual creation via `CreateTokenAccountCpi`):**
117//! ```rust,ignore
118//! #[account(mut, seeds = [b"vault", mint.key().as_ref()], bump)]
119//! #[light_account(token::seeds = [b"vault", self.mint.key()], token::owner_seeds = [b"vault_authority"])]
120//! pub vault: UncheckedAccount<'info>,
121//! ```
122//!
123//! ### 4. Light Token Account (associated token account)
124//!
125//! **With `init` (Anchor-created):**
126//! ```rust,ignore
127//! #[account(mut)]
128//! #[light_account(init, associated_token::authority = owner, associated_token::mint = mint, associated_token::bump = params.bump)]
129//! pub token_account: UncheckedAccount<'info>,
130//! ```
131//!
132//! **Without `init` (manual creation via `CreateTokenAtaCpi`):**
133//! ```rust,ignore
134//! #[account(mut)]
135//! #[light_account(associated_token::authority = owner, associated_token::mint = mint)]
136//! pub token_account: UncheckedAccount<'info>,
137//! ```
138//!
139//! ### 5. Light Mint
140//!
141//! ```rust,ignore
142//! #[account(mut)]
143//! #[light_account(init,
144//! mint::signer = mint_signer, // PDA that signs mint creation
145//! mint::authority = mint_authority, // Mint authority
146//! mint::decimals = 9, // Token decimals
147//! mint::seeds = &[SEED, self.key.as_ref()], // Seeds for mint PDA
148//! mint::bump = params.bump, // Bump seed
149//! // Optional: PDA authority
150//! mint::authority_seeds = &[b"authority"],
151//! mint::authority_bump = params.auth_bump,
152//! // Optional: Token metadata
153//! mint::name = params.name,
154//! mint::symbol = params.symbol,
155//! mint::uri = params.uri,
156//! mint::update_authority = update_auth,
157//! mint::additional_metadata = params.metadata
158//! )]
159//! pub mint: UncheckedAccount<'info>,
160//! ```
161//!
162//! ## Required Derives
163//!
164//! | Derive | Use |
165//! |--------|-----|
166//! | `LightAccount` | State structs (must have `compression_info: CompressionInfo`) |
167//! | `LightAccounts` | Accounts structs with `#[light_account(...)]` fields |
168//!
169//! ## Required Macros
170//!
171//! | Macro | Use |
172//! |-------|-----|
173//! | `#[light_program]` | Program module (before `#[program]`) |
174//! | `derive_light_cpi_signer!` | CPI signer PDA constant |
175//! | `derive_light_rent_sponsor_pda!` | Rent sponsor PDA (optional) |
176
177pub use solana_account_info::AccountInfo;
178
179// ===== TYPE ALIASES (structs generic over AI, specialized with AccountInfo) =====
180
181pub type CpiAccounts<'c, 'info> =
182 light_sdk_types::cpi_accounts::v2::CpiAccounts<'c, AccountInfo<'info>>;
183
184pub type CompressCtx<'a, 'info> =
185 light_sdk_types::interface::program::compression::processor::CompressCtx<
186 'a,
187 AccountInfo<'info>,
188 >;
189
190pub type CompressDispatchFn<'info> =
191 light_sdk_types::interface::program::compression::processor::CompressDispatchFn<
192 AccountInfo<'info>,
193 >;
194
195pub type DecompressCtx<'a, 'info> =
196 light_sdk_types::interface::program::decompression::processor::DecompressCtx<
197 'a,
198 AccountInfo<'info>,
199 >;
200
201pub type ValidatedPdaContext<'info> =
202 light_sdk_types::interface::program::validation::ValidatedPdaContext<AccountInfo<'info>>;
203
204#[cfg(not(target_os = "solana"))]
205pub type PackedAccounts =
206 light_sdk_types::pack_accounts::PackedAccounts<solana_instruction::AccountMeta>;
207
208// ===== RE-EXPORTED TRAITS (generic over AI, used with explicit AccountInfo in impls) =====
209
210pub use light_account_checks::close_account;
211#[cfg(feature = "token")]
212pub use light_compressed_account::instruction_data::compressed_proof::CompressedProof;
213// ===== RE-EXPORTED CONCRETE TRAITS (no AI parameter) =====
214pub use light_sdk_types::interface::account::compression_info::{
215 claim_completed_epoch_rent, CompressAs, CompressedAccountData, CompressedInitSpace,
216 CompressionInfo, CompressionInfoField, CompressionState, HasCompressionInfo, Space,
217 COMPRESSION_INFO_SIZE, OPTION_COMPRESSION_INFO_SPACE,
218};
219#[cfg(not(target_os = "solana"))]
220pub use light_sdk_types::interface::account::pack::Pack;
221// ===== TOKEN-GATED RE-EXPORTS =====
222#[cfg(feature = "token")]
223pub use light_sdk_types::interface::account::token_seeds::{
224 PackedTokenData, TokenDataWithPackedSeeds, TokenDataWithSeeds,
225};
226// Mint creation CPI types and functions
227#[cfg(feature = "token")]
228pub use light_sdk_types::interface::cpi::create_mints::{
229 derive_mint_compressed_address as derive_mint_compressed_address_generic,
230 get_output_queue_next_index, CreateMints, CreateMintsCpi, CreateMintsParams,
231 CreateMintsStaticAccounts, SingleMintParams, DEFAULT_RENT_PAYMENT, DEFAULT_WRITE_TOP_UP,
232};
233// Token account/ATA creation CPI types and functions
234#[cfg(feature = "token")]
235pub use light_sdk_types::interface::cpi::create_token_accounts::{
236 derive_associated_token_account as derive_associated_token_account_generic,
237 CreateTokenAccountCpi, CreateTokenAccountRentFreeCpi, CreateTokenAtaCpi,
238 CreateTokenAtaCpiIdempotent, CreateTokenAtaRentFreeCpi,
239};
240// ===== RE-EXPORTED GENERIC FUNCTIONS (AI inferred from call-site args) =====
241pub use light_sdk_types::interface::cpi::invoke::invoke_light_system_program;
242#[cfg(feature = "token")]
243pub use light_sdk_types::interface::program::decompression::processor::process_decompress_accounts_idempotent;
244#[cfg(feature = "token")]
245pub use light_sdk_types::interface::program::decompression::token::prepare_token_account_for_decompression;
246#[cfg(feature = "token")]
247pub use light_sdk_types::interface::program::variant::{PackedTokenSeeds, UnpackedTokenSeeds};
248pub use light_sdk_types::interface::{
249 account::{
250 light_account::{AccountType, LightAccount},
251 pack::Unpack,
252 pda_seeds::{HasTokenVariant, PdaSeedDerivation},
253 },
254 accounts::{
255 finalize::{LightFinalize, LightPreInit},
256 init_compressed_account::{prepare_compressed_account_on_init, reimburse_rent},
257 },
258 cpi::{
259 account::CpiAccountsTrait,
260 invoke::{invoke_write_pdas_to_cpi_context, InvokeLightSystemProgram},
261 LightCpi,
262 },
263 create_accounts_proof::CreateAccountsProof,
264 program::{
265 compression::{
266 pda::prepare_account_for_compression,
267 processor::{process_compress_pda_accounts_idempotent, CompressAndCloseParams},
268 },
269 config::{
270 process_initialize_light_config_checked, process_update_light_config,
271 InitializeLightConfigParams, LightConfig, UpdateLightConfigParams, LIGHT_CONFIG_SEED,
272 MAX_ADDRESS_TREES_PER_SPACE,
273 },
274 decompression::{
275 pda::prepare_account_for_decompression,
276 processor::{
277 process_decompress_pda_accounts_idempotent, DecompressIdempotentParams,
278 DecompressVariant,
279 },
280 },
281 validation::{
282 extract_tail_accounts, is_pda_initialized, should_skip_compression,
283 split_at_system_accounts_offset, validate_compress_accounts,
284 validate_decompress_accounts,
285 },
286 variant::{IntoVariant, LightAccountVariantTrait, PackedLightAccountVariantTrait},
287 },
288 rent,
289};
290#[cfg(feature = "token")]
291pub use light_token_interface::instructions::extensions::ExtensionInstructionData as TokenExtensionInstructionData;
292// Token-interface re-exports for macro-generated code
293#[cfg(feature = "token")]
294pub use light_token_interface::instructions::extensions::TokenMetadataInstructionData;
295#[cfg(feature = "token")]
296pub use light_token_interface::state::AdditionalMetadata;
297/// Re-export Token state struct for client-side use.
298#[cfg(feature = "token")]
299pub use light_token_interface::state::{AccountState, Token};
300
301/// Token sub-module for paths like `light_account::token::TokenDataWithSeeds`.
302#[cfg(feature = "token")]
303pub mod token {
304 pub use light_sdk_types::interface::{
305 account::token_seeds::{
306 ExtensionInstructionData, MultiInputTokenDataWithContext, PackedTokenData,
307 TokenDataWithPackedSeeds, TokenDataWithSeeds,
308 },
309 program::decompression::token::prepare_token_account_for_decompression,
310 };
311 pub use light_token_interface::state::{AccountState, Token};
312}
313
314/// Compression info sub-module for paths like `light_account::compression_info::CompressedInitSpace`.
315pub mod compression_info {
316 pub use light_sdk_types::interface::account::compression_info::*;
317}
318
319// ===== CPI / SDK-TYPES RE-EXPORTS =====
320
321pub use light_sdk_types::{
322 cpi_accounts::CpiAccountsConfig, cpi_context_write::CpiContextWriteAccounts,
323 interface::program::config::create::process_initialize_light_config,
324};
325
326/// Sub-module for generic `PackedAccounts<AM>` (not specialized to AccountMeta).
327#[cfg(not(target_os = "solana"))]
328pub mod interface {
329 pub mod instruction {
330 pub use light_sdk_types::pack_accounts::PackedAccounts;
331 }
332}
333
334/// Sub-module for account_meta types (e.g. `CompressedAccountMetaNoLamportsNoAddress`).
335pub mod account_meta {
336 pub use light_sdk_types::instruction::account_meta::*;
337}
338
339// ===== ACCOUNT-CHECKS RE-EXPORTS (used by macro-generated code) =====
340
341/// Re-export `light_account_checks` so consumers can use `light_account::light_account_checks::*`.
342pub extern crate light_account_checks;
343// ===== CONVENIENCE RE-EXPORTS =====
344pub use light_account_checks::{
345 discriminator::Discriminator as LightDiscriminator, packed_accounts, AccountInfoTrait,
346 AccountMetaTrait,
347};
348pub use light_compressed_account::instruction_data::compressed_proof::ValidityProof;
349pub use light_compressible::rent::RentConfig;
350pub use light_macros::{derive_light_cpi_signer, derive_light_cpi_signer_pda};
351pub use light_sdk_macros::{
352 // Attribute macros
353 account,
354 // Proc macros
355 derive_light_rent_sponsor,
356 derive_light_rent_sponsor_pda,
357 light_program,
358 // Derive macros
359 AnchorDiscriminator as Discriminator,
360 CompressAs,
361 HasCompressionInfo,
362 LightAccount,
363 LightAccounts,
364 LightDiscriminator,
365 LightHasher,
366 LightHasherSha,
367 LightProgram,
368};
369pub use light_sdk_types::{
370 constants,
371 constants::{CPI_AUTHORITY_PDA_SEED, RENT_SPONSOR_SEED},
372 error::LightSdkTypesError,
373 instruction::*,
374 interface::account::size::Size,
375 CpiSigner,
376};
377
378/// Hasher re-exports for macro-generated code paths like `light_account::hasher::DataHasher`.
379pub mod hasher {
380 pub use light_hasher::{errors::HasherError, DataHasher, Hasher};
381}
382
383/// Re-export LIGHT_TOKEN_PROGRAM_ID as Pubkey for Anchor's `#[account(address = ...)]`.
384pub const LIGHT_TOKEN_PROGRAM_ID: solana_pubkey::Pubkey =
385 solana_pubkey::Pubkey::new_from_array(constants::LIGHT_TOKEN_PROGRAM_ID);
386
387/// Default compressible config PDA for the Light Token Program.
388pub const LIGHT_TOKEN_CONFIG: solana_pubkey::Pubkey =
389 solana_pubkey::Pubkey::new_from_array(constants::LIGHT_TOKEN_CONFIG);
390
391/// Default rent sponsor PDA for the Light Token Program.
392pub const LIGHT_TOKEN_RENT_SPONSOR: solana_pubkey::Pubkey =
393 solana_pubkey::Pubkey::new_from_array(constants::LIGHT_TOKEN_RENT_SPONSOR);
394
395// ===== UTILITY FUNCTIONS =====
396
397/// Converts a [`LightSdkTypesError`] into an [`anchor_lang::error::Error`].
398///
399/// Use with `.map_err(light_err)` in Anchor instruction handlers to disambiguate
400/// the multiple `From` implementations on `LightSdkTypesError`.
401#[cfg(feature = "anchor")]
402pub fn light_err(e: LightSdkTypesError) -> anchor_lang::error::Error {
403 anchor_lang::error::Error::from(e)
404}
405
406/// Derives the rent sponsor PDA for a given program.
407///
408/// Seeds: `["rent_sponsor"]`
409pub fn derive_rent_sponsor_pda(program_id: &solana_pubkey::Pubkey) -> (solana_pubkey::Pubkey, u8) {
410 solana_pubkey::Pubkey::find_program_address(&[constants::RENT_SPONSOR_SEED], program_id)
411}
412
413/// Find the mint PDA address for a given mint seed.
414///
415/// Returns `([u8; 32], u8)` -- the PDA address and bump.
416#[cfg(feature = "token")]
417pub fn find_mint_address(mint_seed: &[u8; 32]) -> ([u8; 32], u8) {
418 light_sdk_types::interface::cpi::create_mints::find_mint_address::<AccountInfo<'static>>(
419 mint_seed,
420 )
421}
422
423/// Derive the compressed mint address from a mint seed and address tree pubkey.
424#[cfg(feature = "token")]
425pub fn derive_mint_compressed_address(
426 mint_seed: &[u8; 32],
427 address_tree_pubkey: &[u8; 32],
428) -> [u8; 32] {
429 derive_mint_compressed_address_generic::<AccountInfo<'static>>(mint_seed, address_tree_pubkey)
430}
431
432/// Derive the associated token account address for a given owner and mint.
433///
434/// Returns `(Pubkey, u8)` -- the ATA address and bump seed.
435#[cfg(feature = "token")]
436pub fn derive_associated_token_account(
437 owner: &solana_pubkey::Pubkey,
438 mint: &solana_pubkey::Pubkey,
439) -> (solana_pubkey::Pubkey, u8) {
440 let (bytes, bump) = derive_associated_token_account_generic::<AccountInfo<'static>>(
441 &owner.to_bytes(),
442 &mint.to_bytes(),
443 );
444 (solana_pubkey::Pubkey::from(bytes), bump)
445}