|
| 1 | +# Light Protocol Compression Integration |
| 2 | + |
| 3 | +## Fork Changes |
| 4 | + |
| 5 | +This fork adds compressed token support to anchor-spl's `token_interface`: |
| 6 | + |
| 7 | +```rust |
| 8 | +// anchor-spl/src/token_interface.rs |
| 9 | +pub const COMPRESSED_TOKEN_ID: Pubkey = ...; |
| 10 | +static IDS: [Pubkey; 3] = [spl_token::ID, spl_token_2022::ID, COMPRESSED_TOKEN_ID]; |
| 11 | +``` |
| 12 | + |
| 13 | +## TS SDK |
| 14 | + |
| 15 | +The TS SDK adds `decompressIfNeeded()` to method builders: |
| 16 | + |
| 17 | +```typescript |
| 18 | +await program.methods |
| 19 | + .swap(amount) |
| 20 | + .decompressIfNeeded() |
| 21 | + .rpc(); |
| 22 | +``` |
| 23 | + |
| 24 | +## Rust Macros (light-sdk-macros) |
| 25 | + |
| 26 | +### `#[add_compressible_instructions]` |
| 27 | + |
| 28 | +Generates compress/decompress instructions for listed accounts. |
| 29 | + |
| 30 | +```rust |
| 31 | +#[add_compressible_instructions( |
| 32 | + PoolState = (POOL_SEED, ctx.accounts.amm_config, ctx.accounts.token_0_mint, ctx.accounts.token_1_mint), |
| 33 | +)] |
| 34 | +#[program] |
| 35 | +pub mod my_program { ... } |
| 36 | +``` |
| 37 | + |
| 38 | +### `#[derive(LightFinalize)]` + `#[compressible]` |
| 39 | + |
| 40 | +Auto-compresses PDAs at instruction end. |
| 41 | + |
| 42 | +```rust |
| 43 | +#[derive(Accounts, LightFinalize)] |
| 44 | +#[instruction(params: MyParams)] |
| 45 | +pub struct MyInstruction<'info> { |
| 46 | + #[account(mut)] |
| 47 | + pub creator: Signer<'info>, |
| 48 | + |
| 49 | + #[account(init, payer = creator, space = 8 + MyAccount::INIT_SPACE, seeds = [...], bump)] |
| 50 | + #[compressible( |
| 51 | + address_tree_info = params.address_tree_info, |
| 52 | + output_tree = params.output_state_tree_index |
| 53 | + )] |
| 54 | + pub my_account: Account<'info, MyAccount>, |
| 55 | + |
| 56 | + pub compression_config: AccountInfo<'info>, |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +### `#[light_instruction(params)]` |
| 61 | + |
| 62 | +Auto-calls `light_finalize()` at instruction end. |
| 63 | + |
| 64 | +```rust |
| 65 | +#[light_instruction(params)] |
| 66 | +pub fn create_account(ctx: Context<MyInstruction>, params: MyParams) -> Result<()> { |
| 67 | + // your logic |
| 68 | + Ok(()) // light_finalize auto-called here |
| 69 | +} |
| 70 | +``` |
| 71 | + |
| 72 | +### `#[light_mint]` (single mint) |
| 73 | + |
| 74 | +Creates a compressed mint at instruction end. |
| 75 | + |
| 76 | +```rust |
| 77 | +#[derive(Accounts, LightFinalize)] |
| 78 | +#[instruction(params: MyParams)] |
| 79 | +pub struct CreateMint<'info> { |
| 80 | + #[account(mut)] |
| 81 | + pub creator: Signer<'info>, |
| 82 | + |
| 83 | + /// CHECK: Mint signer PDA that seeds the mint address |
| 84 | + pub mint_signer: UncheckedAccount<'info>, |
| 85 | + |
| 86 | + /// CHECK: The mint account to be created |
| 87 | + #[account(mut)] |
| 88 | + #[light_mint( |
| 89 | + mint_signer = mint_signer, |
| 90 | + authority = authority, |
| 91 | + decimals = 9, |
| 92 | + address_tree_info = params.mint_address_tree_info, |
| 93 | + output_tree = params.output_state_tree_index |
| 94 | + )] |
| 95 | + pub my_mint: UncheckedAccount<'info>, |
| 96 | + |
| 97 | + pub authority: UncheckedAccount<'info>, |
| 98 | + pub compression_config: AccountInfo<'info>, |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +## Required Program Setup |
| 103 | + |
| 104 | +```rust |
| 105 | +use light_sdk_types::CpiSigner; |
| 106 | +use light_macros::derive_light_cpi_signer; |
| 107 | + |
| 108 | +pub const LIGHT_CPI_SIGNER: CpiSigner = derive_light_cpi_signer!("YOUR_PROGRAM_ID"); |
| 109 | +``` |
| 110 | + |
| 111 | +## Params Struct Requirements |
| 112 | + |
| 113 | +Your params struct (first arg of `#[instruction(...)]`) must have: |
| 114 | + |
| 115 | +```rust |
| 116 | +pub struct MyParams { |
| 117 | + pub address_tree_info: PackedAddressTreeInfo, // for each compressible/mint |
| 118 | + pub output_state_tree_index: u8, |
| 119 | + pub proof: ValidityProof, // required for mint creation |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +## Current Limitations |
| 124 | + |
| 125 | +- **Multiple mints**: Not supported via macro. Use `light_ctoken_sdk::ctoken::CreateCMintCpi` directly with CPI context batching. |
| 126 | +- **Mixed PDAs + mints**: Not supported via macro. Handle mints manually. |
| 127 | +- **Token accounts**: Use `light_ctoken_sdk` directly (`CreateCTokenAccountCpi`, `CTokenMintToCpi`). |
| 128 | + |
| 129 | +## Gotchas |
| 130 | + |
| 131 | +1. **Fee payer field**: Must be named `fee_payer`, `payer`, or `creator` |
| 132 | +2. **compression_config field**: Required as `AccountInfo<'info>` |
| 133 | +3. **remaining_accounts**: Light system accounts come via remaining_accounts - client must pass them in v2 format |
| 134 | +4. **PackedAddressTreeInfo fields**: Uses `address_merkle_tree_pubkey_index`, `address_queue_pubkey_index`, `root_index` |
| 135 | + |
| 136 | +## raydium-cp-swap Usage |
| 137 | + |
| 138 | +Currently uses: |
| 139 | +- `#[compressible]` on `pool_state` for auto-compression |
| 140 | +- Manual `CreateCTokenAccountCpi` for token vaults |
| 141 | +- Manual mint handling (LP mint created by client, no `#[light_mint]`) |
0 commit comments