|
| 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 | +`InterfaceAccount<'info, Mint>` and `InterfaceAccount<'info, TokenAccount>` now work with compressed tokens. |
| 14 | + |
| 15 | +## TS SDK |
| 16 | + |
| 17 | +The TS SDK adds `decompressIfNeeded()` to method builders. It auto-decompresses accounts before instruction execution when needed. |
| 18 | + |
| 19 | +```typescript |
| 20 | +await program.methods |
| 21 | + .swap(amount) |
| 22 | + .decompressIfNeeded() // auto-decompresses any compressed accounts |
| 23 | + .rpc(); |
| 24 | +``` |
| 25 | + |
| 26 | +### Limitations |
| 27 | + |
| 28 | +- Cross-program PDAs (seeds::program) not supported for auto-resolution |
| 29 | +- Instruction argument seeds require explicit account addresses |
| 30 | + |
| 31 | +## Rust Macros (light-sdk-macros) |
| 32 | + |
| 33 | +### `#[add_compressible_instructions]` |
| 34 | + |
| 35 | +Add to `#[program]` module. Generates compress/decompress instructions for listed accounts. |
| 36 | + |
| 37 | +```rust |
| 38 | +#[add_compressible_instructions( |
| 39 | + PoolState = (POOL_SEED, ctx.accounts.amm_config, ctx.accounts.token_0_mint, ctx.accounts.token_1_mint), |
| 40 | + LpVault = (is_token, POOL_VAULT_SEED, ctx.accounts.lp_mint, authority = AUTH_SEED), |
| 41 | +)] |
| 42 | +#[program] |
| 43 | +pub mod my_program { ... } |
| 44 | +``` |
| 45 | + |
| 46 | +### `#[derive(LightFinalize)]` + `#[compressible]` |
| 47 | + |
| 48 | +Auto-compresses PDAs at instruction end. |
| 49 | + |
| 50 | +```rust |
| 51 | +#[derive(Accounts, LightFinalize)] |
| 52 | +#[instruction(params: MyParams)] |
| 53 | +pub struct MyInstruction<'info> { |
| 54 | + #[account(mut)] |
| 55 | + pub creator: Signer<'info>, |
| 56 | + |
| 57 | + #[account(init, payer = creator, space = 8 + MyAccount::INIT_SPACE, seeds = [...], bump)] |
| 58 | + #[compressible( |
| 59 | + address_tree_info = params.address_tree_info, |
| 60 | + output_tree = params.output_state_tree_index |
| 61 | + )] |
| 62 | + pub my_account: Account<'info, MyAccount>, |
| 63 | + |
| 64 | + pub compression_config: AccountInfo<'info>, |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +### `#[light_instruction(params)]` |
| 69 | + |
| 70 | +Auto-calls `light_finalize()` at instruction end. |
| 71 | + |
| 72 | +```rust |
| 73 | +#[light_instruction(params)] |
| 74 | +pub fn create_account(ctx: Context<MyInstruction>, params: MyParams) -> Result<()> { |
| 75 | + // your logic |
| 76 | + Ok(()) // light_finalize auto-called here |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +## Required Program Setup |
| 81 | + |
| 82 | +```rust |
| 83 | +use light_sdk_types::CpiSigner; |
| 84 | +use light_macros::derive_light_cpi_signer; |
| 85 | + |
| 86 | +pub const LIGHT_CPI_SIGNER: CpiSigner = derive_light_cpi_signer!("YOUR_PROGRAM_ID"); |
| 87 | +``` |
| 88 | + |
| 89 | +## What's NOT Supported |
| 90 | + |
| 91 | +- `#[light_mint]` attribute - use `light_ctoken_sdk` directly for mint creation |
| 92 | +- Mixed PDAs + mints in single `LightFinalize` - handle mints manually |
| 93 | + |
| 94 | +## Gotchas |
| 95 | + |
| 96 | +1. **Fee payer field**: LightFinalize looks for `fee_payer`, `payer`, or `creator` field names |
| 97 | +2. **compression_config field**: Required field in accounts struct - must be `AccountInfo<'info>` |
| 98 | +3. **params struct**: First `#[instruction(...)]` arg is used for compression params. Must contain `proof: ValidityProof` and tree info fields. |
| 99 | +4. **remaining_accounts**: Light system accounts go in remaining_accounts - client must pass them |
| 100 | +5. **Token accounts/mints**: Use `light_ctoken_sdk` directly (`CreateCTokenAccountCpi`, `CTokenMintToCpi`). Don't use `#[compressible]` on token accounts. |
| 101 | +6. **Only PDAs**: `#[compressible]` only works on `Account<'info, T>` fields, not token accounts or mints |
| 102 | + |
| 103 | +## Example: Mixed PDA + Token Setup (raydium-cp-swap pattern) |
| 104 | + |
| 105 | +```rust |
| 106 | +#[derive(Accounts, LightFinalize)] |
| 107 | +#[instruction(params: MyParams)] |
| 108 | +pub struct Initialize<'info> { |
| 109 | + #[account(mut)] |
| 110 | + pub creator: Signer<'info>, |
| 111 | + |
| 112 | + // PDA - auto-compressed via LightFinalize |
| 113 | + #[account(init, ...)] |
| 114 | + #[compressible(address_tree_info = params.tree_info, output_tree = params.output_tree)] |
| 115 | + pub my_pda: Account<'info, MyState>, |
| 116 | + |
| 117 | + // Token vault - manual compression via SDK |
| 118 | + /// CHECK: Created via CreateCTokenAccountCpi |
| 119 | + #[account(mut)] |
| 120 | + pub token_vault: UncheckedAccount<'info>, |
| 121 | + |
| 122 | + // Mint - manual creation via SDK |
| 123 | + /// CHECK: Created via Light SDK CreateCMint |
| 124 | + #[account(mut)] |
| 125 | + pub my_mint: UncheckedAccount<'info>, |
| 126 | + |
| 127 | + pub compression_config: AccountInfo<'info>, |
| 128 | + // ... light system accounts in remaining_accounts |
| 129 | +} |
| 130 | + |
| 131 | +#[light_instruction(params)] |
| 132 | +pub fn initialize(ctx: Context<Initialize>, params: MyParams) -> Result<()> { |
| 133 | + // Manual token vault creation |
| 134 | + CreateCTokenAccountCpi { ... }.invoke_signed(...)?; |
| 135 | + |
| 136 | + // Manual mint creation (if needed) |
| 137 | + // ... use light_ctoken_sdk |
| 138 | + |
| 139 | + // PDA (my_pda) auto-compressed at end via light_finalize |
| 140 | + Ok(()) |
| 141 | +} |
| 142 | +``` |
0 commit comments