light_sdk/lib.rs
1//! The base library to use Compressed Accounts in Solana on-chain Rust and Anchor programs.
2//! Compressed accounts do not require rent-exemption, which makes them suitable for:
3//! - user owned accounts
4//! - not for config accounts which are often read
5//! - not pool accounts, since compressed accounts cannot be used concurrently
6//!
7//! Compressed Accounts store state as account hashes in State Merkle trees.
8//! and unique addresses in Address Merkle trees.
9//! Validity proofs (zero-knowledge proofs) verify that compressed account
10//! state exists and new addresses do not exist yet.
11//!
12//! - No rent exemption payment required.
13//! - Constant 128-byte validity proof per transaction for one or multiple compressed accounts and addresses.
14//! - Compressed account data is sent as instruction data when accessed.
15//! - State and address trees are managed by the protocol.
16//!
17//! For full program examples, see the [Program Examples](https://github.com/Lightprotocol/program-examples).
18//! For detailed documentation, visit [zkcompression.com](https://www.zkcompression.com/).
19//! For pinocchio solana program development see [`light-sdk-pinocchio`](https://docs.rs/light-sdk-pinocchio).
20//! For rust client development see [`light-client`](https://docs.rs/light-client).
21//! For rust program testing see [`light-program-test`](https://docs.rs/light-program-test).
22//! For local test validator with light system programs see [Light CLI](https://www.npmjs.com/package/@lightprotocol/zk-compression-cli).
23//!
24//! ## Difference to Light-Accounts (Light-PDA)
25//! Light-PDAs are Solana accounts with sponsored rent-exemption.
26//! There is no proof required for interactions with Light-PDAs which makes
27//! them suitable for Defi Usecases. Compressed PDAs don't require rent-exemption,
28//! but a proof for interactions.
29//!
30//! # Using Compressed Accounts in Solana Programs
31//! 1. [`Instruction`](crate::instruction)
32//! - [`CompressedAccountMeta`](crate::instruction::account_meta::CompressedAccountMeta) - Compressed account metadata structs for instruction data.
33//! - [`PackedAccounts`](crate::instruction::PackedAccounts) - Abstraction to prepare accounts offchain for instructions with compressed accounts.
34//! - [`ValidityProof`](crate::instruction::ValidityProof) - Proves that new addresses don't exist yet, and compressed account state exists.
35//! 2. Compressed Account in Program
36//! - [`LightAccount`](crate::account) - Compressed account abstraction similar to anchor Account.
37//! - [`derive_address`](crate::address) - Create a compressed account address.
38//! - [`LightDiscriminator`] - DeriveMacro to derive a compressed account discriminator.
39//! 3. [`Cpi`](crate::cpi)
40//! - [`CpiAccounts`](crate::cpi::v1::CpiAccounts) - Prepare accounts to cpi the light system program.
41//! - [`LightSystemProgramCpi`](crate::cpi::v1::LightSystemProgramCpi) - Prepare instruction data to cpi the light system program.
42//! - [`InvokeLightSystemProgram::invoke`](crate::cpi) - Invoke the light system program via cpi.
43//!
44//! # Client Program Interaction Flow
45//! ```text
46//! ├─ 𝐂𝐥𝐢𝐞𝐧𝐭
47//! │ ├─ Get ValidityProof from RPC.
48//! │ ├─ pack accounts with PackedAccounts into PackedAddressTreeInfo and PackedStateTreeInfo.
49//! │ ├─ pack CompressedAccountMeta.
50//! │ ├─ Build Instruction from PackedAccounts and CompressedAccountMetas.
51//! │ └─ Send transaction.
52//! │
53//! └─ 𝐂𝐮𝐬𝐭𝐨𝐦 𝐏𝐫𝐨𝐠𝐫𝐚𝐦
54//! ├─ CpiAccounts parse accounts consistent with PackedAccounts.
55//! ├─ LightAccount instantiates from CompressedAccountMeta.
56//! │
57//! └─ 𝐋𝐢𝐠𝐡𝐭 𝐒𝐲𝐬𝐭𝐞𝐦 𝐏𝐫𝐨𝐠𝐫𝐚𝐦 𝐂𝐏𝐈
58//! ├─ Verify ValidityProof.
59//! ├─ Update State Merkle tree.
60//! ├─ Update Address Merkle tree.
61//! └─ Complete atomic state transition.
62//! ```
63//!
64//! # Features
65//! 1. `anchor` - Derives AnchorSerialize, AnchorDeserialize instead of BorshSerialize, BorshDeserialize.
66//!
67//! 2. `v2`
68//! - available on devnet, localnet, and light-program-test.
69//! - Support for optimized v2 light system program instructions.
70//!
71//! 3. `cpi-context` - Enables CPI context operations for batched compressed account operations.
72//! - available on devnet, localnet, and light-program-test.
73//! - Enables the use of one validity proof across multiple cpis from different programs in one instruction.
74//! - For example spending compressed tokens (owned by the ctoken program) and updating a compressed pda (owned by a custom program)
75//! with one validity proof.
76//! - An instruction should not use more than one validity proof.
77//! - Requires the v2 feature.
78//!
79//! ### Example Solana program code to create a compressed account
80//! ```rust, compile_fail
81//! use anchor_lang::{prelude::*, Discriminator};
82//! use light_sdk::{
83//! account::LightAccount,
84//! address::v1::derive_address,
85//! cpi::{v1::LightSystemProgramCpi, CpiAccounts, InvokeLightSystemProgram, LightCpiInstruction},
86//! derive_light_cpi_signer,
87//! instruction::{account_meta::CompressedAccountMeta, PackedAddressTreeInfo},
88//! CpiSigner, LightDiscriminator, LightHasher, ValidityProof,
89//! };
90//!
91//! declare_id!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt");
92//!
93//! pub const LIGHT_CPI_SIGNER: CpiSigner =
94//! derive_light_cpi_signer!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt");
95//!
96//! #[program]
97//! pub mod counter {
98//!
99//! use super::*;
100//!
101//! pub fn create_compressed_account<'info>(
102//! ctx: Context<'_, '_, '_, 'info, CreateCompressedAccount<'info>>,
103//! proof: ValidityProof,
104//! address_tree_info: PackedAddressTreeInfo,
105//! output_tree_index: u8,
106//! ) -> Result<()> {
107//! let light_cpi_accounts = CpiAccounts::new(
108//! ctx.accounts.fee_payer.as_ref(),
109//! ctx.remaining_accounts,
110//! crate::LIGHT_CPI_SIGNER,
111//! )?;
112//!
113//! let (address, address_seed) = derive_address(
114//! &[b"counter", ctx.accounts.fee_payer.key().as_ref()],
115//! &address_tree_info.get_tree_pubkey(&light_cpi_accounts)?,
116//! &crate::ID,
117//! );
118//! let new_address_params = address_tree_info
119//! .into_new_address_params_packed(address_seed);
120//!
121//! let mut my_compressed_account = LightAccount::<CounterAccount>::new_init(
122//! &crate::ID,
123//! Some(address),
124//! output_tree_index,
125//! );
126//!
127//! my_compressed_account.owner = ctx.accounts.fee_payer.key();
128//!
129//! LightSystemProgramCpi::new_cpi(crate::LIGHT_CPI_SIGNER, proof)
130//! .with_light_account(my_compressed_account)?
131//! .with_new_addresses(&[new_address_params])
132//! .invoke(light_cpi_accounts)
133//! }
134//! }
135//!
136//! #[derive(Accounts)]
137//! pub struct CreateCompressedAccount<'info> {
138//! #[account(mut)]
139//! pub fee_payer: Signer<'info>,
140//! }
141//!
142//! #[derive(Clone, Debug, Default, LightDiscriminator)]
143//!pub struct CounterAccount {
144//! pub owner: Pubkey,
145//! pub counter: u64
146//!}
147//! ```
148
149/// Compressed account abstraction similar to anchor Account.
150pub mod account;
151pub use account::sha::LightAccount;
152
153/// Functions to derive compressed account addresses.
154pub mod address;
155/// Utilities to invoke the light-system-program via cpi.
156pub mod cpi;
157pub mod error;
158/// Utilities to build instructions for programs with compressed accounts.
159pub mod instruction;
160pub mod legacy;
161pub mod proof;
162/// Transfer compressed sol between compressed accounts.
163pub mod transfer;
164pub mod utils;
165
166pub use proof::borsh_compat;
167#[cfg(feature = "merkle-tree")]
168pub mod merkle_tree;
169
170// Re-export hasher types for macro-generated code
171pub mod hasher {
172 pub use light_hasher::{
173 errors, hash_to_field_size, sha256, to_byte_array, DataHasher, Hasher, HasherError,
174 Poseidon, Sha256,
175 };
176}
177
178// Re-export compressed_account types for macro-generated code
179pub mod compressed_account {
180 pub use light_compressed_account::{
181 address::derive_address,
182 instruction_data::{
183 data::NewAddressParamsAssignedPacked, with_account_info::CompressedAccountInfo,
184 },
185 };
186}
187
188// Re-export sdk_types for macro-generated code
189pub mod sdk_types {
190 #[cfg(feature = "cpi-context")]
191 pub use light_sdk_types::cpi_context_write::CpiContextWriteAccounts;
192 pub use light_sdk_types::{
193 cpi_accounts::CpiAccountsConfig,
194 instruction::{PackedAddressTreeInfo, PackedAddressTreeInfoExt},
195 RentSponsor,
196 };
197}
198
199#[cfg(feature = "anchor")]
200use anchor_lang::{AnchorDeserialize, AnchorSerialize};
201#[cfg(not(feature = "anchor"))]
202use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize};
203pub use light_account_checks::{self, discriminator::Discriminator as LightDiscriminator};
204// Re-export as extern crate so downstream crates can use `::light_hasher::` paths
205pub extern crate light_hasher;
206#[cfg(feature = "poseidon")]
207use light_hasher::DataHasher;
208pub use light_macros::{derive_light_cpi_signer, derive_light_cpi_signer_pda};
209pub use light_sdk_macros::{
210 derive_light_rent_sponsor, derive_light_rent_sponsor_pda, AnchorDiscriminator,
211 LightDiscriminator, LightHasher, LightHasherSha,
212};
213pub use light_sdk_types::{constants, instruction::PackedAddressTreeInfoExt, CpiSigner};
214use solana_account_info::AccountInfo;
215use solana_instruction::AccountMeta;
216use solana_program_error::ProgramError;
217use solana_pubkey::Pubkey;
218
219// Re-export SDK traits
220pub use crate::cpi::LightCpiInstruction;
221
222pub trait PubkeyTrait {
223 fn to_solana_pubkey(&self) -> Pubkey;
224 fn to_array(&self) -> [u8; 32];
225}
226
227impl PubkeyTrait for [u8; 32] {
228 fn to_solana_pubkey(&self) -> Pubkey {
229 Pubkey::from(*self)
230 }
231
232 fn to_array(&self) -> [u8; 32] {
233 *self
234 }
235}
236
237#[cfg(not(feature = "anchor"))]
238impl PubkeyTrait for Pubkey {
239 fn to_solana_pubkey(&self) -> Pubkey {
240 *self
241 }
242
243 fn to_array(&self) -> [u8; 32] {
244 self.to_bytes()
245 }
246}
247
248#[cfg(feature = "anchor")]
249impl PubkeyTrait for anchor_lang::prelude::Pubkey {
250 fn to_solana_pubkey(&self) -> Pubkey {
251 Pubkey::from(self.to_bytes())
252 }
253
254 fn to_array(&self) -> [u8; 32] {
255 self.to_bytes()
256 }
257}