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