From 512b473827c7a2fa963945a2f230197cd3ce0822 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 27 Nov 2025 01:01:53 +0000 Subject: [PATCH 001/143] Add compressed-token-program documentation with cMint and cToken guides --- compressed-token-program/c-token-program.mdx | 370 ++++++++++++++++++ compressed-token-program/cmint/cmint.mdx | 131 +++++++ .../cmint/update-metadata.mdx | 89 +++++ .../ctoken/SPL-to-ctoken-transfer.mdx | 36 ++ .../ctoken/close-ctoken.mdx | 38 ++ .../ctoken/compress-and-close.mdx | 33 ++ .../ctoken/create-ata.mdx | 103 +++++ .../ctoken/create-ctoken.mdx | 120 ++++++ .../ctoken/ctoken-to-spl-decompress.mdx | 49 +++ .../ctoken/ctoken-transfer.mdx | 53 +++ .../ctoken/mint-actions.mdx | 43 ++ .../ctoken/mint-ctokens.mdx | 33 ++ compressed-token-program/overview.mdx | 57 +++ docs.json | 234 ++++++++--- 14 files changed, 1337 insertions(+), 52 deletions(-) create mode 100644 compressed-token-program/c-token-program.mdx create mode 100644 compressed-token-program/cmint/cmint.mdx create mode 100644 compressed-token-program/cmint/update-metadata.mdx create mode 100644 compressed-token-program/ctoken/SPL-to-ctoken-transfer.mdx create mode 100644 compressed-token-program/ctoken/close-ctoken.mdx create mode 100644 compressed-token-program/ctoken/compress-and-close.mdx create mode 100644 compressed-token-program/ctoken/create-ata.mdx create mode 100644 compressed-token-program/ctoken/create-ctoken.mdx create mode 100644 compressed-token-program/ctoken/ctoken-to-spl-decompress.mdx create mode 100644 compressed-token-program/ctoken/ctoken-transfer.mdx create mode 100644 compressed-token-program/ctoken/mint-actions.mdx create mode 100644 compressed-token-program/ctoken/mint-ctokens.mdx create mode 100644 compressed-token-program/overview.mdx diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx new file mode 100644 index 00000000..142135b2 --- /dev/null +++ b/compressed-token-program/c-token-program.mdx @@ -0,0 +1,370 @@ +--- +title: Compressed Token Program +sidebarTitle: Overview New +description: Overview of cMints, cTokens, and compressed token accounts. +--- + +import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; + +The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, cTokens and compressed tokens. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Account TypeKey Features
**[cMint](#cmint-accounts)**Compressed account +
    +
  • **Rent-free mint accounts** (similar to SPL mints)
  • +
  • Custom [Token Metadata](#token-metadata) **extension**
  • +
+
**[cToken](#ctoken-account)**Solana account +
    +
  • **Token accounts** derived from cMints (**SPL-compatible**)
  • +
  • **Sponsored rent** exemption via [compressible extension](#compressible) (200x cheaper than SPL tokens)
  • +
  • Use for **active token accounts** with frequent writes (trading, etc.)
  • +
+
**[Compressed Token](#compressed-token-account)**Compressed account +
    +
  • **Compressed account** with `TokenData` field
  • +
  • **Rent-free** and SPL-compatible
  • +
  • Use for **storage of inactive tokens** or **token distribution**
  • +
  • cToken accounts with the **[compressible extension](#compressible) are automatically compressed/decompressed** when active/inactive.
  • +
+
+ +_add graphic of all tokens here_ + + + +**SPL mints and tokens** owned by the [Token Program](https://github.com/solana-program/token) or [Token-2022](https://github.com/solana-program/token-2022) **require rent** by default.
+You can **migrate SPL token accounts to cTokens** with compressible extension for **sponsored rent-exemption** while keeping the **same interoparability**. +
+ +# cMint Accounts + + +* **cMints are compressed accounts** and **cannot be decompressed**. +* SPL mints can not be compressed to cMints. + + +cMints **uniquely represent a token on Solana and store its global metadata**, similar to SPL mint accounts with few core differences: +1. cMint accounts are **rent-free**. +2. Tokens created from cMints are **cTokens** (fully compatible with SPL tokens). +3. Token metadata (name, symbol, URI) is stored as an extension within the compressed mint account. + + + + + Diagram showing cMint compressed account structure with three components: Hash (identifier for cMint in purple box), Account (struct containing BaseMint with SPL-compatible fields, cMint Data for program state, and optional Extensions for Token Metadata), and BasemintData (containing Supply, Decimals, Mint Authority, and Freeze Authority fields) with Token Metadata extension + + + + ```rust + pub struct CompressedMint { + // SPL mint layout + pub base: BaseMint, + // cMint state used by the Compressed Token Program + pub metadata: CompressedMintMetadata + // Field for Token Metadata extension + pub extensions: Option>, + } + ``` + + + + +Find the [source code of cMints here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/mint/compressed_mint.rs). + + +The `metadata` field is used by the Compressed Token Program to store the internal state of a cMint. + +The `BaseMint` field replicates the field layout and serialization format of [SPL Mint accounts](https://solana.com/docs/tokens#mint-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens and mints. + +Here is how cMints and SPL mints compare: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldcMintSPL Mint
mint_authority
supply
decimals
is_initialized
freeze_authority
cMint Data-
Extensionsvia Token-2022
+
+ + ```rust + pub struct BaseMint { + /// Optional authority used to mint new tokens. The mint authority may only + /// be provided during mint creation. If no mint authority is present + /// then the mint has a fixed supply and no further tokens may be + /// minted. + pub mint_authority: Option, + /// Total supply of tokens. + pub supply: u64, + /// Number of base 10 digits to the right of the decimal place. + pub decimals: u8, + /// Is initialized - for SPL compatibility + pub is_initialized: bool, + /// Optional authority to freeze token accounts. + pub freeze_authority: Option, + } + ``` + +
+ +# cToken Account + + +**cToken accounts are Solana accounts**, not compressed accounts. + + +A cToken account holds token balances like SPL Token accounts: +* A wallet needs a cToken account for each cMint it wants to hold, with the wallet address set as the cToken account owner. +* Each wallet can own multiple cToken accounts for the same cMint. +* A cToken account can only have one owner and hold units of one cMint. + + + + + Diagram showing cToken Solana account structure with three components: Address (identifier for cToken account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Owner set to Compressed Token Program), and AccountData (containing Mint, Owner, Amount, and Extensions fields) + + + + ```rust + /// Ctoken account structure (with extensions). + pub struct CToken { + pub mint: Pubkey, + pub owner: Pubkey, + pub amount: u64, + pub delegate: Option, // instruction not implemented yet + pub state: u8, + pub is_native: Option, + pub delegated_amount: u64, // instruction not implemented yet + pub close_authority: Option, + pub extensions: Option>, // Optional extensions e.g. compressible + } + ``` + + + + +Find the [source code of cToken here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken_struct.rs). + + +cToken accounts replicate the field layout and serialization format of [SPL Token accounts](https://solana.com/docs/tokens#token-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens. + +Here is how cTokens and SPL tokens compare: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldcTokenSPL Token Account
mint
owner
amount
delegateunimplemented
state
is_native
delegated_amountunimplemented
close_authority
extensionsvia Token-2022
+ +### Compressible Extension + +Compressible accounts maintain the **functionality of Solana accounts** but with **rent-exemption sponsorship**: + +1. The protocol funds the account's rent exemption at creation. +2. The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min) +3. The transaction payer top-ups the lamports balance when the account's lamports balance is below 2 epochs. +4. The account will be compressed when inactive for a period of 12,600 slots (84 min). +5. When the account is written to, it's automatically decompressed. + +We recommend to create cToken accounts always with the [compressible extension](/compressible/compressible) for sponsored rent exemption. + +This extension makes cToken accounts 200x cheaper than SPL token accounts + + | | cToken | SPL | + |-----------|--------|-----| + | allocate | 0.000011 | 0.002 | + | rent for 24h | 0.000005 | - | + + + +# Associated cToken Account + +**Associated cToken** accounts follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): +* Each wallet needs its own cToken account to hold tokens from the same cMint. +* It's derived with the owner's address, program ID, and mint address. +* The only **difference** in ATA derivation is the **program ID parameter** used in the seeds. + + +```rust +let seeds = [ + owner.as_ref(), // Wallet address (32 bytes) + program_id.as_ref(), // Compressed Token Program ID (32 bytes) + mint.as_ref(), // cMint address (32 bytes) + bump.as_ref(), // Bump seed (1 byte) +]; +``` + + +Find the [source code to associated cToken accounts here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/create_associated_token_account.rs). + + + +cToken ATAs can implement the **[compressible extension](/compressible/compressible) for sponsored rent exemption**. + + +# Compressed Token Account + +Compressed token accounts store token balance, owner, and other information like SPL and cTokens. Any cToken or SPL token can be compressed/decompressed at will. + +We recommend to use compressed tokens for **token distribution** or **storage of inactive tokens**. + + +**cToken accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 12,600 slots (84 min) and decompressed with new writes. + + + + + + Diagram showing compressed token account structure with three components: Hash (identifier for compressed token account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Address set to None), and AccountData (containing Mint, Owner, Amount, and Extensions fields marked as unimplemented) + + + + ```rust + pub struct TokenData { + pub mint: Pubkey, + pub owner: Pubkey, + pub amount: u64, + pub delegate: Option, + pub state: u8, + /// Placeholder for TokenExtension tlv data (unimplemented) + pub tlv: Option>, + } + ``` + + + +# Next Steps + + + \ No newline at end of file diff --git a/compressed-token-program/cmint/cmint.mdx b/compressed-token-program/cmint/cmint.mdx new file mode 100644 index 00000000..2e989c4e --- /dev/null +++ b/compressed-token-program/cmint/cmint.mdx @@ -0,0 +1,131 @@ +--- +title: Create cMint with Token Metadata +description: Guide to create a cMint, a rent-free mint account to store global metadata of tokens. +--- + + + + + + + + + + + + + + + + + + + + + + +
TypeKey Features
**[cMint](#cmint-accounts)**Compressed account +
    +
  • **Rent-free mint accounts** (similar to SPL mints)
  • +
+
**Token Metadata**Extension + * Stores information like name, symbol, URI, and custom fields. + * Must be added at creation of cMint +
+ + +Learn the core concepts to cMints [here](/compressed-token-program/c-token-program#cmint-accounts). + +## Get Started + + +These are the steps to create a cMint with metadata: + +1. Generate a mint seed keypair +2. Prepare token metadata +3. Call the `create_mint` action +4. Sign and send the transaction + + + + + + + +```typescript +const { transactionSignature, mint } = await createMint( + rpc, + payer, + mintAuthority, + null, // freezeAuthority + decimals, + Keypair.generate() +); +``` + +```typescript +const { transactionSignature, mint } = await createMint( + rpc, + payer, + mintAuthority, + null, // freezeAuthority + decimals, + Keypair.generate(), + createTokenMetadata( + 'name', + 'symbol', + 'ipfs://QmYwAPJzv5CZsnAzt8auVTL7oU4vNnB8L2xZ5C4X5PqW3R/metadata.json', + ), +); +``` + + + ```rust + use light_sdk::compressed_account::CompressedAccountWithMerkleContext; + use light_compressed_token::mint_sdk::create_mint; + + // 1. Generate mint seed + let mint_seed = Keypair::new(); + + // 2. Prepare metadata + let metadata = Some(TokenMetadataInstructionData { + name: b"My Compressed Token".to_vec(), + symbol: b"MCT".to_vec(), + uri: b"https://example.com/token-metadata.json".to_vec(), + update_authority: mint_authority.pubkey(), + additional_metadata: vec![ + AdditionalMetadata { + key: b"description".to_vec(), + value: b"A sample compressed token".to_vec(), + } + ], + }); + + // 3. Create the cMint with metadata + create_mint( + &rpc, + &mint_seed, + 9, // decimals + &mint_authority, + None, // freeze_authority + metadata, + &payer, + ).await?; + ``` + + + +**Metadata fields must be set at cMint creation.** +* Standard fields can be updated freely +* For `additional_metadata`, only existing keys can be modified or removed. + +## Next Steps + + + \ No newline at end of file diff --git a/compressed-token-program/cmint/update-metadata.mdx b/compressed-token-program/cmint/update-metadata.mdx new file mode 100644 index 00000000..c048e1c1 --- /dev/null +++ b/compressed-token-program/cmint/update-metadata.mdx @@ -0,0 +1,89 @@ +--- +title: Update Token Metadata of cMints +sidebarTitle: Update Token Metadata +description: Overview how to update token metadata of cMints. +--- + +Once set, metadata can be updated with `MintAction`: + +1. [`UpdateMetadataField`](#update-metadata-fields) updates specific fields. +2. [`UpdateMetadataAuthority`](#update-metadata-authority) updates the authority that can modify the extension. +3. [`RemoveMetadataKey`](#remove-metadata-keys) removes a key-value pair from `additional_metadata`. + + +Find the [source code for these actions here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/mint_action/actions/update_metadata.rs). + + + + + +### Update Metadata Fields + +Update standard fields (name, symbol, URI) or existing keys in `additional_metadata`: + +```rust +use light_ctoken_types::instructions::mint_action::UpdateMetadataFieldAction; + +// Update token name +let action = UpdateMetadataFieldAction { + extension_index: 0, // Index of TokenMetadata extension + field_type: 0, // 0 = Name + key: vec![], // Empty for standard fields + value: b"New Token Name".to_vec(), +}; +``` + + +* For `additional_metadata`, only existing keys can be modified or removed. +* New keys cannot be added after cMint creation. Only keys set during creation can be updated. + + + + + +### Update Metadata Authority + +```rust +use light_ctoken_types::instructions::mint_action::UpdateMetadataAuthorityAction; + +let action = UpdateMetadataAuthorityAction { + extension_index: 0, + new_authority: new_authority_pubkey, +}; +``` + + +To revoke update authority, set `new_authority` to a zero-byte pubkey (`Pubkey::default()`). + + + + + +### Remove Metadata Keys + +Removes a key from `additional_metadata`: + +```rust +use light_ctoken_types::instructions::mint_action::RemoveMetadataKeyAction; + +let action = RemoveMetadataKeyAction { + extension_index: 0, + key: b"category".to_vec(), + idempotent: 0, // 0 = error if key doesn't exist +}; +``` + + + + + +## Next Steps + + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/SPL-to-ctoken-transfer.mdx b/compressed-token-program/ctoken/SPL-to-ctoken-transfer.mdx new file mode 100644 index 00000000..30dffbb9 --- /dev/null +++ b/compressed-token-program/ctoken/SPL-to-ctoken-transfer.mdx @@ -0,0 +1,36 @@ +--- +title: Migrate SPL to cToken +description: Guide to migrate SPL tokens into compressed tokens and transfer them to a CToken account. +--- + +Converts SPL tokens into compressed tokens and transfers them to a CToken account. Interacts with a token pool PDA to synchronize supply between SPL and compressed tokens. + +**Use when:** Bridging tokens from the SPL ecosystem to compressed tokens, allowing users to deposit SPL tokens into their CToken accounts. + +**Key parameters:** + +- `source_spl_token_account`: Public key of the source SPL token account +- `to`: Public key of the destination CToken account +- `amount`: Number of tokens to transfer +- `authority`: Authority of the source SPL token account (signer) +- `mint`: Public key of the mint +- `payer`: Transaction fee payer +- `token_pool_pda`: Public key of the token pool PDA +- `token_pool_pda_bump`: Bump seed for the token pool PDA +- `spl_token_program`: Public key of the SPL Token program + +**Example:** + +```rust +use light_token_client::actions::transfer2::spl_to_ctoken_transfer; + +spl_to_ctoken_transfer( + &mut rpc, + spl_token_account_keypair.pubkey(), + associated_token_account, + transfer_amount, + &sender, + &payer, +) +.await?; +``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/close-ctoken.mdx b/compressed-token-program/ctoken/close-ctoken.mdx new file mode 100644 index 00000000..9d8f18dc --- /dev/null +++ b/compressed-token-program/ctoken/close-ctoken.mdx @@ -0,0 +1,38 @@ +--- +title: Close cToken +description: Guide to close a cToken, a rent-free mint account to store global metadata of tokens. +--- +Permanently closes a decompressed CToken Solana account. Distributes remaining lamports to a destination account, then zeros out and resizes the account to 0 bytes to prevent revival attacks. Works with both regular and compressible token accounts. + +**Use when:** You want to permanently close a CToken account with a zero token balance and reclaim its rent exemption. + +**Key parameters:** + +- `token_account`: The CToken account to close (must be initialized with zero balance) +- `destination`: Account receiving remaining user funds (non-rent lamports) +- `authority`: Signer (account owner or rent authority for compressible accounts) +- `rent_sponsor`: (Required for compressible accounts) Account receiving rent exemption + +**Example:** + +```rust +use light_compressed_token_sdk::ctoken::close_account::{ + close_account, + close_compressible_account, +}; + +// Close non-compressible account +let close_ix = close_account( + token_account_pubkey, + destination_pubkey, + authority.pubkey(), +)?; + +// Close compressible account +let close_compressible_ix = close_compressible_account( + token_account_pubkey, + destination_pubkey, + authority.pubkey(), + rent_sponsor_pubkey, +)?; +``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/compress-and-close.mdx b/compressed-token-program/ctoken/compress-and-close.mdx new file mode 100644 index 00000000..3c8e05be --- /dev/null +++ b/compressed-token-program/ctoken/compress-and-close.mdx @@ -0,0 +1,33 @@ +### CompressAndClose mode + + +The `Transfer2` instruction provides a `CompressAndClose` mode that compresses the full balance of a CToken account and closes it in a single transaction. + +**Use when:** Optimizing storage and costs by moving tokens off-chain into compressed format while cleaning up the on-chain account. + +**Key parameters:** + +- `amount`: Full balance of the CToken account to compress +- `mode`: Set to `CompressionMode::CompressAndClose` +- `mint`: Index of the mint account +- `source_or_recipient`: Index of the source CToken account +- `authority`: Index of the owner/delegate account (must be signer) +- `pool_account_index`: Used as `rent_sponsor_index` for CompressAndClose +- `pool_index`: Used as `compressed_account_index` for CompressAndClose +- `bump`: Used as `destination_index` for CompressAndClose + +**Example:** + +```rust +use light_token_client::actions::compress_and_close::compress_and_close; + +// Compress full balance and close account +compress_and_close( + &mut rpc, + ctoken_account_pubkey, + compressed_account_index, + &authority, + &payer, +) +.await?; +``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/create-ata.mdx b/compressed-token-program/ctoken/create-ata.mdx new file mode 100644 index 00000000..33ea1464 --- /dev/null +++ b/compressed-token-program/ctoken/create-ata.mdx @@ -0,0 +1,103 @@ +--- +title: CreateAssociatedTokenAccount +description: Creates an associated token account (ATA) for compressed tokens +--- + + +1. Creates an associated token account (ATA) for compressed tokens, passing `owner` and `mint` public keys as instruction data. Supports both basic and compressible accounts. Includes idempotent versions that succeed even if the account already exists. + +**Use when:** Creating an ATA where owner and mint public keys are readily available and can be included directly in instruction data. + +2. Creates an associated token account with `owner` and `mint` passed as account infos rather than instruction data. Use when the program needs to perform checks or operations directly on the owner or mint accounts (e.g., signature verification, accessing mint data). + +**Use when:** The program needs to access owner or mint accounts as AccountInfo objects for verification or data access beyond just knowing their public keys. + +**Key parameters:** + +Same as CreateAssociatedTokenAccount, but `owner` and `mint` are passed as `AccountMeta` entries instead of instruction data. + + +### 1. +**Key parameters:** + +- `payer`: Public key of the account paying for transaction and account creation +- `owner`: Public key of the ATA owner +- `mint`: Public key of the mint +- `ata_pubkey`: Derived public key of the ATA (use `derive_ctoken_ata`) +- `bump`: PDA bump seed for ATA derivation + +**Compressible-specific parameters:** + +- `compressible_config`: Public key of the CompressibleConfig account +- `rent_sponsor`: Public key receiving lamports when account is closed by rent authority +- `pre_pay_num_epochs`: Number of epochs of rent to prepay +- `lamports_per_write`: (Optional) Initial lamports for rent top-ups +- `token_account_version`: TokenDataVersion specifying hashing scheme + +**Example:** + +```rust +use light_compressed_token_sdk::ctoken::create_associated_token_account::{ + create_associated_token_account, + create_compressible_associated_token_account, + CreateCompressibleAssociatedTokenAccountInputs, +}; +use light_ctoken_types::state::TokenDataVersion; + +// Basic ATA +let basic_ata_ix = create_associated_token_account( + payer_pubkey, + owner_pubkey, + mint_pubkey, +)?; + +// Compressible ATA +let compressible_ata_ix = create_compressible_associated_token_account( + CreateCompressibleAssociatedTokenAccountInputs { + payer: payer_pubkey, + owner: owner_pubkey, + mint: mint_pubkey, + compressible_config, + rent_sponsor, + pre_pay_num_epochs: 0, + lamports_per_write: Some(150), + token_account_version: TokenDataVersion::ShaFlat, + }, +)?; +``` + + +### 2. +**Key parameters:** + +Same as CreateAssociatedTokenAccount, but `owner` and `mint` are passed as `AccountMeta` entries instead of instruction data. + +**Example:** + +```rust +use light_compressed_token_sdk::ctoken::create_associated_token_account::{ + create_associated_token_account2, + create_compressible_associated_token_account2, +}; + +// Basic ATA (version 2) +let basic_ata2_ix = create_associated_token_account2( + payer_pubkey, + owner_pubkey, + mint_pubkey, +)?; + +// Compressible ATA (version 2) +let compressible_ata2_ix = create_compressible_associated_token_account2( + CreateCompressibleAssociatedTokenAccountInputs { + payer: payer_pubkey, + owner: owner_pubkey, + mint: mint_pubkey, + compressible_config, + rent_sponsor, + pre_pay_num_epochs: 0, + lamports_per_write: Some(150), + token_account_version: TokenDataVersion::ShaFlat, + }, +)?; +``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/create-ctoken.mdx b/compressed-token-program/ctoken/create-ctoken.mdx new file mode 100644 index 00000000..da7cf821 --- /dev/null +++ b/compressed-token-program/ctoken/create-ctoken.mdx @@ -0,0 +1,120 @@ +--- +title: Create a cToken Account with Compressible Extension +description: Guide to create a cToken account with compressible extension. +--- + +import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; + + + + + + + + + + + + + + + + + + + + + +
TypeKey Features
**[cMint](#cmint-accounts)**Solana account +
    +
  • **SPL-compatible**
  • +
  • Use for **active token accounts** with frequent writes (trading, etc.)
  • +
+
**[Compressible](#compressible)**Extension +
    +
  • **Sponsored rent-exemption** makes cTokens 200x cheaper than SPL tokens
  • +
  • Must be added at creation of cToken
  • +
+
+ + + +Learn the core concepts to cTokens and compressible extension [here](/compressed-token-program/c-token-program#ctoken-accounts). + + + + +## Get Started + + + + +### Initialize cToken Account + + + + + +### Configure Compressible Extension +1. **Set the initial lamports balance** to N epochs (must be at least 2 epochs) + * Customize N **based on expected account activity**. + * You will top-up the account with rent for N epochs and it stays decompressed. +``` rust +rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) + +// For 260-byte cToken account: +// 128 + (260 * 1) = 388 lamports per epoch +``` + +2. **Set the top-up amount** for writes when the account balance falls below the set lamports balance. + * Determines how much lamports are added when the account needs a top-up. + * Top-ups are made by the **transaction fee payer** when the account is written to and is below the set lamports balance. + + +* Accounts become compressible when they advance two epochs without new transactions that add lamports. +* A forester node compresses the account after these two epochs and the protocol can reclaim the rent. + + + + +### Full Code Example + +```rust +use light_token_client::actions::{ + create_compressible_token_account, + CreateCompressibleTokenAccountInputs, +}; +use light_ctoken_types::state::TokenDataVersion; + +let token_account_pubkey = create_compressible_token_account( + &mut rpc, + CreateCompressibleTokenAccountInputs { + owner: owner_pubkey, + mint: mint_pubkey, + payer: &payer_keypair, + num_prepaid_epochs: 10, // 0 or ≥2 + lamports_per_write: Some(5_000), // lamports added per top-up + token_account_version: TokenDataVersion::ShaFlat, + token_account_keypair: None, // SDK generates keypair + } +).await?; +``` + + +For associated cToken accounts derive the address with [owner, compressed_token_program_id, mint](https://solscan.io/account/cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m). + + + + + + +## Next Steps + + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/ctoken-to-spl-decompress.mdx b/compressed-token-program/ctoken/ctoken-to-spl-decompress.mdx new file mode 100644 index 00000000..98e27c7c --- /dev/null +++ b/compressed-token-program/ctoken/ctoken-to-spl-decompress.mdx @@ -0,0 +1,49 @@ +--- +title: Transfer cToken to SPL account +description: Transfer compressed tokens from a cToken account to an SPL token account. +--- + + +Converts compressed tokens back to SPL tokens and transfers them to an SPL token account. Compresses the CToken balance into the token pool, then decompresses to the SPL account. + +**Use when:** Users want to withdraw compressed tokens from a CToken account back into the standard SPL token ecosystem. + +**Key parameters:** + +- `source_ctoken_account`: Public key of the source CToken account +- `destination_spl_token_account`: Public key of the destination SPL token account +- `amount`: Number of tokens to transfer +- `authority`: Authority of the source CToken account (signer) +- `mint`: Public key of the mint +- `payer`: Transaction fee payer +- `token_pool_pda`: Public key of the token pool PDA +- `token_pool_pda_bump`: Bump seed for the token pool PDA +- `spl_token_program`: Public key of the SPL Token program + +**Example:** + +```rust +use light_compressed_token_sdk::ctoken::transfer_interface::TransferCtokenToSpl; +use light_compressed_token_sdk::token_pool::find_token_pool_pda_with_index; + +let (token_pool_pda, token_pool_pda_bump) = find_token_pool_pda_with_index(&mint, 0); + +let transfer_ix = TransferCtokenToSpl { + source_ctoken_account, + destination_spl_token_account, + amount, + authority: authority.pubkey(), + mint, + payer: payer.pubkey(), + token_pool_pda, + token_pool_pda_bump, + spl_token_program: Pubkey::new_from_array(SPL_TOKEN_PROGRAM_ID), +} +.instruction()?; + +rpc.create_and_send_transaction( + &[transfer_ix], + &payer.pubkey(), + &signers +).await?; +``` diff --git a/compressed-token-program/ctoken/ctoken-transfer.mdx b/compressed-token-program/ctoken/ctoken-transfer.mdx new file mode 100644 index 00000000..b0b88a91 --- /dev/null +++ b/compressed-token-program/ctoken/ctoken-transfer.mdx @@ -0,0 +1,53 @@ +--- +title: Transfer cToken to cToken account +description: Transfer tokens between two cToken accounts. +--- + + +Transfers tokens between two cToken accounts. Follows SPL Token semantics and includes automatic rent top-ups for compressible accounts. + +**Use when:** Performing standard token transfers between users who both hold decompressed CToken accounts. + +**Key parameters:** + +- `source`: Public key of the source CToken account +- `destination`: Public key of the destination CToken account +- `amount`: Number of tokens to transfer (u64) +- `authority`: Public key of the authority (owner or delegate) that can spend from source +- `payer`: (Optional) Public key paying for transaction fees and rent top-ups + +**Example:** + +```rust +use light_client::rpc::Rpc; +use light_token_client::actions::ctoken_transfer::create_transfer_ctoken_instruction; + +async fn example_ctoken_transfer( + rpc: &mut R, + source_pubkey: Pubkey, + destination_pubkey: Pubkey, + amount: u64, + authority_keypair: &Keypair, + payer_keypair: &Keypair, +) -> Result<(), RpcError> { + let transfer_instruction = create_transfer_ctoken_instruction( + source_pubkey, + destination_pubkey, + amount, + authority_keypair.pubkey(), + )?; + + let mut signers = vec![payer_keypair]; + if authority_keypair.pubkey() != payer_keypair.pubkey() { + signers.push(authority_keypair); + } + + rpc.create_and_send_transaction( + &[transfer_instruction], + &payer_keypair.pubkey(), + &signers + ).await?; + + Ok(()) +} +``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/mint-actions.mdx b/compressed-token-program/ctoken/mint-actions.mdx new file mode 100644 index 00000000..3bc6980a --- /dev/null +++ b/compressed-token-program/ctoken/mint-actions.mdx @@ -0,0 +1,43 @@ +--- +title: Mint actions overview +description: Builder instructions for creating and managing compressed token accounts and transfers. +--- + +The CToken SDK provides builder instructions for managing compressed token accounts. These instructions follow a consistent builder pattern and handle account creation, minting, transfers, and conversions between compressed and SPL token formats. + +## Account creation + +### CreateCTokenAccount + + + +### CreateAssociatedTokenAccount + +### CreateAssociatedTokenAccount2 + + +## Mint operations + +### CreateCMint + + +### MintToCTokenTransfer + +## Transfer operations + +### CtokenTransfer + + +### SplToCtokenTransfer + + +### CtokenToSpl + +### CtokenToSplTransferAndClose + +## Account management + +### Close Token Account + +### CompressAndClose mode + diff --git a/compressed-token-program/ctoken/mint-ctokens.mdx b/compressed-token-program/ctoken/mint-ctokens.mdx new file mode 100644 index 00000000..8c17201e --- /dev/null +++ b/compressed-token-program/ctoken/mint-ctokens.mdx @@ -0,0 +1,33 @@ +--- +title: Mint cTokens +description: Mint new tokens directly to decompressed CToken accounts. +--- + +Part of the MintAction batch instruction. + +**Use when:** Issuing new tokens of a specific compressed mint to users holding CToken accounts. + +**Key parameters:** + +- `account_index`: Index into remaining accounts for the recipient token account (u8) +- `amount`: Number of tokens to mint (u64) + +**Example:** + +```rust +use light_compressed_token_sdk::compressed_token::mint_action::{ + MintActionCompressedInstructionData, + MintToCTokenAction, +}; + +let instruction_data = MintActionCompressedInstructionData::new_mint( + input.compressed_mint_with_context.address, + input.compressed_mint_with_context.root_index, + compressed_proof, + input.compressed_mint_with_context.mint.clone(), +) +.with_mint_to_ctoken(MintToCTokenAction { + account_index: 0, // First account in remaining accounts + amount: 1_000_000, +}); +``` \ No newline at end of file diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx new file mode 100644 index 00000000..781a74b6 --- /dev/null +++ b/compressed-token-program/overview.mdx @@ -0,0 +1,57 @@ +--- +title: Overview +description: Compressed tokens provide full SPL token functionality without per-account rent cost. +--- + +import GuidesTable from '/snippets/compressed-tokens-guides-table.mdx'; +import AdvancedGuidesTable from '/snippets/compressed-tokens-advanced-guides-table.mdx'; +import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; + +| Creation | SPL Token | Compressed Token | Cost Reduction | +|:---------------------|:------------------|:----------------------|:---------------| +| 100 Token Accounts | ~ 0.2 SOL | **~ 0.00004 SOL** | **5000x** | + +# Start building + + + + +### Install dependencies + + + + + + +### Set up developer environment + + + + + + + +### Get started + + + +## Guides + + + + +## Advanced Guides + + + + +## Next Steps + + \ No newline at end of file diff --git a/docs.json b/docs.json index cbe8ded0..d76ec0cd 100644 --- a/docs.json +++ b/docs.json @@ -11,16 +11,27 @@ "appearance": { "default": "light" }, + "customCSS": "/custom.css", "navigation": { "tabs": [ { "tab": "Docs", "pages": [ { - "group": "Get Started", + "group": "Introduction", + "expanded": true, "pages": [ - "/landing", - "/quickstart" + "intro-pages/landing", + { + "group": "Quickstart", + "icon": "bolt", + "expanded": true, + "pages": [ + "intro-pages/quickstart", + "intro-pages/quickstart-2" + ] + }, + "intro-pages/support" ] }, { @@ -39,35 +50,79 @@ ] }, { - "group": "Compressed Tokens", + "group": "Compressed Token Program", "pages": [ - "compressed-tokens/overview", + "compressed-token-program/overview", + "compressed-token-program/c-token-program", { - "group": "Guides", + "group": "cMint", "pages": [ - "compressed-tokens/guides", - "compressed-tokens/guides/how-to-create-compressed-token-accounts", - "compressed-tokens/guides/how-to-mint-compressed-tokens", - "compressed-tokens/guides/how-to-transfer-compressed-token", - "compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens", - "compressed-tokens/guides/how-to-compress-complete-spl-token-accounts", - "compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression", - "compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts", - "compressed-tokens/guides/how-to-merge-compressed-token-accounts", - "compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority" + "compressed-token-program/cmint/cmint", + "compressed-token-program/cmint/update-metadata" ] }, { - "group": "Advanced Guides", + "group": "cToken", "pages": [ - "compressed-tokens/advanced-guides", - "compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", - "compressed-tokens/advanced-guides/create-an-airdrop", - "compressed-tokens/advanced-guides/create-an-airdrop-with-claim", - "compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", - "compressed-tokens/advanced-guides/use-token-2022-with-compression", - "compressed-tokens/advanced-guides/example-web-client", - "compressed-tokens/advanced-guides/example-node-js" + "compressed-token-program/ctoken/mint-actions", + { + "group": "Guides", + "expanded": true, + "pages": [ + "compressed-token-program/ctoken/create-ctoken", + "compressed-token-program/ctoken/mint-ctokens", + "compressed-token-program/ctoken/create-ata", + "compressed-token-program/ctoken/ctoken-transfer", + "compressed-token-program/ctoken/SPL-to-ctoken-transfer", + "compressed-token-program/ctoken/ctoken-to-spl-decompress", + "compressed-token-program/ctoken/compress-and-close", + "compressed-token-program/ctoken/close-ctoken" + ] + } + ] + }, + { + "group": "Compressed Token", + "pages": [ + "compressed-token-program/compressed-tokens/compressed-token-overview", + { + "group": "Basic Guides", + "pages": [ + "compressed-token-program/compressed-tokens/basic-guides/how-to-create-compressed-token-accounts", + "compressed-token-program/compressed-tokens/basic-guides/how-to-mint-compressed-tokens", + "compressed-token-program/compressed-tokens/basic-guides/how-to-transfer-compressed-token", + "compressed-token-program/compressed-tokens/basic-guides/how-to-compress-and-decompress-spl-tokens", + "compressed-token-program/compressed-tokens/basic-guides/how-to-compress-complete-spl-token-accounts", + "compressed-token-program/compressed-tokens/basic-guides/how-to-create-and-register-a-mint-account-for-compression", + "compressed-token-program/compressed-tokens/basic-guides/how-to-create-compressed-token-pools-for-mint-accounts", + "compressed-token-program/compressed-tokens/basic-guides/how-to-merge-compressed-token-accounts", + "compressed-token-program/compressed-tokens/basic-guides/how-to-approve-and-revoke-delegate-authority" + ] + }, + { + "group": "Advanced Guides", + "pages": [ + { + "group": "Integration", + "expanded": true, + "pages": [ + "compressed-token-program/compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", + "compressed-token-program/compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", + "compressed-token-program/compressed-tokens/advanced-guides/use-token-2022-with-compression" + ] + }, + { + "group": "Examples", + "expanded": true, + "pages": [ + "compressed-token-program/compressed-tokens/advanced-guides/create-an-airdrop", + "compressed-token-program/compressed-tokens/advanced-guides/create-an-airdrop-with-claim", + "compressed-token-program/compressed-tokens/advanced-guides/example-web-client", + "compressed-token-program/compressed-tokens/advanced-guides/example-node-js" + ] + } + ] + } ] } ] @@ -395,7 +450,6 @@ { "group": "SDK Reference", "pages": [ - "changelog", "api-reference/libraries/stateless-js", "api-reference/libraries/compressed-token", "api-reference/libraries/light-client", @@ -406,33 +460,78 @@ ] }, { - "tab": "Compressed Tokens", + "tab": "cToken Program", "pages": [ - "compressed-tokens/overview", + "compressed-token-program/overview", + "compressed-token-program/c-token-program", { - "group": "Guides", + "group": "cMint", + "pages": [ + "compressed-token-program/cmint/cmint", + "compressed-token-program/cmint/update-metadata" + ] + }, + { + "group": "cToken", "pages": [ - "compressed-tokens/guides/how-to-create-compressed-token-accounts", - "compressed-tokens/guides/how-to-mint-compressed-tokens", - "compressed-tokens/guides/how-to-transfer-compressed-token", - "compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens", - "compressed-tokens/guides/how-to-compress-complete-spl-token-accounts", - "compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression", - "compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts", - "compressed-tokens/guides/how-to-merge-compressed-token-accounts", - "compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority" + "compressed-token-program/ctoken/mint-actions", + { + "group": "Guides", + "pages": [ + "compressed-token-program/ctoken/ctoken", + "compressed-token-program/ctoken/create-ctoken", + "compressed-token-program/ctoken/create-ata", + "compressed-token-program/ctoken/ctoken-transfer", + "compressed-token-program/ctoken/SPL-to-ctoken-transfer", + "compressed-token-program/ctoken/ctoken-to-spl-decompress", + "compressed-token-program/ctoken/compress-and-close", + "compressed-token-program/ctoken/close-ctoken" + ] + } ] }, { - "group": "Advanced Guides", + "group": "Compressed Token", "pages": [ - "compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", - "compressed-tokens/advanced-guides/create-an-airdrop", - "compressed-tokens/advanced-guides/create-an-airdrop-with-claim", - "compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", - "compressed-tokens/advanced-guides/use-token-2022-with-compression", - "compressed-tokens/advanced-guides/example-web-client", - "compressed-tokens/advanced-guides/example-node-js" + "compressed-token-program/compressed-tokens/compressed-token-overview", + { + "group": "Basic Guides", + "pages": [ + "compressed-token-program/compressed-tokens/basic-guides/how-to-create-compressed-token-accounts", + "compressed-token-program/compressed-tokens/basic-guides/how-to-mint-compressed-tokens", + "compressed-token-program/compressed-tokens/basic-guides/how-to-transfer-compressed-token", + "compressed-token-program/compressed-tokens/basic-guides/how-to-compress-and-decompress-spl-tokens", + "compressed-token-program/compressed-tokens/basic-guides/how-to-compress-complete-spl-token-accounts", + "compressed-token-program/compressed-tokens/basic-guides/how-to-create-and-register-a-mint-account-for-compression", + "compressed-token-program/compressed-tokens/basic-guides/how-to-create-compressed-token-pools-for-mint-accounts", + "compressed-token-program/compressed-tokens/basic-guides/how-to-merge-compressed-token-accounts", + "compressed-token-program/compressed-tokens/basic-guides/how-to-approve-and-revoke-delegate-authority" + ] + }, + { + "group": "Advanced Guides", + "pages": [ + { + "group": "Integration", + "expanded": true, + "pages": [ + "compressed-token-program/compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", + "compressed-token-program/compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", + "compressed-token-program/compressed-tokens/advanced-guides/use-token-2022-with-compression" + ] + }, + { + "group": "Examples", + "expanded": true, + "pages": [ + "compressed-token-program/compressed-tokens/advanced-guides/create-an-airdrop", + "compressed-token-program/compressed-tokens/advanced-guides/create-an-airdrop-with-claim", + "compressed-token-program/compressed-tokens/advanced-guides/example-web-client", + "compressed-token-program/compressed-tokens/advanced-guides/example-node-js" + ] + } + ] + } ] } ] @@ -457,6 +556,12 @@ { "tab": "API Reference", "pages": [ + { + "group": "SDK Reference", + "pages": [ + "api-reference/index" + ] + }, { "group": "JSON RPC Methods", "pages": [ @@ -493,7 +598,7 @@ ] }, { - "tab": "Releases", + "tab": "Changelog", "pages": [ "changelog" ] @@ -504,12 +609,17 @@ { "anchor": "AI Tools", "href": "https://zkcompression.com/learn/ai-tools-guide", - "icon": "robot" + "icon": "bot" + }, + { + "anchor": "GitHub", + "href": "https://github.com/Lightprotocol/light-protocol", + "icon": "github" }, { - "anchor": "Support", - "href": "https://www.zkcompression.com/support", - "icon": "headset" + "anchor": "Discord", + "href": "https://discord.com/invite/CYvjBgzRFP", + "icon": "discord" } ] } @@ -570,6 +680,26 @@ "x": "https://x.com/lightprotocol", "github": "https://github.com/Lightprotocol/light-protocol", "discord": "https://discord.com/invite/CYvjBgzRFP" - } + }, + "links": [ + { + "header": "Compressed by Light Protocol", + "items": [ + { + "label": "lightprotocol.com", + "href": "https://lightprotocol.com" + } + ] + }, + { + "header": "Indexed by Helius", + "items": [ + { + "label": "helius.dev", + "href": "https://helius.dev" + } + ] + } + ] } } \ No newline at end of file From fe06a417675112eb123bbe8b72002dd7186e5d10 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 27 Nov 2025 01:22:38 +0000 Subject: [PATCH 002/143] Update docs.json navigation structure for compressed token program --- docs.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs.json b/docs.json index d76ec0cd..77b400f3 100644 --- a/docs.json +++ b/docs.json @@ -19,18 +19,9 @@ "pages": [ { "group": "Introduction", - "expanded": true, "pages": [ "intro-pages/landing", - { - "group": "Quickstart", - "icon": "bolt", - "expanded": true, - "pages": [ - "intro-pages/quickstart", - "intro-pages/quickstart-2" - ] - }, + "intro-pages/quickstart", "intro-pages/support" ] }, From aa9fb1847a51a9b1325cd3ec064d9c978cd9b7e3 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 27 Nov 2025 18:12:32 +0000 Subject: [PATCH 003/143] Add program-create-ctoken guide and refactor cMint documentation - Add compressed-token-program/ctoken/program-create-ctoken.mdx with program-side cToken creation guide - Refactor cmint.mdx: restructure with client/program tabs, add install dependencies accordion - Update c-token-program.mdx: simplify compressible extension description, remove redundant tip - Update docs.json: flatten quickstart structure, add program-create-ctoken to navigation - Add snippets: compressible-vs-solana-rent.mdx, ctoken-create-accounts-list.mdx --- compressed-token-program/c-token-program.mdx | 6 +- compressed-token-program/cmint/cmint.mdx | 163 +++++---- .../ctoken/program-create-ctoken.mdx | 316 ++++++++++++++++++ docs.json | 2 + snippets/compressible-vs-solana-rent.mdx | 145 ++++++++ snippets/ctoken-create-accounts-list.mdx | 67 ++++ 6 files changed, 627 insertions(+), 72 deletions(-) create mode 100644 compressed-token-program/ctoken/program-create-ctoken.mdx create mode 100644 snippets/compressible-vs-solana-rent.mdx create mode 100644 snippets/ctoken-create-accounts-list.mdx diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx index 142135b2..0b01a8b4 100644 --- a/compressed-token-program/c-token-program.mdx +++ b/compressed-token-program/c-token-program.mdx @@ -280,16 +280,14 @@ Here is how cTokens and SPL tokens compare: ### Compressible Extension -Compressible accounts maintain the **functionality of Solana accounts** but with **rent-exemption sponsorship**: +cToken accounts include the compressible extension with rent-exemption sponsorship: 1. The protocol funds the account's rent exemption at creation. 2. The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min) 3. The transaction payer top-ups the lamports balance when the account's lamports balance is below 2 epochs. 4. The account will be compressed when inactive for a period of 12,600 slots (84 min). 5. When the account is written to, it's automatically decompressed. - -We recommend to create cToken accounts always with the [compressible extension](/compressible/compressible) for sponsored rent exemption. - + This extension makes cToken accounts 200x cheaper than SPL token accounts | | cToken | SPL | diff --git a/compressed-token-program/cmint/cmint.mdx b/compressed-token-program/cmint/cmint.mdx index 2e989c4e..183fc02b 100644 --- a/compressed-token-program/cmint/cmint.mdx +++ b/compressed-token-program/cmint/cmint.mdx @@ -3,6 +3,7 @@ title: Create cMint with Token Metadata description: Guide to create a cMint, a rent-free mint account to store global metadata of tokens. --- +import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; @@ -36,83 +37,109 @@ description: Guide to create a cMint, a rent-free mint account to store global m Learn the core concepts to cMints [here](/compressed-token-program/c-token-program#cmint-accounts). -## Get Started +## Get Started + + + -These are the steps to create a cMint with metadata: + +### Setup -1. Generate a mint seed keypair -2. Prepare token metadata -3. Call the `create_mint` action -4. Sign and send the transaction + + + - - - - -```typescript -const { transactionSignature, mint } = await createMint( - rpc, - payer, - mintAuthority, - null, // freezeAuthority - decimals, - Keypair.generate() -); + + + +### Create cMint with Token Metadata + +*test*! + + +```typescript Typescript +import { createRpc, confirmTx } from '@lightprotocol/stateless.js'; +import { createMint, createTokenMetadata } from '@lightprotocol/compressed-token'; +import { Keypair } from '@solana/web3.js'; + +const RPC_ENDPOINT = 'https://devnet.helius-rpc.com/?api-key='; +const COMPRESSION_ENDPOINT = RPC_ENDPOINT; + +async function main() { + const rpc = createRpc(RPC_ENDPOINT, COMPRESSION_ENDPOINT); + const payer = Keypair.generate(); + const mintAuthority = payer; + const decimals = 9; + + // Fund payer (devnet only) + const airdropSig = await rpc.requestAirdrop(payer.publicKey, 1e9); + await confirmTx(rpc, airdropSig); + + // Create cMint with token metadata + const { transactionSignature, mint } = await createMint( + rpc, + payer, + mintAuthority, + null, // freezeAuthority + decimals, + Keypair.generate(), + createTokenMetadata( + 'My Token', + 'MTK', + 'ipfs://QmYwAPJzv5CZsnAzt8auVTL7oU4vNnB8L2xZ5C4X5PqW3R/metadata.json', + ), + ); + + console.log('cMint created:', mint.toBase58()); +} + +main().catch(console.error); ``` -```typescript -const { transactionSignature, mint } = await createMint( - rpc, - payer, - mintAuthority, - null, // freezeAuthority - decimals, - Keypair.generate(), - createTokenMetadata( - 'name', - 'symbol', - 'ipfs://QmYwAPJzv5CZsnAzt8auVTL7oU4vNnB8L2xZ5C4X5PqW3R/metadata.json', - ), -); +```rust Rust +use light_compressed_token::mint_sdk::create_mint; + +// Generate mint seed +let mint_seed = Keypair::new(); + +// Prepare metadata +let metadata = Some(TokenMetadataInstructionData { + name: b"My Compressed Token".to_vec(), + symbol: b"MCT".to_vec(), + uri: b"https://example.com/token-metadata.json".to_vec(), + update_authority: mint_authority.pubkey(), + additional_metadata: vec![ + AdditionalMetadata { + key: b"description".to_vec(), + value: b"A sample compressed token".to_vec(), + } + ], +}); + +// Create the cMint with metadata +create_mint( + &rpc, + &mint_seed, + 9, // decimals + &mint_authority, + None, // freeze_authority + metadata, + &payer, +).await?; ``` - - - ```rust - use light_sdk::compressed_account::CompressedAccountWithMerkleContext; - use light_compressed_token::mint_sdk::create_mint; - - // 1. Generate mint seed - let mint_seed = Keypair::new(); - - // 2. Prepare metadata - let metadata = Some(TokenMetadataInstructionData { - name: b"My Compressed Token".to_vec(), - symbol: b"MCT".to_vec(), - uri: b"https://example.com/token-metadata.json".to_vec(), - update_authority: mint_authority.pubkey(), - additional_metadata: vec![ - AdditionalMetadata { - key: b"description".to_vec(), - value: b"A sample compressed token".to_vec(), - } - ], - }); - - // 3. Create the cMint with metadata - create_mint( - &rpc, - &mint_seed, - 9, // decimals - &mint_authority, - None, // freeze_authority - metadata, - &payer, - ).await?; - ``` - + + + + + + + + +Coming soon. + **Metadata fields must be set at cMint creation.** diff --git a/compressed-token-program/ctoken/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-create-ctoken.mdx new file mode 100644 index 00000000..be1bd4a5 --- /dev/null +++ b/compressed-token-program/ctoken/program-create-ctoken.mdx @@ -0,0 +1,316 @@ +--- +title: Create cToken Accounts from Programs +description: Guide to create compressible cToken accounts via CPI from Solana programs. +--- + +import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; +import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; + + +Compressible cToken accounts are created via CPI to the cToken Program. + +* For **user-owned accounts** use `invoke()` - the user's wallet signs the transaction +* For **PDA-owned accounts** use `invoke_signed()` - the owner program provides the seeds to sign the transaction + +Find [full code examples at the end](#full-code-example). + +### Key Points + +cToken accounts include the compressible extension with rent-exemption sponsorship: + +1. The protocol funds the account's rent exemption at creation. +2. The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min) +3. The transaction payer top-ups the lamports balance when the account's lamports balance is below 2 epochs. +4. The account will be compressed when inactive for a period of 12,600 slots (84 min). +5. When the account is written to, it's automatically decompressed. + + + +# Implementation Guide + + + + +### Dependencies + +```toml +[dependencies] +light-compressed-token-sdk = "0.1.0" +solana-program = "2.2" +borsh = "0.10.0" +``` + + + + + +### Configure Rent & Instruction Data + +```rust +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::pubkey::Pubkey; + +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateTokenAccountData { + pub owner: Pubkey, + pub pre_pay_num_epochs: u8, + pub lamports_per_write: u32, +} +``` +1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** + * Customize N **based on expected account activity**. + * The account stays decompressed while it has rent for N epochs. +``` rust +rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) + +// For 260-byte cToken account: +// 128 + (260 * 1) = 388 lamports per epoch +``` + +2. **Set** in `lamports_per_write` the **top-up amount**. + * Top-ups are **made by** the **transaction fee payer** when the **lamports balance** falls **below two epochs of rent**. + + +* Accounts become compressible when they advance two epochs without new transactions that add lamports. +* A forester node compresses the account after these two epochs and the protocol can reclaim the rent. + + + + + + +### Build Account Infos + + +Build `CompressibleParamsInfos` with the rent configuration: + +```rust +use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; + +let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), // compressible_config + accounts[5].clone(), // rent_sponsor + accounts[4].clone(), // system_program +); +``` + +Build `CreateCTokenAccountInfos` with account references: + +```rust +let account_infos = CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), +}; +``` + + + + + + + + + + + +### CPI + +Call `invoke()` for user-owned accounts or `invoke_signed()` for PDA-owned accounts. + + + + +```rust +use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError}; + +pub fn process_create_token_account_invoke( + accounts: &[AccountInfo], + data: CreateTokenAccountData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), // compressible_config + accounts[5].clone(), // rent_sponsor + accounts[4].clone(), // system_program + ); + + CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), + } + .invoke()?; + + Ok(()) +} +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +pub const TOKEN_ACCOUNT_SEED: &[u8] = b"token_account"; + +pub fn process_create_token_account_invoke_signed( + accounts: &[AccountInfo], + data: CreateTokenAccountData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive and verify PDA + let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &crate::ID); + if &pda != accounts[1].key { + return Err(ProgramError::InvalidSeeds); + } + + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), // compressible_config + accounts[5].clone(), // rent_sponsor + accounts[4].clone(), // system_program + ); + + let account_infos = CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), + }; + + let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; + account_infos.invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::{ID, TOKEN_ACCOUNT_SEED}; + +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateTokenAccountData { + pub owner: Pubkey, + pub pre_pay_num_epochs: u8, + pub lamports_per_write: u32, +} + +/// Creates a user-owned compressible cToken account +pub fn process_create_token_account_invoke( + accounts: &[AccountInfo], + data: CreateTokenAccountData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), + ); + + CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), + } + .invoke()?; + + Ok(()) +} + +/// Creates a PDA-owned compressible cToken account +pub fn process_create_token_account_invoke_signed( + accounts: &[AccountInfo], + data: CreateTokenAccountData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); + if &pda != accounts[1].key { + return Err(ProgramError::InvalidSeeds); + } + + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), + ); + + let account_infos = CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), + }; + + let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; + account_infos.invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + +# Next Steps + + + + + diff --git a/docs.json b/docs.json index 77b400f3..63300b69 100644 --- a/docs.json +++ b/docs.json @@ -61,6 +61,7 @@ "expanded": true, "pages": [ "compressed-token-program/ctoken/create-ctoken", + "compressed-token-program/ctoken/program-create-ctoken", "compressed-token-program/ctoken/mint-ctokens", "compressed-token-program/ctoken/create-ata", "compressed-token-program/ctoken/ctoken-transfer", @@ -471,6 +472,7 @@ "pages": [ "compressed-token-program/ctoken/ctoken", "compressed-token-program/ctoken/create-ctoken", + "compressed-token-program/ctoken/program-create-ctoken", "compressed-token-program/ctoken/create-ata", "compressed-token-program/ctoken/ctoken-transfer", "compressed-token-program/ctoken/SPL-to-ctoken-transfer", diff --git a/snippets/compressible-vs-solana-rent.mdx b/snippets/compressible-vs-solana-rent.mdx new file mode 100644 index 00000000..c66e7704 --- /dev/null +++ b/snippets/compressible-vs-solana-rent.mdx @@ -0,0 +1,145 @@ + + +### Initial Rent Top-Up +The **creator of compressible accounts** tops-up the account with **rent for at least two epochs**. +Two epochs for compressible accounts have a length of **12,600 slots**. + +``` rust +rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) + +// For 260-byte cToken account: +// 128 + (260 * 1) = 388 lamports per epoch +``` + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentAmountDescription
Prepaid rent388 × N epochs*Rent for N epochs
Compression cost & incentive10,000 lamports
+ 1,000 lamports
Transaction cost for compression
+ protocol incentive
Transaction fee5,000-10,000 lamportsStandard Solana tx fees
+ +\* _`N` = `num_prepaid_epochs` parameter (0 or ≥ 2, set at creation)._ + +Normally, the **creator of Solana accounts** pays the **rent exemption balance equal to 2 years of rent** proportional to account size: + +``` rust +minimum_balance = (ACCOUNT_STORAGE_OVERHEAD + data_len) * lamports_per_byte * exemption_threshold + +/// Example 165 byte SPL token account: +/// minimum_balance = (128 + 165) * 6960 = 2,039,280 lamports +``` + +* Most Solana accounts are rarely accessed after creation, but continue to lock up SOL for the account's lifetime. +* The creator of Solana accounts still must pay the rent-exemption balance. +* The creator of compressible accounts only needs to pay the rent for the first two epochs. + + + +### Top-ups per Transaction + +The **transaction payer tops-up** the account with rent **when the account's lamports balance is below 2 epochs**. This keeps accounts decompressed while active. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Account StatePayer CostExample
Funded for 2+ epochs0 lamportsNo top-up required
Funded for less than 2 epochs`lamports_per_write`Configured as 5,000 lamports
→ payer pays 5,000 lamports
Compressible
(ran out of rent)
`lamports_per_write + rent_deficit`Configured top-up: 5,000 lamports
Rent deficit: 3,000 lamports
Total payer cost: 8,000 lamports
+ + +Top-ups are additional to standard Solana transaction fees (typically <10,000 lamports). + + +### Closing compressible accounts + +Compressible accounts can be closed by two parties. How rent and lamports balance are distributed depends on which party closes the account. + +1. The **Account Owner** can close the account at any time, regardless of rent status. The account is not compressed in this case. + + + + + + + + + + + + + + + + + + +
Component and AmountRecipient
Rent exemption + completed epoch rentRent sponsor
Remaining lamports from top-up (partial epoch)Account Owner
+ +2. The **Compression Authority** can compress and close the account when it becomes compressible + + + + + + + + + + + + + + + + + + +
Component and AmountRecipient
Rent exemption + remaining epoch rent
Rent sponsor
Compression incentive
(11,000 lamports)
Forester node
+ + \ No newline at end of file diff --git a/snippets/ctoken-create-accounts-list.mdx b/snippets/ctoken-create-accounts-list.mdx new file mode 100644 index 00000000..009dcd8f --- /dev/null +++ b/snippets/ctoken-create-accounts-list.mdx @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ConstraintsDescription
0Payersigner, mutable + - Pays transaction fee and compression incentive (prepaid epochs).
+ - Does NOT pay rent exemption (fronted by `rent_sponsor`). +
1Token Accountsigner*, mutable + - The cToken account being created.
+ - *Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds. +
2Mint-The SPL or cMint token mint. Defines which token this account holds.
3Compressible Config- + - Config account owned by LightRegistry.
+ - Stores rent parameters: `rent_sponsor`, `compression_delay`, `rent_config`.
+ - Defines `address_space` for allowed address trees. +
4System Program-Solana System Program. Required for CPI to create the on-chain account.
5Rent Sponsormutable + - CToken program PDA that fronts rent exemption (~0.002 SOL) at creation.
+ - Claims rent when account compresses.
+
From cb0763e153321d5e6d99b242010e12ae6a9ed82e Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 27 Nov 2025 18:41:03 +0000 Subject: [PATCH 004/143] Refactor program-create-ctoken guide structure and simplify descriptions - Restructure guide with tabs for program/client implementations - Move Key Points section before Implementation Guide - Reorder account info setup: rent params first, then token account config - Simplify compressible config and rent sponsor descriptions - Remove SOL amount from rent sponsor description - Add client tab placeholder --- .../ctoken/program-create-ctoken.mdx | 52 ++++++++++++------- snippets/ctoken-create-accounts-list.mdx | 8 ++- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/compressed-token-program/ctoken/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-create-ctoken.mdx index be1bd4a5..d9c2271e 100644 --- a/compressed-token-program/ctoken/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-create-ctoken.mdx @@ -1,22 +1,14 @@ --- title: Create cToken Accounts from Programs -description: Guide to create compressible cToken accounts via CPI from Solana programs. +description: Guide to create compressible cToken accounts from programs, and clients. --- import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; +## Key Points -Compressible cToken accounts are created via CPI to the cToken Program. - -* For **user-owned accounts** use `invoke()` - the user's wallet signs the transaction -* For **PDA-owned accounts** use `invoke_signed()` - the owner program provides the seeds to sign the transaction - -Find [full code examples at the end](#full-code-example). - -### Key Points - -cToken accounts include the compressible extension with rent-exemption sponsorship: +cToken accounts are compressible with rent-exemption sponsorship: 1. The protocol funds the account's rent exemption at creation. 2. The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min) @@ -28,6 +20,17 @@ cToken accounts include the compressible extension with rent-exemption sponsorsh # Implementation Guide + + + + +Compressible cToken accounts are created via CPI to the cToken Program. + +* For **user-owned accounts** use `invoke()` - the user's wallet signs the transaction +* For **PDA-owned accounts** use `invoke_signed()` - the owner program provides the seeds to sign the transaction + +Find [full code examples at the end](#full-code-example). + @@ -81,8 +84,15 @@ rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) ### Build Account Infos +Your program receives these accounts from the client.
Bundle them for the CPI: -Build `CompressibleParamsInfos` with the rent configuration: + + + + + + +#### 1. Configure Rent Parameters ```rust use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; @@ -96,24 +106,18 @@ let compressible_params = CompressibleParamsInfos::new( ); ``` -Build `CreateCTokenAccountInfos` with account references: +#### 2. Configure Token Account ```rust let account_infos = CreateCTokenAccountInfos { payer: accounts[0].clone(), account: accounts[1].clone(), - mint: accounts[2].clone(), + mint: accounts[2].clone(), // can be SPL or cMint owner: data.owner, compressible: Some(compressible_params), }; ``` - - - - - -
@@ -210,6 +214,14 @@ pub fn process_create_token_account_invoke_signed(
+
+ + +Coming soon. + + +
+ # Full Code Example diff --git a/snippets/ctoken-create-accounts-list.mdx b/snippets/ctoken-create-accounts-list.mdx index 009dcd8f..eae2b23a 100644 --- a/snippets/ctoken-create-accounts-list.mdx +++ b/snippets/ctoken-create-accounts-list.mdx @@ -43,9 +43,7 @@ Compressible Config - - - Config account owned by LightRegistry.
- - Stores rent parameters: `rent_sponsor`, `compression_delay`, `rent_config`.
- - Defines `address_space` for allowed address trees. + Protocol PDA that stores rent config and compression authorities. @@ -59,8 +57,8 @@ Rent Sponsor mutable - - CToken program PDA that fronts rent exemption (~0.002 SOL) at creation.
- - Claims rent when account compresses.
+ - CToken program PDA that fronts rent exemption at creation.
+ - Claims rent when account compresses. From 5f63a85bb9cb122f432bd670cf3d48f137ba5d52 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 27 Nov 2025 19:05:12 +0000 Subject: [PATCH 005/143] Simplify program-create-ctoken guide: consolidate steps and add comments - Merge build account infos and CPI sections into single step - Inline code snippets to show complete flow - Add detailed code comments to full example - Update title from "Create cToken Accounts from Programs" to "Create cToken Accounts" - Remove redundant imports from snippets - Reorder tabs: Program first, Client second - Add account order documentation in comments --- .../ctoken/program-create-ctoken.mdx | 201 +++++++----------- 1 file changed, 82 insertions(+), 119 deletions(-) diff --git a/compressed-token-program/ctoken/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-create-ctoken.mdx index d9c2271e..96de3a89 100644 --- a/compressed-token-program/ctoken/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-create-ctoken.mdx @@ -1,5 +1,5 @@ --- -title: Create cToken Accounts from Programs +title: Create cToken Accounts description: Guide to create compressible cToken accounts from programs, and clients. --- @@ -50,9 +50,6 @@ borsh = "0.10.0" ### Configure Rent & Instruction Data ```rust -use borsh::{BorshDeserialize, BorshSerialize}; -use solana_program::pubkey::Pubkey; - #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct CreateTokenAccountData { pub owner: Pubkey, @@ -82,130 +79,75 @@ rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) -### Build Account Infos +### Build Account Infos & CPI cToken Program -Your program receives these accounts from the client.
Bundle them for the CPI: +Your program receives these accounts from the client. +
Bundle them for the CPI: +1. Configure Rent Parameters -#### 1. Configure Rent Parameters +2. Configure Token Account -```rust -use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; +3. CPI to cToken Program +Call `invoke()` for user-owned accounts or `invoke_signed()` for PDA-owned accounts. + + + + +```rust +// Build the compressible params let compressible_params = CompressibleParamsInfos::new( data.pre_pay_num_epochs, data.lamports_per_write, - accounts[3].clone(), // compressible_config - accounts[5].clone(), // rent_sponsor - accounts[4].clone(), // system_program + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), ); -``` - -#### 2. Configure Token Account -```rust -let account_infos = CreateCTokenAccountInfos { +// Build the account infos struct +CreateCTokenAccountInfos { payer: accounts[0].clone(), account: accounts[1].clone(), - mint: accounts[2].clone(), // can be SPL or cMint + mint: accounts[2].clone(), owner: data.owner, compressible: Some(compressible_params), -}; -``` - -
- - - -### CPI - -Call `invoke()` for user-owned accounts or `invoke_signed()` for PDA-owned accounts. - - - - -```rust -use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; -use solana_program::{account_info::AccountInfo, program_error::ProgramError}; - -pub fn process_create_token_account_invoke( - accounts: &[AccountInfo], - data: CreateTokenAccountData, -) -> Result<(), ProgramError> { - if accounts.len() < 6 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), // compressible_config - accounts[5].clone(), // rent_sponsor - accounts[4].clone(), // system_program - ); - - CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), - owner: data.owner, - compressible: Some(compressible_params), - } - .invoke()?; - - Ok(()) } +.invoke()?; ``` ```rust -use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -pub const TOKEN_ACCOUNT_SEED: &[u8] = b"token_account"; - -pub fn process_create_token_account_invoke_signed( - accounts: &[AccountInfo], - data: CreateTokenAccountData, -) -> Result<(), ProgramError> { - if accounts.len() < 6 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive and verify PDA - let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &crate::ID); - if &pda != accounts[1].key { - return Err(ProgramError::InvalidSeeds); - } - - let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), // compressible_config - accounts[5].clone(), // rent_sponsor - accounts[4].clone(), // system_program - ); +// Build the compressible params +let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), +); - let account_infos = CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), - owner: data.owner, - compressible: Some(compressible_params), - }; +// Build the account infos struct +let account_infos = CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), +}; - let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; - account_infos.invoke_signed(&[signer_seeds])?; +// Invoke with PDA-signer +let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; +account_infos.invoke_signed(&[signer_seeds])?; - Ok(()) -} +Ok(()) ``` @@ -214,14 +156,6 @@ pub fn process_create_token_account_invoke_signed( - - - -Coming soon. - - - - # Full Code Example @@ -235,6 +169,7 @@ use solana_program::{account_info::AccountInfo, program_error::ProgramError, pub use crate::{ID, TOKEN_ACCOUNT_SEED}; +/// Instruction data for create token account #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct CreateTokenAccountData { pub owner: Pubkey, @@ -242,7 +177,19 @@ pub struct CreateTokenAccountData { pub lamports_per_write: u32, } -/// Creates a user-owned compressible cToken account +/// Handler for creating a compressible token account (invoke) +/// +/// Uses the builder pattern from the ctoken module. This demonstrates how to: +/// 1. Build the account infos struct with compressible params +/// 2. Call the invoke() method which handles instruction building and CPI +/// +/// Account order: +/// - accounts[0]: payer (signer) +/// - accounts[1]: account to create (signer) +/// - accounts[2]: mint +/// - accounts[3]: compressible_config +/// - accounts[4]: system_program +/// - accounts[5]: rent_sponsor pub fn process_create_token_account_invoke( accounts: &[AccountInfo], data: CreateTokenAccountData, @@ -251,6 +198,7 @@ pub fn process_create_token_account_invoke( return Err(ProgramError::NotEnoughAccountKeys); } + // Build the compressible params using constructor let compressible_params = CompressibleParamsInfos::new( data.pre_pay_num_epochs, data.lamports_per_write, @@ -259,6 +207,7 @@ pub fn process_create_token_account_invoke( accounts[4].clone(), ); + // Build the account infos struct CreateCTokenAccountInfos { payer: accounts[0].clone(), account: accounts[1].clone(), @@ -271,7 +220,15 @@ pub fn process_create_token_account_invoke( Ok(()) } -/// Creates a PDA-owned compressible cToken account +/// Handler for creating a compressible token account with PDA ownership (invoke_signed) +/// +/// Account order: +/// - accounts[0]: payer (signer) +/// - accounts[1]: account to create (PDA, will be derived and verified) +/// - accounts[2]: mint +/// - accounts[3]: compressible_config +/// - accounts[4]: system_program +/// - accounts[5]: rent_sponsor pub fn process_create_token_account_invoke_signed( accounts: &[AccountInfo], data: CreateTokenAccountData, @@ -280,11 +237,15 @@ pub fn process_create_token_account_invoke_signed( return Err(ProgramError::NotEnoughAccountKeys); } + // Derive the PDA for the token account let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); + + // Verify the account to create is the PDA if &pda != accounts[1].key { return Err(ProgramError::InvalidSeeds); } + // Build the compressible params using constructor let compressible_params = CompressibleParamsInfos::new( data.pre_pay_num_epochs, data.lamports_per_write, @@ -293,6 +254,7 @@ pub fn process_create_token_account_invoke_signed( accounts[4].clone(), ); + // Build the account infos struct let account_infos = CreateCTokenAccountInfos { payer: accounts[0].clone(), account: accounts[1].clone(), @@ -301,28 +263,29 @@ pub fn process_create_token_account_invoke_signed( compressible: Some(compressible_params), }; + // Invoke with PDA signing let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; account_infos.invoke_signed(&[signer_seeds])?; Ok(()) } ``` + + + + +Coming soon. + + + # Next Steps - - - + From 5a1f4ea2a2fafd720bea79564cf085744d9dad34 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 27 Nov 2025 19:11:24 +0000 Subject: [PATCH 006/143] Update program-create-ctoken description and consolidate guide structure - Update frontmatter description with implementation details - Merge build and CPI steps into single consolidated section - Inline code snippets to show complete implementation flow - Add detailed comments to full code examples - Remove redundant imports from code blocks - Reorder tabs: Program first, Client second - Simplify Next Steps section with single card --- compressed-token-program/ctoken/program-create-ctoken.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compressed-token-program/ctoken/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-create-ctoken.mdx index 96de3a89..12d139f6 100644 --- a/compressed-token-program/ctoken/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-create-ctoken.mdx @@ -1,6 +1,6 @@ --- title: Create cToken Accounts -description: Guide to create compressible cToken accounts from programs, and clients. +description: Program and client guide to create compressible cToken accounts with step-by-step implementation and full code examples. --- import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; From fbed74e344e589d843e05fb4e093af958bec321d Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 27 Nov 2025 22:37:48 +0000 Subject: [PATCH 007/143] Add Rust client implementation to program-create-ctoken guide - Add Rust client tab with invoke/invoke_signed examples - Add instruction data configuration with discriminators (2u8/3u8) - Add full code examples with account verification - Add snippets: ctoken-create-accounts-list-client.mdx, ctoken-protocol-pdas.mdx - Include prerequisites: dependencies and existing mint requirement - Document PDA derivation and signer differences between invoke methods --- .../ctoken/program-create-ctoken.mdx | 348 +++++++++++++++++- .../ctoken-create-accounts-list-client.mdx | 74 ++++ snippets/ctoken-protocol-pdas.mdx | 27 ++ 3 files changed, 444 insertions(+), 5 deletions(-) create mode 100644 snippets/ctoken-create-accounts-list-client.mdx create mode 100644 snippets/ctoken-protocol-pdas.mdx diff --git a/compressed-token-program/ctoken/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-create-ctoken.mdx index 12d139f6..523aed39 100644 --- a/compressed-token-program/ctoken/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-create-ctoken.mdx @@ -4,6 +4,8 @@ description: Program and client guide to create compressible cToken accounts wit --- import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; +import CTokenCreateAccountsListClient from '/snippets/ctoken-create-accounts-list-client.mdx'; +import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; ## Key Points @@ -26,8 +28,7 @@ cToken accounts are compressible with rent-exemption sponsorship: Compressible cToken accounts are created via CPI to the cToken Program. -* For **user-owned accounts** use `invoke()` - the user's wallet signs the transaction -* For **PDA-owned accounts** use `invoke_signed()` - the owner program provides the seeds to sign the transaction +_Add key points_ Find [full code examples at the end](#full-code-example). @@ -95,7 +96,9 @@ Your program receives these accounts from the client. 3. CPI to cToken Program -Call `invoke()` for user-owned accounts or `invoke_signed()` for PDA-owned accounts. + +* For **user-owned accounts** use `invoke()` - the user's wallet signs the transaction +* For **PDA-owned accounts** use `invoke_signed()` - the owner program provides the seeds to sign the transaction @@ -272,9 +275,344 @@ pub fn process_create_token_account_invoke_signed( ``` - + + +Build and send instructions to your program that CPIs to the cToken Program. + +* For **user-owned accounts** use discriminator `2u8` - the cToken account keypair signs +* For **PDA-owned accounts** use discriminator `3u8` - your program signs via `invoke_signed` + + +Find [full code examples at the end](#full-code-example-1). + + + + + +### Prerequisites + +#### Dependencies + +```toml +[dependencies] +light-compressed-token-sdk = "0.1.0" +light-client = "0.16.0" +solana-sdk = "2.2" +borsh = "0.10.0" +``` + +#### Existing Mint +A cMint / SPL mint (with token pool) must exist before creating a cToken account. + + +The full code example uses a [helper function to set up a mint](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/tests/shared.rs). + + + + + + + +### Configure Rent & Instruction Data + + +```rust invoke (User-Owned) +let create_token_account_data = CreateTokenAccountData { + owner, + pre_pay_num_epochs: 2, + lamports_per_write: 1, +}; + +// Discriminator 2 = CreateTokenAccountInvoke +let instruction_data = [ + vec![2u8], + create_token_account_data.try_to_vec().unwrap() +].concat(); +``` + +```rust invoke_signed (PDA-Owned) highlight={7, 9} +let create_token_account_data = CreateTokenAccountData { + owner, + pre_pay_num_epochs: 2, + lamports_per_write: 1, +}; + +// Discriminator 3 = CreateTokenAccountInvokeSigned +let instruction_data = [ + vec![3u8], + create_token_account_data.try_to_vec().unwrap() +].concat(); +``` + + + + +1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** + * Customize N **based on expected account activity**. + * The account stays decompressed while it has rent for N epochs. + +2. Set `lamports_per_write` for the **top-up amount**. + * Top-ups are **made by** the **transaction fee payer** when the **lamports balance** falls **below two epochs of rent**. + +3. Select discriminator based on account ownership: + * For **user-owned accounts** use discriminator `2u8` - the cToken account keypair signs + * For **PDA-owned accounts** use discriminator `3u8` - your program signs via `invoke_signed` + + + +### Build Instruction + +Fetch the protocol accounts required for the creation of the cToken account and build the instruction. + + + + + +```rust invoke (User-Owned) +// Fetch Protocol Accounts +let config = config_pda(); +let rent_sponsor = rent_sponsor_pda(); + +let ctoken_account = Keypair::new(); + +let instruction = Instruction { + program_id: YOUR_PROGRAM_ID, + accounts: vec![ + AccountMeta::new(payer.pubkey(), true), // payer (signer) + AccountMeta::new(ctoken_account.pubkey(), true), // account (signer) + AccountMeta::new_readonly(mint_pda, false), // mint + AccountMeta::new_readonly(config, false), // compressible_config + AccountMeta::new_readonly(Pubkey::default(), false), // system_program + AccountMeta::new(rent_sponsor, false), // rent_sponsor + AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), + ], + data: instruction_data, +}; +``` + +```rust invoke_signed (PDA-Owned) highlight={5-7,13,23-26} +// Fetch Protocol Accounts +let config = config_pda(); +let rent_sponsor = rent_sponsor_pda(); + +// Derive the PDA for the token account +let token_account_seed: &[u8] = b"token_account"; +let (ctoken_account_pda, _bump) = Pubkey::find_program_address(&[token_account_seed], &YOUR_PROGRAM_ID); + +let instruction = Instruction { + program_id: YOUR_PROGRAM_ID, + accounts: vec![ + AccountMeta::new(payer.pubkey(), true), // payer (signer) + AccountMeta::new(ctoken_account_pda, false), // PDA, not a signer + AccountMeta::new_readonly(mint_pda, false), // mint + AccountMeta::new_readonly(config, false), // compressible_config + AccountMeta::new_readonly(Pubkey::default(), false), // system_program + AccountMeta::new(rent_sponsor, false), // rent_sponsor + AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), + ], + data: instruction_data, +}; +``` + + + + + +### Send Transaction + +```rust invoke (User-Owned) +// Payer signs +rpc.create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&payer] +).await?; +``` + +```rust invoke_signed (PDA-Owned) highlight={5} +// Both payer and ctoken_account sign +rpc.create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&payer, &ctoken_account] +).await?; +``` + + + + +## Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/tests/test_create_token_account.rs). + + + + +```typescript TypeScript +// Coming soon. +``` + +```rust Rust +use borsh::{BorshDeserialize, BorshSerialize}; +use light_client::rpc::Rpc; +use light_compressed_token_sdk::ctoken::CTOKEN_PROGRAM_ID; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use native_ctoken_examples::{CreateTokenAccountData, ID}; +use shared::setup_create_compressed_mint; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + signature::Keypair, + signer::Signer, +}; + +/// Create a token account using CreateCTokenAccountInfos::invoke() +async fn create_token_account_invoke() { + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( + false, + Some(vec![("native_ctoken_examples", ID)]), + )) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + let mint_authority = payer.pubkey(); + + // Create compressed mint first (using helper) + let (mint_pda, _compression_address, _) = + setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; + + // Create ctoken account via wrapper program + let ctoken_account = Keypair::new(); + let owner = payer.pubkey(); + + let create_token_account_data = CreateTokenAccountData { + owner, + pre_pay_num_epochs: 2, + lamports_per_write: 1, + }; + let instruction_data = [vec![2u8], create_token_account_data.try_to_vec().unwrap()].concat(); + + use light_compressed_token_sdk::ctoken::{config_pda, rent_sponsor_pda}; + let config = config_pda(); + let rent_sponsor = rent_sponsor_pda(); + + let instruction = Instruction { + program_id: ID, + accounts: vec![ + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new(ctoken_account.pubkey(), true), + AccountMeta::new_readonly(mint_pda, false), + AccountMeta::new_readonly(config, false), + AccountMeta::new_readonly(Pubkey::default(), false), // system_program + AccountMeta::new(rent_sponsor, false), + AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), + ], + data: instruction_data, + }; + + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &ctoken_account]) + .await + .unwrap(); + + // Verify ctoken account was created + let ctoken_account_data = rpc + .get_account(ctoken_account.pubkey()) + .await + .unwrap() + .unwrap(); + + // Parse and verify account data + use light_ctoken_types::state::CToken; + let account_state = CToken::deserialize(&mut &ctoken_account_data.data[..]).unwrap(); + assert_eq!( + account_state.mint.to_bytes(), + mint_pda.to_bytes(), + "Mint should match" + ); + assert_eq!( + account_state.owner.to_bytes(), + owner.to_bytes(), + "Owner should match" + ); + assert_eq!(account_state.amount, 0, "Initial amount should be 0"); +} + +/// Create a PDA-owned token account using CreateCTokenAccountInfos::invoke_signed() +async fn create_token_account_invoke_signed() { + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( + false, + Some(vec![("native_ctoken_examples", ID)]), + )) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + let mint_authority = payer.pubkey(); + + // Create compressed mint first (using helper) + let (mint_pda, _compression_address, _) = + setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; + + // Derive the PDA for the token account (same seeds as in the program) + let token_account_seed: &[u8] = b"token_account"; + let (ctoken_account_pda, _bump) = Pubkey::find_program_address(&[token_account_seed], &ID); + + let owner = payer.pubkey(); + + let create_token_account_data = CreateTokenAccountData { + owner, + pre_pay_num_epochs: 2, + lamports_per_write: 1, + }; + // Discriminator 3 = CreateTokenAccountInvokeSigned + let instruction_data = [vec![3u8], create_token_account_data.try_to_vec().unwrap()].concat(); + + use light_compressed_token_sdk::ctoken::{config_pda, rent_sponsor_pda}; + let config = config_pda(); + let rent_sponsor = rent_sponsor_pda(); + + let instruction = Instruction { + program_id: ID, + accounts: vec![ + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new(ctoken_account_pda, false), // PDA, not a signer + AccountMeta::new_readonly(mint_pda, false), + AccountMeta::new_readonly(config, false), + AccountMeta::new_readonly(Pubkey::default(), false), // system_program + AccountMeta::new(rent_sponsor, false), + AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), + ], + data: instruction_data, + }; + + // Note: only payer signs, the PDA account is signed by the program via invoke_signed + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Verify ctoken account was created + let ctoken_account_data = rpc.get_account(ctoken_account_pda).await.unwrap().unwrap(); + + // Parse and verify account data + use light_ctoken_types::state::CToken; + let account_state = CToken::deserialize(&mut &ctoken_account_data.data[..]).unwrap(); + assert_eq!( + account_state.mint.to_bytes(), + mint_pda.to_bytes(), + "Mint should match" + ); + assert_eq!( + account_state.owner.to_bytes(), + owner.to_bytes(), + "Owner should match" + ); + assert_eq!(account_state.amount, 0, "Initial amount should be 0"); +} +``` -Coming soon. + diff --git a/snippets/ctoken-create-accounts-list-client.mdx b/snippets/ctoken-create-accounts-list-client.mdx new file mode 100644 index 00000000..f48eb1f6 --- /dev/null +++ b/snippets/ctoken-create-accounts-list-client.mdx @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ConstraintsDescription
0Payersigner, mutable + - Pays transaction fee and compression incentive (prepaid epochs).
+ - Does NOT pay rent exemption (fronted by `rent_sponsor`). +
1Token Accountsigner*, mutable + - The cToken account being created.
+ - *Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds. +
2Mint-The SPL or cMint token mint. Defines which token this account holds.
3Compressible Config- + Protocol PDA that stores rent config and compression authorities. +
4System Program-Solana System Program. Required for CPI to create the on-chain account.
5Rent Sponsormutable + - CToken program PDA that fronts rent exemption at creation.
+ - Claims rent when account compresses. +
6cToken Program- + - Program to create and interact with cMints, cTokens and compressed tokens. + - Your program calls this to create the cToken account. +
\ No newline at end of file diff --git a/snippets/ctoken-protocol-pdas.mdx b/snippets/ctoken-protocol-pdas.mdx new file mode 100644 index 00000000..b68a78c5 --- /dev/null +++ b/snippets/ctoken-protocol-pdas.mdx @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + +
Compressible Config + Protocol PDA that stores rent config and compression authorities. +
Rent Sponsor + - CToken program PDA that fronts rent exemption at creation.
+ - Claims rent when account compresses. +
\ No newline at end of file From 5f7a307dce8a9e3e164b6a2234f27022f8a524b3 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 27 Nov 2025 22:53:18 +0000 Subject: [PATCH 008/143] Refine client guide: add account generation and remove unused snippet - Add cToken keypair generation step in invoke (User-Owned) example - Clarify discriminator differences between invoke patterns - Update client account list to include cToken program (account 6) - Remove unused ctoken-protocol-pdas.mdx snippet - Add inline comments to build instruction examples --- .../ctoken/program-create-ctoken.mdx | 27 +++++++++---------- .../ctoken-create-accounts-list-client.mdx | 3 ++- snippets/ctoken-protocol-pdas.mdx | 27 ------------------- 3 files changed, 15 insertions(+), 42 deletions(-) delete mode 100644 snippets/ctoken-protocol-pdas.mdx diff --git a/compressed-token-program/ctoken/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-create-ctoken.mdx index 523aed39..e5050672 100644 --- a/compressed-token-program/ctoken/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-create-ctoken.mdx @@ -277,10 +277,7 @@ pub fn process_create_token_account_invoke_signed( -Build and send instructions to your program that CPIs to the cToken Program. - -* For **user-owned accounts** use discriminator `2u8` - the cToken account keypair signs -* For **PDA-owned accounts** use discriminator `3u8` - your program signs via `invoke_signed` +_add key points_ Find [full code examples at the end](#full-code-example-1). @@ -315,6 +312,8 @@ The full code example uses a [helper function to set up a mint](https://github.c ### Configure Rent & Instruction Data +The discriminator must be set differently for **user-owned accounts** and **PDA-owned accounts**. + ```rust invoke (User-Owned) let create_token_account_data = CreateTokenAccountData { @@ -345,8 +344,6 @@ let instruction_data = [ ``` - - 1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** * Customize N **based on expected account activity**. * The account stays decompressed while it has rent for N epochs. @@ -354,10 +351,9 @@ let instruction_data = [ 2. Set `lamports_per_write` for the **top-up amount**. * Top-ups are **made by** the **transaction fee payer** when the **lamports balance** falls **below two epochs of rent**. -3. Select discriminator based on account ownership: +3. Set discriminator based on account ownership. * For **user-owned accounts** use discriminator `2u8` - the cToken account keypair signs * For **PDA-owned accounts** use discriminator `3u8` - your program signs via `invoke_signed` - ### Build Instruction @@ -373,8 +369,10 @@ Fetch the protocol accounts required for the creation of the cToken account and let config = config_pda(); let rent_sponsor = rent_sponsor_pda(); +// Generate cToken keypair let ctoken_account = Keypair::new(); +// Build instruction with accounts and data let instruction = Instruction { program_id: YOUR_PROGRAM_ID, accounts: vec![ @@ -390,7 +388,7 @@ let instruction = Instruction { }; ``` -```rust invoke_signed (PDA-Owned) highlight={5-7,13,23-26} +```rust invoke_signed (PDA-Owned) highlight={5-7,14,23-26} // Fetch Protocol Accounts let config = config_pda(); let rent_sponsor = rent_sponsor_pda(); @@ -399,6 +397,7 @@ let rent_sponsor = rent_sponsor_pda(); let token_account_seed: &[u8] = b"token_account"; let (ctoken_account_pda, _bump) = Pubkey::find_program_address(&[token_account_seed], &YOUR_PROGRAM_ID); +// Build instruction with accounts and data let instruction = Instruction { program_id: YOUR_PROGRAM_ID, accounts: vec![ @@ -421,20 +420,20 @@ let instruction = Instruction { ### Send Transaction ```rust invoke (User-Owned) -// Payer signs +// Both payer and ctoken_account keypair sign rpc.create_and_send_transaction( &[instruction], &payer.pubkey(), - &[&payer] + &[&payer, &ctoken_account] ).await?; ``` -```rust invoke_signed (PDA-Owned) highlight={5} -// Both payer and ctoken_account sign +```rust invoke_signed (PDA-Owned) highlight={4} +// Only payer signs; program signs for PDA via invoke_signed rpc.create_and_send_transaction( &[instruction], &payer.pubkey(), - &[&payer, &ctoken_account] + &[&payer] ).await?; ``` diff --git a/snippets/ctoken-create-accounts-list-client.mdx b/snippets/ctoken-create-accounts-list-client.mdx index f48eb1f6..6e5d2e3b 100644 --- a/snippets/ctoken-create-accounts-list-client.mdx +++ b/snippets/ctoken-create-accounts-list-client.mdx @@ -29,7 +29,8 @@ signer*, mutable - The cToken account being created.
- - *Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds. + - *Must be signer for `invoke()`. + - *For `invoke_signed()`, program signs via PDA seeds. diff --git a/snippets/ctoken-protocol-pdas.mdx b/snippets/ctoken-protocol-pdas.mdx deleted file mode 100644 index b68a78c5..00000000 --- a/snippets/ctoken-protocol-pdas.mdx +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -
Compressible Config - Protocol PDA that stores rent config and compression authorities. -
Rent Sponsor - - CToken program PDA that fronts rent exemption at creation.
- - Claims rent when account compresses. -
\ No newline at end of file From 0db55b6d5fbab028d36d2c2619061f74c8aa9ac5 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 28 Nov 2025 02:53:22 +0000 Subject: [PATCH 009/143] Restructure cToken documentation: separate client and program guides - Reorganize cmint guides into client-guides/ and program-guides/ - Reorganize ctoken guides into client-guides/ and program-guides/ - Add program-create-cmint.mdx with CPI implementation - Add client guides: client-create-ctoken, client-create-cATA, client-create-cmint, client-mint-to-ctoken - Add program guides: program-create-ctoken, program-create-cATA, program-mint-to-cToken - Remove deprecated guides: SPL-to-ctoken-transfer, close-ctoken, compress-and-close, etc. - Add snippets: cmint-system-accounts-list, ctoken-create-ata-accounts-list - Wrap CompressibleVsSolanaRent in Accordion in c-token-program.mdx - Fix docs.json: properly nest client/program guides under cMint and cToken groups - Add compressed-tokens/README.mdx --- compressed-token-program/c-token-program.mdx | 4 + .../cmint/{ => client-guides}/cmint.mdx | 0 .../{ => client-guides}/update-metadata.mdx | 0 .../program-guides/program-create-cmint.mdx | 480 +++++++++++++ .../ctoken/SPL-to-ctoken-transfer.mdx | 36 - .../client-guides/client-create-cATA.mdx | 57 ++ .../client-guides/client-create-cmint.mdx | 91 +++ .../client-guides/client-create-ctoken.mdx | 139 ++++ .../client-guides/client-mint-to-ctoken.mdx | 104 +++ .../ctoken/close-ctoken.mdx | 38 -- .../ctoken/compress-and-close.mdx | 33 - .../ctoken/create-ata.mdx | 103 --- .../ctoken/create-ctoken.mdx | 120 ---- .../ctoken/ctoken-to-spl-decompress.mdx | 49 -- .../ctoken/ctoken-transfer.mdx | 53 -- .../ctoken/mint-actions.mdx | 43 -- .../ctoken/mint-ctokens.mdx | 33 - .../ctoken/program-create-ctoken.mdx | 628 ------------------ .../program-guides/program-create-cATA.mdx | 287 ++++++++ .../program-guides/program-create-ctoken.mdx | 279 ++++++++ .../program-guides/program-mint-to-cToken.mdx | 29 + compressed-tokens/README.mdx | 96 +++ docs.json | 89 ++- snippets/cmint-system-accounts-list.mdx | 46 ++ snippets/compressible-vs-solana-rent.mdx | 3 - snippets/ctoken-create-ata-accounts-list.mdx | 77 +++ 26 files changed, 1744 insertions(+), 1173 deletions(-) rename compressed-token-program/cmint/{ => client-guides}/cmint.mdx (100%) rename compressed-token-program/cmint/{ => client-guides}/update-metadata.mdx (100%) create mode 100644 compressed-token-program/cmint/program-guides/program-create-cmint.mdx delete mode 100644 compressed-token-program/ctoken/SPL-to-ctoken-transfer.mdx create mode 100644 compressed-token-program/ctoken/client-guides/client-create-cATA.mdx create mode 100644 compressed-token-program/ctoken/client-guides/client-create-cmint.mdx create mode 100644 compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx create mode 100644 compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx delete mode 100644 compressed-token-program/ctoken/close-ctoken.mdx delete mode 100644 compressed-token-program/ctoken/compress-and-close.mdx delete mode 100644 compressed-token-program/ctoken/create-ata.mdx delete mode 100644 compressed-token-program/ctoken/create-ctoken.mdx delete mode 100644 compressed-token-program/ctoken/ctoken-to-spl-decompress.mdx delete mode 100644 compressed-token-program/ctoken/ctoken-transfer.mdx delete mode 100644 compressed-token-program/ctoken/mint-actions.mdx delete mode 100644 compressed-token-program/ctoken/mint-ctokens.mdx delete mode 100644 compressed-token-program/ctoken/program-create-ctoken.mdx create mode 100644 compressed-token-program/ctoken/program-guides/program-create-cATA.mdx create mode 100644 compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx create mode 100644 compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx create mode 100644 compressed-tokens/README.mdx create mode 100644 snippets/cmint-system-accounts-list.mdx create mode 100644 snippets/ctoken-create-ata-accounts-list.mdx diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx index 0b01a8b4..5584363b 100644 --- a/compressed-token-program/c-token-program.mdx +++ b/compressed-token-program/c-token-program.mdx @@ -295,7 +295,11 @@ This extension makes cToken accounts 200x cheaper than SPL token accounts | allocate | 0.000011 | 0.002 | | rent for 24h | 0.000005 | - | + + + + # Associated cToken Account diff --git a/compressed-token-program/cmint/cmint.mdx b/compressed-token-program/cmint/client-guides/cmint.mdx similarity index 100% rename from compressed-token-program/cmint/cmint.mdx rename to compressed-token-program/cmint/client-guides/cmint.mdx diff --git a/compressed-token-program/cmint/update-metadata.mdx b/compressed-token-program/cmint/client-guides/update-metadata.mdx similarity index 100% rename from compressed-token-program/cmint/update-metadata.mdx rename to compressed-token-program/cmint/client-guides/update-metadata.mdx diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx new file mode 100644 index 00000000..be8a6e6a --- /dev/null +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -0,0 +1,480 @@ +--- +title: Create cMint Accounts +description: Program guide to create cMint accounts with step-by-step implementation and full code examples. +--- + +import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; + +## Key Points + +cMints are compressed accounts and created via CPI to the Compressed Token Program. + +* cMints uniquely represent a token on Solana and store its global metadata. +* cMints are rent-free. +* The `mint_signer` keypair/PDA derives both the SPL mint address and the cMint's `compression_address`. + + +Find [full code examples at the end](#full-code-example). + + +# Implementation Guide + +Your program's responsibility: +1. Deserialize the instruction data +2. Build the SDK structs (CreateCMintParams, SystemAccountInfos, CreateCMintAccountInfos) +3. Call .invoke() or .invoke_signed() + + + + +### Dependencies + +```toml +[dependencies] +light-compressed-token-sdk = "0.1.0" +solana-program = "2.2" +borsh = "0.10.0" +``` + + + + +### Instruction Data + +```rust +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateCmintData { + pub decimals: u8, + pub address_merkle_tree_root_index: u16, + pub mint_authority: Pubkey, + pub proof: CompressedProof, + pub compression_address: [u8; 32], + pub mint: Pubkey, + pub freeze_authority: Option, + pub extensions: Option>, +} +``` + +* Define the cMint struct to deserialize the instruction data correctly. +* The client passes a validity proof that proves the address of the cMint does not exist yet in the address tree. +Your program validates this proof by passing it to the CPI. + + +The address of a cMint is stored in an address Merkle tree. +You can safely ignore the `address_merkle_tree_root_index` - it tells your program where in the address tree the address is stored. + + + + + + +### Configure cMint + +Set `decimals`, `mint_authority`, `freeze_authority`, and `extensions`. + +```rust +let params = CreateCMintParams { + decimals: data.decimals, + address_merkle_tree_root_index: data.address_merkle_tree_root_index, + mint_authority: data.mint_authority, + proof: data.proof, + compression_address: data.compression_address, + mint: data.mint, + freeze_authority: data.freeze_authority, + extensions: data.extensions, +}; +``` + + + + + +### System Accounts + +Include system accounts such as the Light System Program required to interact with compressed state. +The client includes them in the instruction. + + + + + +```rust +let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[5].clone(), + registered_program_pda: accounts[6].clone(), + account_compression_authority: accounts[7].clone(), + account_compression_program: accounts[8].clone(), + system_program: accounts[9].clone(), +}; +``` + + + + +### CPI to Compressed Token Program + +Build the instruction and invoke the Compressed Token Program. + + +* Both mint_signer and authority are PDAs. + + + + + +// mint_signer is a keypair - user signs the transaction + +```rust +// Build the account infos struct +// In this case, payer == authority (accounts[3]) +CreateCMintAccountInfos { + mint_signer: accounts[2].clone(), + authority: accounts[3].clone(), + payer: accounts[3].clone(), + address_tree: accounts[11].clone(), + output_queue: accounts[10].clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, +} +.invoke()?; +``` + + + +// mint_signer is a PDA - program signs via invoke_signed + +```rust +// Build the account infos struct +// In this case, payer == authority (accounts[3]) +let account_infos = CreateCMintAccountInfos { + mint_signer: accounts[2].clone(), + authority: accounts[3].clone(), + payer: accounts[3].clone(), + address_tree: accounts[11].clone(), + output_queue: accounts[10].clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, +}; + +// Invoke with PDA signing +let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]]; +account_infos.invoke_signed(&[signer_seeds])?; +``` + + + + +```rust +// Build the account infos struct using SDK +let account_infos = CreateCMintAccountInfos { + mint_signer: accounts[2].clone(), + authority: accounts[3].clone(), + payer: accounts[4].clone(), + address_tree: accounts[11].clone(), + output_queue: accounts[10].clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, +}; + +// Invoke with both PDAs signing +let mint_signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_signer_bump]]; +let authority_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[authority_bump]]; +account_infos.invoke_signed(&[mint_signer_seeds, authority_seeds])?; +``` + + + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::{ + ctoken::{ + CreateCMintAccountInfos, CreateCMintParams, ExtensionInstructionData, SystemAccountInfos, + }, + CompressedProof, +}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::ID; + +/// PDA seed for mint signer in invoke_signed variant +pub const MINT_SIGNER_SEED: &[u8] = b"mint_signer"; + +/// Instruction data for create compressed mint +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateCmintData { + pub decimals: u8, + pub address_merkle_tree_root_index: u16, + pub mint_authority: Pubkey, + pub proof: CompressedProof, + pub compression_address: [u8; 32], + pub mint: Pubkey, + pub freeze_authority: Option, + pub extensions: Option>, +} + +/// Handler for creating a compressed mint (invoke) +/// +/// Uses the CreateCMintAccountInfos builder pattern. This demonstrates how to: +/// 1. Build the CreateCMintParams struct from instruction data +/// 2. Build the CreateCMintAccountInfos with accounts +/// 3. Call invoke() which handles instruction building and CPI +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: mint_signer (signer) +/// - accounts[3]: payer (signer, also authority) +/// - accounts[4]: payer again (fee_payer in SDK) +/// - accounts[5]: cpi_authority_pda +/// - accounts[6]: registered_program_pda +/// - accounts[7]: account_compression_authority +/// - accounts[8]: account_compression_program +/// - accounts[9]: system_program +/// - accounts[10]: output_queue +/// - accounts[11]: address_tree +/// - accounts[12] (optional): cpi_context_account +pub fn process_create_cmint( + accounts: &[AccountInfo], + data: CreateCmintData, +) -> Result<(), ProgramError> { + if accounts.len() < 12 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Build the params + let params = CreateCMintParams { + decimals: data.decimals, + address_merkle_tree_root_index: data.address_merkle_tree_root_index, + mint_authority: data.mint_authority, + proof: data.proof, + compression_address: data.compression_address, + mint: data.mint, + freeze_authority: data.freeze_authority, + extensions: data.extensions, + }; + + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[5].clone(), + registered_program_pda: accounts[6].clone(), + account_compression_authority: accounts[7].clone(), + account_compression_program: accounts[8].clone(), + system_program: accounts[9].clone(), + }; + + // Build the account infos struct + // In this case, payer == authority (accounts[3]) + CreateCMintAccountInfos { + mint_signer: accounts[2].clone(), + authority: accounts[3].clone(), + payer: accounts[3].clone(), + address_tree: accounts[11].clone(), + output_queue: accounts[10].clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + } + .invoke()?; + + Ok(()) +} + +/// Handler for creating a compressed mint with PDA mint signer (invoke_signed) +/// +/// Uses the CreateCMintAccountInfos builder pattern with invoke_signed. +/// The mint_signer is a PDA derived from this program. +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: mint_signer (PDA, not signer - program signs) +/// - accounts[3]: payer (signer, also authority) +/// - accounts[4]: payer again (fee_payer in SDK) +/// - accounts[5]: cpi_authority_pda +/// - accounts[6]: registered_program_pda +/// - accounts[7]: account_compression_authority +/// - accounts[8]: account_compression_program +/// - accounts[9]: system_program +/// - accounts[10]: output_queue +/// - accounts[11]: address_tree +/// - accounts[12] (optional): cpi_context_account +pub fn process_create_cmint_invoke_signed( + accounts: &[AccountInfo], + data: CreateCmintData, +) -> Result<(), ProgramError> { + if accounts.len() < 12 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the mint signer + let (pda, bump) = Pubkey::find_program_address(&[MINT_SIGNER_SEED], &ID); + + // Verify the mint_signer account is the PDA we expect + if &pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); + } + + // Build the params + let params = CreateCMintParams { + decimals: data.decimals, + address_merkle_tree_root_index: data.address_merkle_tree_root_index, + mint_authority: data.mint_authority, + proof: data.proof, + compression_address: data.compression_address, + mint: data.mint, + freeze_authority: data.freeze_authority, + extensions: data.extensions, + }; + + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[5].clone(), + registered_program_pda: accounts[6].clone(), + account_compression_authority: accounts[7].clone(), + account_compression_program: accounts[8].clone(), + system_program: accounts[9].clone(), + }; + + // Build the account infos struct + // In this case, payer == authority (accounts[3]) + let account_infos = CreateCMintAccountInfos { + mint_signer: accounts[2].clone(), + authority: accounts[3].clone(), + payer: accounts[3].clone(), + address_tree: accounts[11].clone(), + output_queue: accounts[10].clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + }; + + // Invoke with PDA signing + let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]]; + account_infos.invoke_signed(&[signer_seeds])?; + + Ok(()) +} + +/// Handler for creating a compressed mint with PDA mint signer AND PDA authority (invoke_signed) +/// +/// Uses the SDK's CreateCMintAccountInfos with separate authority and payer accounts. +/// Both mint_signer and authority are PDAs signed by this program. +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: mint_signer (PDA from MINT_SIGNER_SEED, not signer - program signs) +/// - accounts[3]: authority (PDA from MINT_AUTHORITY_SEED, not signer - program signs) +/// - accounts[4]: fee_payer (signer) +/// - accounts[5]: cpi_authority_pda +/// - accounts[6]: registered_program_pda +/// - accounts[7]: account_compression_authority +/// - accounts[8]: account_compression_program +/// - accounts[9]: system_program +/// - accounts[10]: output_queue +/// - accounts[11]: address_tree +/// - accounts[12] (optional): cpi_context_account +pub fn process_create_cmint_with_pda_authority( + accounts: &[AccountInfo], + data: CreateCmintData, +) -> Result<(), ProgramError> { + use crate::mint_to_ctoken::MINT_AUTHORITY_SEED; + + if accounts.len() < 12 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the mint signer + let (mint_signer_pda, mint_signer_bump) = + Pubkey::find_program_address(&[MINT_SIGNER_SEED], &ID); + + // Derive the PDA for the authority + let (authority_pda, authority_bump) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); + + // Verify the mint_signer account is the PDA we expect + if &mint_signer_pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); + } + + // Verify the authority account is the PDA we expect + if &authority_pda != accounts[3].key { + return Err(ProgramError::InvalidSeeds); + } + + // Build the params - authority is the PDA + let params = CreateCMintParams { + decimals: data.decimals, + address_merkle_tree_root_index: data.address_merkle_tree_root_index, + mint_authority: authority_pda, // Use the derived PDA as authority + proof: data.proof, + compression_address: data.compression_address, + mint: data.mint, + freeze_authority: data.freeze_authority, + extensions: data.extensions, + }; + + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[5].clone(), + registered_program_pda: accounts[6].clone(), + account_compression_authority: accounts[7].clone(), + account_compression_program: accounts[8].clone(), + system_program: accounts[9].clone(), + }; + + // Build the account infos struct using SDK + let account_infos = CreateCMintAccountInfos { + mint_signer: accounts[2].clone(), + authority: accounts[3].clone(), + payer: accounts[4].clone(), + address_tree: accounts[11].clone(), + output_queue: accounts[10].clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + }; + + // Invoke with both PDAs signing + let mint_signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_signer_bump]]; + let authority_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[authority_bump]]; + account_infos.invoke_signed(&[mint_signer_seeds, authority_seeds])?; + + Ok(()) +} +``` + +# Next Steps + + + diff --git a/compressed-token-program/ctoken/SPL-to-ctoken-transfer.mdx b/compressed-token-program/ctoken/SPL-to-ctoken-transfer.mdx deleted file mode 100644 index 30dffbb9..00000000 --- a/compressed-token-program/ctoken/SPL-to-ctoken-transfer.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: Migrate SPL to cToken -description: Guide to migrate SPL tokens into compressed tokens and transfer them to a CToken account. ---- - -Converts SPL tokens into compressed tokens and transfers them to a CToken account. Interacts with a token pool PDA to synchronize supply between SPL and compressed tokens. - -**Use when:** Bridging tokens from the SPL ecosystem to compressed tokens, allowing users to deposit SPL tokens into their CToken accounts. - -**Key parameters:** - -- `source_spl_token_account`: Public key of the source SPL token account -- `to`: Public key of the destination CToken account -- `amount`: Number of tokens to transfer -- `authority`: Authority of the source SPL token account (signer) -- `mint`: Public key of the mint -- `payer`: Transaction fee payer -- `token_pool_pda`: Public key of the token pool PDA -- `token_pool_pda_bump`: Bump seed for the token pool PDA -- `spl_token_program`: Public key of the SPL Token program - -**Example:** - -```rust -use light_token_client::actions::transfer2::spl_to_ctoken_transfer; - -spl_to_ctoken_transfer( - &mut rpc, - spl_token_account_keypair.pubkey(), - associated_token_account, - transfer_amount, - &sender, - &payer, -) -.await?; -``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx b/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx new file mode 100644 index 00000000..f85b0847 --- /dev/null +++ b/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx @@ -0,0 +1,57 @@ +--- +title: Create a Compressed Token ATA +description: Create an associated cToken account (cATA) using the cToken SDK. +--- + + + + +```rust create_cata.rs +use light_compressed_token_sdk::ctoken::{ + derive_ctoken_ata, CompressibleParams, CreateAssociatedTokenAccount, +}; +use light_client::{indexer::Indexer, rpc::Rpc}; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; + +async fn create_compressed_ata( + rpc: &mut (impl Rpc + Indexer), + payer: &Keypair, + owner: Pubkey, + mint_pda: Pubkey, +) -> Pubkey { + // Derive the cToken ATA address + let (ata_address, _bump) = derive_ctoken_ata(&owner, &mint_pda); + + // Create ATA instruction + let compressible_params = CompressibleParams::default(); + let create_ata = CreateAssociatedTokenAccount::new( + payer.pubkey(), + owner, + mint_pda, + compressible_params, + ); + let instruction = create_ata.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer]) + .await + .unwrap(); + + println!("cATA created!"); + println!("cATA address: {}", ata_address); + println!("Owner: {}", owner); + println!("Mint: {}", mint_pda); + + ata_address +} +``` + + + + +```typescript create_cata.ts +// TODO: Add TypeScript example +``` + + + diff --git a/compressed-token-program/ctoken/client-guides/client-create-cmint.mdx b/compressed-token-program/ctoken/client-guides/client-create-cmint.mdx new file mode 100644 index 00000000..91f7143a --- /dev/null +++ b/compressed-token-program/ctoken/client-guides/client-create-cmint.mdx @@ -0,0 +1,91 @@ +--- +title: Create a Compressed Mint +description: Create a cMint using the cToken SDK. +--- + + + + +```rust create_cmint.rs +use light_compressed_token_sdk::ctoken::{CreateCMint, CreateCMintParams}; +use light_client::{indexer::Indexer, rpc::Rpc}; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; + +async fn create_compressed_mint( + rpc: &mut (impl Rpc + Indexer), + payer: &Keypair, + mint_authority: Pubkey, + decimals: u8, +) -> (Pubkey, [u8; 32]) { + let mint_signer = Keypair::new(); + let address_tree = rpc.get_address_tree_v2(); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; + + // Derive compression address + let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + &mint_signer.pubkey(), + &address_tree.tree, + ); + + // Derive mint PDA + let mint_pda = + light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + + // Get validity proof for the address + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![light_client::indexer::AddressWithTree { + address: compression_address, + tree: address_tree.tree, + }], + None, + ) + .await + .unwrap() + .value; + + // Build CreateCMint params + let params = CreateCMintParams { + decimals, + address_merkle_tree_root_index: rpc_result.addresses[0].root_index, + mint_authority, + proof: rpc_result.proof.0.unwrap(), + compression_address, + mint: mint_pda, + freeze_authority: None, + extensions: None, + }; + + // Create instruction + let create_cmint = CreateCMint::new( + params, + mint_signer.pubkey(), + payer.pubkey(), + address_tree.tree, + output_queue, + ); + let instruction = create_cmint.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) + .await + .unwrap(); + + println!("Compressed mint created!"); + println!("Mint PDA: {}", mint_pda); + println!("Compression address: {:?}", compression_address); + + (mint_pda, compression_address) +} +``` + + + + +```typescript create_cmint.ts +// TODO: Add TypeScript example +``` + + + diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx new file mode 100644 index 00000000..21a85d5b --- /dev/null +++ b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx @@ -0,0 +1,139 @@ +--- +title: Create a cToken Account +description: Guide to create a cToken account using the cSDK. +--- + +Create a cToken account with a new keypair (non-ATA). + + + + +```rust create-ctoken.rs + +``` + + + + +```typescript create-ctoken.ts +// TODO: TypeScript SDK coming soon +``` + + + + + +cToken accounts are compressible with rent-exemption sponsorship. The protocol funds the account's rent exemption at creation, and the account compresses automatically when inactive for ~84 minutes (12,600 slots). + + +# Full Code Example + + + +### Prerequisites + +You need a compressed mint (cMint) before creating cToken accounts. + + + +See [Create a Compressed Mint](/compressed-token-program/ctoken/client-guides/client-create-cmint) for the full guide. + + + + + +### Create cToken Account + + + + +```rust create_ctoken.rs expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_client::rpc::Rpc; +use light_compressed_token_sdk::ctoken::{config_pda, rent_sponsor_pda, CTOKEN_PROGRAM_ID}; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + signature::Keypair, + signer::Signer, +}; + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct CreateTokenAccountData { + pub owner: Pubkey, + pub pre_pay_num_epochs: u64, + pub lamports_per_write: u64, +} + +async fn create_ctoken_account( + rpc: &mut impl Rpc, + payer: &Keypair, + mint_pda: Pubkey, + owner: Pubkey, + wrapper_program_id: Pubkey, +) -> Pubkey { + // Step 1: Generate new keypair for cToken account + let ctoken_account = Keypair::new(); + + // Step 2: Build CreateTokenAccountData + let create_token_account_data = CreateTokenAccountData { + owner, + pre_pay_num_epochs: 2, + lamports_per_write: 1, + }; + let instruction_data = [vec![2u8], create_token_account_data.try_to_vec().unwrap()].concat(); + + // Step 3: Get PDAs + let config = config_pda(); + let rent_sponsor = rent_sponsor_pda(); + + // Step 4: Build instruction + let instruction = Instruction { + program_id: wrapper_program_id, + accounts: vec![ + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new(ctoken_account.pubkey(), true), + AccountMeta::new_readonly(mint_pda, false), + AccountMeta::new_readonly(config, false), + AccountMeta::new_readonly(Pubkey::default(), false), // system_program + AccountMeta::new(rent_sponsor, false), + AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), + ], + data: instruction_data, + }; + + // Step 5: Send transaction (ctoken_account must sign) + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &ctoken_account]) + .await + .unwrap(); + + println!("cToken account created!"); + println!("Account: {}", ctoken_account.pubkey()); + println!("Owner: {}", owner); + println!("Mint: {}", mint_pda); + + ctoken_account.pubkey() +} +``` + + + + +```typescript create_ctoken.ts expandable +// TODO: TypeScript SDK coming soon +``` + + + + + + +# Next Steps + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx new file mode 100644 index 00000000..9630896a --- /dev/null +++ b/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx @@ -0,0 +1,104 @@ +--- +title: Mint to cToken +description: Mint compressed tokens to cToken ATAs using the cToken SDK. +--- + + + + +```rust mint_to_ctoken.rs +use borsh::BorshDeserialize; +use light_compressed_token_sdk::ctoken::{MintToCToken, MintToCTokenParams}; +use light_ctoken_types::{ + instructions::mint_action::CompressedMintWithContext, + state::CompressedMint, +}; +use light_client::{indexer::Indexer, rpc::Rpc}; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; + +async fn mint_to_ctoken( + rpc: &mut (impl Rpc + Indexer), + payer: &Keypair, + mint_authority: Pubkey, + compression_address: [u8; 32], + ata_pubkeys: Vec, + amounts: Vec, +) { + // Get the compressed mint account + let compressed_mint_account = rpc + .get_compressed_account(compression_address, None) + .await + .unwrap() + .value + .expect("Compressed mint should exist"); + + // Deserialize the compressed mint + let compressed_mint = + CompressedMint::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) + .unwrap(); + + // Get validity proof for the mint operation + let rpc_result = rpc + .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) + .await + .unwrap() + .value; + + // Build CompressedMintWithContext + let compressed_mint_with_context = CompressedMintWithContext { + address: compression_address, + leaf_index: compressed_mint_account.leaf_index, + prove_by_index: true, + root_index: rpc_result.accounts[0] + .root_index + .root_index() + .unwrap_or_default(), + mint: compressed_mint.try_into().unwrap(), + }; + + // Build mint params with first recipient + let mut mint_params = MintToCTokenParams::new( + compressed_mint_with_context, + amounts[0], + mint_authority, + rpc_result.proof, + ); + mint_params.mint_to_actions[0].account_index = 0; + + // Add remaining recipients + for (idx, amount) in amounts.iter().enumerate().skip(1) { + mint_params = mint_params.add_mint_to_action(idx as u8, *amount); + } + + // Build MintToCToken instruction + let mint_to_ctoken = MintToCToken::new( + mint_params, + payer.pubkey(), + compressed_mint_account.tree_info.tree, + compressed_mint_account.tree_info.queue, + compressed_mint_account.tree_info.queue, + ata_pubkeys.clone(), + ); + let instruction = mint_to_ctoken.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer]) + .await + .unwrap(); + + println!("Tokens minted to cToken ATAs!"); + for (ata, amount) in ata_pubkeys.iter().zip(amounts.iter()) { + println!(" {} -> {} tokens", ata, amount); + } +} +``` + + + + +```typescript mint_to_ctoken.ts +// TODO: Add TypeScript example +``` + + + diff --git a/compressed-token-program/ctoken/close-ctoken.mdx b/compressed-token-program/ctoken/close-ctoken.mdx deleted file mode 100644 index 9d8f18dc..00000000 --- a/compressed-token-program/ctoken/close-ctoken.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Close cToken -description: Guide to close a cToken, a rent-free mint account to store global metadata of tokens. ---- -Permanently closes a decompressed CToken Solana account. Distributes remaining lamports to a destination account, then zeros out and resizes the account to 0 bytes to prevent revival attacks. Works with both regular and compressible token accounts. - -**Use when:** You want to permanently close a CToken account with a zero token balance and reclaim its rent exemption. - -**Key parameters:** - -- `token_account`: The CToken account to close (must be initialized with zero balance) -- `destination`: Account receiving remaining user funds (non-rent lamports) -- `authority`: Signer (account owner or rent authority for compressible accounts) -- `rent_sponsor`: (Required for compressible accounts) Account receiving rent exemption - -**Example:** - -```rust -use light_compressed_token_sdk::ctoken::close_account::{ - close_account, - close_compressible_account, -}; - -// Close non-compressible account -let close_ix = close_account( - token_account_pubkey, - destination_pubkey, - authority.pubkey(), -)?; - -// Close compressible account -let close_compressible_ix = close_compressible_account( - token_account_pubkey, - destination_pubkey, - authority.pubkey(), - rent_sponsor_pubkey, -)?; -``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/compress-and-close.mdx b/compressed-token-program/ctoken/compress-and-close.mdx deleted file mode 100644 index 3c8e05be..00000000 --- a/compressed-token-program/ctoken/compress-and-close.mdx +++ /dev/null @@ -1,33 +0,0 @@ -### CompressAndClose mode - - -The `Transfer2` instruction provides a `CompressAndClose` mode that compresses the full balance of a CToken account and closes it in a single transaction. - -**Use when:** Optimizing storage and costs by moving tokens off-chain into compressed format while cleaning up the on-chain account. - -**Key parameters:** - -- `amount`: Full balance of the CToken account to compress -- `mode`: Set to `CompressionMode::CompressAndClose` -- `mint`: Index of the mint account -- `source_or_recipient`: Index of the source CToken account -- `authority`: Index of the owner/delegate account (must be signer) -- `pool_account_index`: Used as `rent_sponsor_index` for CompressAndClose -- `pool_index`: Used as `compressed_account_index` for CompressAndClose -- `bump`: Used as `destination_index` for CompressAndClose - -**Example:** - -```rust -use light_token_client::actions::compress_and_close::compress_and_close; - -// Compress full balance and close account -compress_and_close( - &mut rpc, - ctoken_account_pubkey, - compressed_account_index, - &authority, - &payer, -) -.await?; -``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/create-ata.mdx b/compressed-token-program/ctoken/create-ata.mdx deleted file mode 100644 index 33ea1464..00000000 --- a/compressed-token-program/ctoken/create-ata.mdx +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: CreateAssociatedTokenAccount -description: Creates an associated token account (ATA) for compressed tokens ---- - - -1. Creates an associated token account (ATA) for compressed tokens, passing `owner` and `mint` public keys as instruction data. Supports both basic and compressible accounts. Includes idempotent versions that succeed even if the account already exists. - -**Use when:** Creating an ATA where owner and mint public keys are readily available and can be included directly in instruction data. - -2. Creates an associated token account with `owner` and `mint` passed as account infos rather than instruction data. Use when the program needs to perform checks or operations directly on the owner or mint accounts (e.g., signature verification, accessing mint data). - -**Use when:** The program needs to access owner or mint accounts as AccountInfo objects for verification or data access beyond just knowing their public keys. - -**Key parameters:** - -Same as CreateAssociatedTokenAccount, but `owner` and `mint` are passed as `AccountMeta` entries instead of instruction data. - - -### 1. -**Key parameters:** - -- `payer`: Public key of the account paying for transaction and account creation -- `owner`: Public key of the ATA owner -- `mint`: Public key of the mint -- `ata_pubkey`: Derived public key of the ATA (use `derive_ctoken_ata`) -- `bump`: PDA bump seed for ATA derivation - -**Compressible-specific parameters:** - -- `compressible_config`: Public key of the CompressibleConfig account -- `rent_sponsor`: Public key receiving lamports when account is closed by rent authority -- `pre_pay_num_epochs`: Number of epochs of rent to prepay -- `lamports_per_write`: (Optional) Initial lamports for rent top-ups -- `token_account_version`: TokenDataVersion specifying hashing scheme - -**Example:** - -```rust -use light_compressed_token_sdk::ctoken::create_associated_token_account::{ - create_associated_token_account, - create_compressible_associated_token_account, - CreateCompressibleAssociatedTokenAccountInputs, -}; -use light_ctoken_types::state::TokenDataVersion; - -// Basic ATA -let basic_ata_ix = create_associated_token_account( - payer_pubkey, - owner_pubkey, - mint_pubkey, -)?; - -// Compressible ATA -let compressible_ata_ix = create_compressible_associated_token_account( - CreateCompressibleAssociatedTokenAccountInputs { - payer: payer_pubkey, - owner: owner_pubkey, - mint: mint_pubkey, - compressible_config, - rent_sponsor, - pre_pay_num_epochs: 0, - lamports_per_write: Some(150), - token_account_version: TokenDataVersion::ShaFlat, - }, -)?; -``` - - -### 2. -**Key parameters:** - -Same as CreateAssociatedTokenAccount, but `owner` and `mint` are passed as `AccountMeta` entries instead of instruction data. - -**Example:** - -```rust -use light_compressed_token_sdk::ctoken::create_associated_token_account::{ - create_associated_token_account2, - create_compressible_associated_token_account2, -}; - -// Basic ATA (version 2) -let basic_ata2_ix = create_associated_token_account2( - payer_pubkey, - owner_pubkey, - mint_pubkey, -)?; - -// Compressible ATA (version 2) -let compressible_ata2_ix = create_compressible_associated_token_account2( - CreateCompressibleAssociatedTokenAccountInputs { - payer: payer_pubkey, - owner: owner_pubkey, - mint: mint_pubkey, - compressible_config, - rent_sponsor, - pre_pay_num_epochs: 0, - lamports_per_write: Some(150), - token_account_version: TokenDataVersion::ShaFlat, - }, -)?; -``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/create-ctoken.mdx b/compressed-token-program/ctoken/create-ctoken.mdx deleted file mode 100644 index da7cf821..00000000 --- a/compressed-token-program/ctoken/create-ctoken.mdx +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: Create a cToken Account with Compressible Extension -description: Guide to create a cToken account with compressible extension. ---- - -import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; - - - - - - - - - - - - - - - - - - - - - -
TypeKey Features
**[cMint](#cmint-accounts)**Solana account -
    -
  • **SPL-compatible**
  • -
  • Use for **active token accounts** with frequent writes (trading, etc.)
  • -
-
**[Compressible](#compressible)**Extension -
    -
  • **Sponsored rent-exemption** makes cTokens 200x cheaper than SPL tokens
  • -
  • Must be added at creation of cToken
  • -
-
- - - -Learn the core concepts to cTokens and compressible extension [here](/compressed-token-program/c-token-program#ctoken-accounts). - - - - -## Get Started - - - - -### Initialize cToken Account - - - - - -### Configure Compressible Extension -1. **Set the initial lamports balance** to N epochs (must be at least 2 epochs) - * Customize N **based on expected account activity**. - * You will top-up the account with rent for N epochs and it stays decompressed. -``` rust -rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) - -// For 260-byte cToken account: -// 128 + (260 * 1) = 388 lamports per epoch -``` - -2. **Set the top-up amount** for writes when the account balance falls below the set lamports balance. - * Determines how much lamports are added when the account needs a top-up. - * Top-ups are made by the **transaction fee payer** when the account is written to and is below the set lamports balance. - - -* Accounts become compressible when they advance two epochs without new transactions that add lamports. -* A forester node compresses the account after these two epochs and the protocol can reclaim the rent. - - - - -### Full Code Example - -```rust -use light_token_client::actions::{ - create_compressible_token_account, - CreateCompressibleTokenAccountInputs, -}; -use light_ctoken_types::state::TokenDataVersion; - -let token_account_pubkey = create_compressible_token_account( - &mut rpc, - CreateCompressibleTokenAccountInputs { - owner: owner_pubkey, - mint: mint_pubkey, - payer: &payer_keypair, - num_prepaid_epochs: 10, // 0 or ≥2 - lamports_per_write: Some(5_000), // lamports added per top-up - token_account_version: TokenDataVersion::ShaFlat, - token_account_keypair: None, // SDK generates keypair - } -).await?; -``` - - -For associated cToken accounts derive the address with [owner, compressed_token_program_id, mint](https://solscan.io/account/cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m). - - - - - - -## Next Steps - - - \ No newline at end of file diff --git a/compressed-token-program/ctoken/ctoken-to-spl-decompress.mdx b/compressed-token-program/ctoken/ctoken-to-spl-decompress.mdx deleted file mode 100644 index 98e27c7c..00000000 --- a/compressed-token-program/ctoken/ctoken-to-spl-decompress.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Transfer cToken to SPL account -description: Transfer compressed tokens from a cToken account to an SPL token account. ---- - - -Converts compressed tokens back to SPL tokens and transfers them to an SPL token account. Compresses the CToken balance into the token pool, then decompresses to the SPL account. - -**Use when:** Users want to withdraw compressed tokens from a CToken account back into the standard SPL token ecosystem. - -**Key parameters:** - -- `source_ctoken_account`: Public key of the source CToken account -- `destination_spl_token_account`: Public key of the destination SPL token account -- `amount`: Number of tokens to transfer -- `authority`: Authority of the source CToken account (signer) -- `mint`: Public key of the mint -- `payer`: Transaction fee payer -- `token_pool_pda`: Public key of the token pool PDA -- `token_pool_pda_bump`: Bump seed for the token pool PDA -- `spl_token_program`: Public key of the SPL Token program - -**Example:** - -```rust -use light_compressed_token_sdk::ctoken::transfer_interface::TransferCtokenToSpl; -use light_compressed_token_sdk::token_pool::find_token_pool_pda_with_index; - -let (token_pool_pda, token_pool_pda_bump) = find_token_pool_pda_with_index(&mint, 0); - -let transfer_ix = TransferCtokenToSpl { - source_ctoken_account, - destination_spl_token_account, - amount, - authority: authority.pubkey(), - mint, - payer: payer.pubkey(), - token_pool_pda, - token_pool_pda_bump, - spl_token_program: Pubkey::new_from_array(SPL_TOKEN_PROGRAM_ID), -} -.instruction()?; - -rpc.create_and_send_transaction( - &[transfer_ix], - &payer.pubkey(), - &signers -).await?; -``` diff --git a/compressed-token-program/ctoken/ctoken-transfer.mdx b/compressed-token-program/ctoken/ctoken-transfer.mdx deleted file mode 100644 index b0b88a91..00000000 --- a/compressed-token-program/ctoken/ctoken-transfer.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Transfer cToken to cToken account -description: Transfer tokens between two cToken accounts. ---- - - -Transfers tokens between two cToken accounts. Follows SPL Token semantics and includes automatic rent top-ups for compressible accounts. - -**Use when:** Performing standard token transfers between users who both hold decompressed CToken accounts. - -**Key parameters:** - -- `source`: Public key of the source CToken account -- `destination`: Public key of the destination CToken account -- `amount`: Number of tokens to transfer (u64) -- `authority`: Public key of the authority (owner or delegate) that can spend from source -- `payer`: (Optional) Public key paying for transaction fees and rent top-ups - -**Example:** - -```rust -use light_client::rpc::Rpc; -use light_token_client::actions::ctoken_transfer::create_transfer_ctoken_instruction; - -async fn example_ctoken_transfer( - rpc: &mut R, - source_pubkey: Pubkey, - destination_pubkey: Pubkey, - amount: u64, - authority_keypair: &Keypair, - payer_keypair: &Keypair, -) -> Result<(), RpcError> { - let transfer_instruction = create_transfer_ctoken_instruction( - source_pubkey, - destination_pubkey, - amount, - authority_keypair.pubkey(), - )?; - - let mut signers = vec![payer_keypair]; - if authority_keypair.pubkey() != payer_keypair.pubkey() { - signers.push(authority_keypair); - } - - rpc.create_and_send_transaction( - &[transfer_instruction], - &payer_keypair.pubkey(), - &signers - ).await?; - - Ok(()) -} -``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/mint-actions.mdx b/compressed-token-program/ctoken/mint-actions.mdx deleted file mode 100644 index 3bc6980a..00000000 --- a/compressed-token-program/ctoken/mint-actions.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Mint actions overview -description: Builder instructions for creating and managing compressed token accounts and transfers. ---- - -The CToken SDK provides builder instructions for managing compressed token accounts. These instructions follow a consistent builder pattern and handle account creation, minting, transfers, and conversions between compressed and SPL token formats. - -## Account creation - -### CreateCTokenAccount - - - -### CreateAssociatedTokenAccount - -### CreateAssociatedTokenAccount2 - - -## Mint operations - -### CreateCMint - - -### MintToCTokenTransfer - -## Transfer operations - -### CtokenTransfer - - -### SplToCtokenTransfer - - -### CtokenToSpl - -### CtokenToSplTransferAndClose - -## Account management - -### Close Token Account - -### CompressAndClose mode - diff --git a/compressed-token-program/ctoken/mint-ctokens.mdx b/compressed-token-program/ctoken/mint-ctokens.mdx deleted file mode 100644 index 8c17201e..00000000 --- a/compressed-token-program/ctoken/mint-ctokens.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Mint cTokens -description: Mint new tokens directly to decompressed CToken accounts. ---- - -Part of the MintAction batch instruction. - -**Use when:** Issuing new tokens of a specific compressed mint to users holding CToken accounts. - -**Key parameters:** - -- `account_index`: Index into remaining accounts for the recipient token account (u8) -- `amount`: Number of tokens to mint (u64) - -**Example:** - -```rust -use light_compressed_token_sdk::compressed_token::mint_action::{ - MintActionCompressedInstructionData, - MintToCTokenAction, -}; - -let instruction_data = MintActionCompressedInstructionData::new_mint( - input.compressed_mint_with_context.address, - input.compressed_mint_with_context.root_index, - compressed_proof, - input.compressed_mint_with_context.mint.clone(), -) -.with_mint_to_ctoken(MintToCTokenAction { - account_index: 0, // First account in remaining accounts - amount: 1_000_000, -}); -``` \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-create-ctoken.mdx deleted file mode 100644 index e5050672..00000000 --- a/compressed-token-program/ctoken/program-create-ctoken.mdx +++ /dev/null @@ -1,628 +0,0 @@ ---- -title: Create cToken Accounts -description: Program and client guide to create compressible cToken accounts with step-by-step implementation and full code examples. ---- - -import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; -import CTokenCreateAccountsListClient from '/snippets/ctoken-create-accounts-list-client.mdx'; -import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; -import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; - -## Key Points - -cToken accounts are compressible with rent-exemption sponsorship: - -1. The protocol funds the account's rent exemption at creation. -2. The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min) -3. The transaction payer top-ups the lamports balance when the account's lamports balance is below 2 epochs. -4. The account will be compressed when inactive for a period of 12,600 slots (84 min). -5. When the account is written to, it's automatically decompressed. - - - -# Implementation Guide - - - - - -Compressible cToken accounts are created via CPI to the cToken Program. - -_Add key points_ - -Find [full code examples at the end](#full-code-example). - - - - -### Dependencies - -```toml -[dependencies] -light-compressed-token-sdk = "0.1.0" -solana-program = "2.2" -borsh = "0.10.0" -``` - - - - - -### Configure Rent & Instruction Data - -```rust -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateTokenAccountData { - pub owner: Pubkey, - pub pre_pay_num_epochs: u8, - pub lamports_per_write: u32, -} -``` -1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** - * Customize N **based on expected account activity**. - * The account stays decompressed while it has rent for N epochs. -``` rust -rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) - -// For 260-byte cToken account: -// 128 + (260 * 1) = 388 lamports per epoch -``` - -2. **Set** in `lamports_per_write` the **top-up amount**. - * Top-ups are **made by** the **transaction fee payer** when the **lamports balance** falls **below two epochs of rent**. - - -* Accounts become compressible when they advance two epochs without new transactions that add lamports. -* A forester node compresses the account after these two epochs and the protocol can reclaim the rent. - - - - - - -### Build Account Infos & CPI cToken Program - -Your program receives these accounts from the client. - - - - - - -
Bundle them for the CPI: -1. Configure Rent Parameters - -2. Configure Token Account - -3. CPI to cToken Program - - -* For **user-owned accounts** use `invoke()` - the user's wallet signs the transaction -* For **PDA-owned accounts** use `invoke_signed()` - the owner program provides the seeds to sign the transaction - - - - -```rust -// Build the compressible params -let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), - accounts[5].clone(), - accounts[4].clone(), -); - -// Build the account infos struct -CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), - owner: data.owner, - compressible: Some(compressible_params), -} -.invoke()?; -``` - - - - -```rust -// Build the compressible params -let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), - accounts[5].clone(), - accounts[4].clone(), -); - -// Build the account infos struct -let account_infos = CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), - owner: data.owner, - compressible: Some(compressible_params), -}; - -// Invoke with PDA-signer -let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; -account_infos.invoke_signed(&[signer_seeds])?; - -Ok(()) -``` - - - - -
-
- -# Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). - - -```rust expandable -use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::{ID, TOKEN_ACCOUNT_SEED}; - -/// Instruction data for create token account -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateTokenAccountData { - pub owner: Pubkey, - pub pre_pay_num_epochs: u8, - pub lamports_per_write: u32, -} - -/// Handler for creating a compressible token account (invoke) -/// -/// Uses the builder pattern from the ctoken module. This demonstrates how to: -/// 1. Build the account infos struct with compressible params -/// 2. Call the invoke() method which handles instruction building and CPI -/// -/// Account order: -/// - accounts[0]: payer (signer) -/// - accounts[1]: account to create (signer) -/// - accounts[2]: mint -/// - accounts[3]: compressible_config -/// - accounts[4]: system_program -/// - accounts[5]: rent_sponsor -pub fn process_create_token_account_invoke( - accounts: &[AccountInfo], - data: CreateTokenAccountData, -) -> Result<(), ProgramError> { - if accounts.len() < 6 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Build the compressible params using constructor - let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), - accounts[5].clone(), - accounts[4].clone(), - ); - - // Build the account infos struct - CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), - owner: data.owner, - compressible: Some(compressible_params), - } - .invoke()?; - - Ok(()) -} - -/// Handler for creating a compressible token account with PDA ownership (invoke_signed) -/// -/// Account order: -/// - accounts[0]: payer (signer) -/// - accounts[1]: account to create (PDA, will be derived and verified) -/// - accounts[2]: mint -/// - accounts[3]: compressible_config -/// - accounts[4]: system_program -/// - accounts[5]: rent_sponsor -pub fn process_create_token_account_invoke_signed( - accounts: &[AccountInfo], - data: CreateTokenAccountData, -) -> Result<(), ProgramError> { - if accounts.len() < 6 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive the PDA for the token account - let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); - - // Verify the account to create is the PDA - if &pda != accounts[1].key { - return Err(ProgramError::InvalidSeeds); - } - - // Build the compressible params using constructor - let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), - accounts[5].clone(), - accounts[4].clone(), - ); - - // Build the account infos struct - let account_infos = CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), - owner: data.owner, - compressible: Some(compressible_params), - }; - - // Invoke with PDA signing - let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; - account_infos.invoke_signed(&[signer_seeds])?; - - Ok(()) -} -``` -
- - - -_add key points_ - - -Find [full code examples at the end](#full-code-example-1). - - - - - -### Prerequisites - -#### Dependencies - -```toml -[dependencies] -light-compressed-token-sdk = "0.1.0" -light-client = "0.16.0" -solana-sdk = "2.2" -borsh = "0.10.0" -``` - -#### Existing Mint -A cMint / SPL mint (with token pool) must exist before creating a cToken account. - - -The full code example uses a [helper function to set up a mint](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/tests/shared.rs). - - - - - - - -### Configure Rent & Instruction Data - -The discriminator must be set differently for **user-owned accounts** and **PDA-owned accounts**. - - -```rust invoke (User-Owned) -let create_token_account_data = CreateTokenAccountData { - owner, - pre_pay_num_epochs: 2, - lamports_per_write: 1, -}; - -// Discriminator 2 = CreateTokenAccountInvoke -let instruction_data = [ - vec![2u8], - create_token_account_data.try_to_vec().unwrap() -].concat(); -``` - -```rust invoke_signed (PDA-Owned) highlight={7, 9} -let create_token_account_data = CreateTokenAccountData { - owner, - pre_pay_num_epochs: 2, - lamports_per_write: 1, -}; - -// Discriminator 3 = CreateTokenAccountInvokeSigned -let instruction_data = [ - vec![3u8], - create_token_account_data.try_to_vec().unwrap() -].concat(); -``` - - -1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** - * Customize N **based on expected account activity**. - * The account stays decompressed while it has rent for N epochs. - -2. Set `lamports_per_write` for the **top-up amount**. - * Top-ups are **made by** the **transaction fee payer** when the **lamports balance** falls **below two epochs of rent**. - -3. Set discriminator based on account ownership. - * For **user-owned accounts** use discriminator `2u8` - the cToken account keypair signs - * For **PDA-owned accounts** use discriminator `3u8` - your program signs via `invoke_signed` - - -### Build Instruction - -Fetch the protocol accounts required for the creation of the cToken account and build the instruction. - - - - - -```rust invoke (User-Owned) -// Fetch Protocol Accounts -let config = config_pda(); -let rent_sponsor = rent_sponsor_pda(); - -// Generate cToken keypair -let ctoken_account = Keypair::new(); - -// Build instruction with accounts and data -let instruction = Instruction { - program_id: YOUR_PROGRAM_ID, - accounts: vec![ - AccountMeta::new(payer.pubkey(), true), // payer (signer) - AccountMeta::new(ctoken_account.pubkey(), true), // account (signer) - AccountMeta::new_readonly(mint_pda, false), // mint - AccountMeta::new_readonly(config, false), // compressible_config - AccountMeta::new_readonly(Pubkey::default(), false), // system_program - AccountMeta::new(rent_sponsor, false), // rent_sponsor - AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), - ], - data: instruction_data, -}; -``` - -```rust invoke_signed (PDA-Owned) highlight={5-7,14,23-26} -// Fetch Protocol Accounts -let config = config_pda(); -let rent_sponsor = rent_sponsor_pda(); - -// Derive the PDA for the token account -let token_account_seed: &[u8] = b"token_account"; -let (ctoken_account_pda, _bump) = Pubkey::find_program_address(&[token_account_seed], &YOUR_PROGRAM_ID); - -// Build instruction with accounts and data -let instruction = Instruction { - program_id: YOUR_PROGRAM_ID, - accounts: vec![ - AccountMeta::new(payer.pubkey(), true), // payer (signer) - AccountMeta::new(ctoken_account_pda, false), // PDA, not a signer - AccountMeta::new_readonly(mint_pda, false), // mint - AccountMeta::new_readonly(config, false), // compressible_config - AccountMeta::new_readonly(Pubkey::default(), false), // system_program - AccountMeta::new(rent_sponsor, false), // rent_sponsor - AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), - ], - data: instruction_data, -}; -``` - - - - - -### Send Transaction - -```rust invoke (User-Owned) -// Both payer and ctoken_account keypair sign -rpc.create_and_send_transaction( - &[instruction], - &payer.pubkey(), - &[&payer, &ctoken_account] -).await?; -``` - -```rust invoke_signed (PDA-Owned) highlight={4} -// Only payer signs; program signs for PDA via invoke_signed -rpc.create_and_send_transaction( - &[instruction], - &payer.pubkey(), - &[&payer] -).await?; -``` - - - - -## Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/tests/test_create_token_account.rs). - - - - -```typescript TypeScript -// Coming soon. -``` - -```rust Rust -use borsh::{BorshDeserialize, BorshSerialize}; -use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::CTOKEN_PROGRAM_ID; -use light_program_test::{LightProgramTest, ProgramTestConfig}; -use native_ctoken_examples::{CreateTokenAccountData, ID}; -use shared::setup_create_compressed_mint; -use solana_sdk::{ - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - signature::Keypair, - signer::Signer, -}; - -/// Create a token account using CreateCTokenAccountInfos::invoke() -async fn create_token_account_invoke() { - let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( - false, - Some(vec![("native_ctoken_examples", ID)]), - )) - .await - .unwrap(); - - let payer = rpc.get_payer().insecure_clone(); - let mint_authority = payer.pubkey(); - - // Create compressed mint first (using helper) - let (mint_pda, _compression_address, _) = - setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; - - // Create ctoken account via wrapper program - let ctoken_account = Keypair::new(); - let owner = payer.pubkey(); - - let create_token_account_data = CreateTokenAccountData { - owner, - pre_pay_num_epochs: 2, - lamports_per_write: 1, - }; - let instruction_data = [vec![2u8], create_token_account_data.try_to_vec().unwrap()].concat(); - - use light_compressed_token_sdk::ctoken::{config_pda, rent_sponsor_pda}; - let config = config_pda(); - let rent_sponsor = rent_sponsor_pda(); - - let instruction = Instruction { - program_id: ID, - accounts: vec![ - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new(ctoken_account.pubkey(), true), - AccountMeta::new_readonly(mint_pda, false), - AccountMeta::new_readonly(config, false), - AccountMeta::new_readonly(Pubkey::default(), false), // system_program - AccountMeta::new(rent_sponsor, false), - AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), - ], - data: instruction_data, - }; - - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &ctoken_account]) - .await - .unwrap(); - - // Verify ctoken account was created - let ctoken_account_data = rpc - .get_account(ctoken_account.pubkey()) - .await - .unwrap() - .unwrap(); - - // Parse and verify account data - use light_ctoken_types::state::CToken; - let account_state = CToken::deserialize(&mut &ctoken_account_data.data[..]).unwrap(); - assert_eq!( - account_state.mint.to_bytes(), - mint_pda.to_bytes(), - "Mint should match" - ); - assert_eq!( - account_state.owner.to_bytes(), - owner.to_bytes(), - "Owner should match" - ); - assert_eq!(account_state.amount, 0, "Initial amount should be 0"); -} - -/// Create a PDA-owned token account using CreateCTokenAccountInfos::invoke_signed() -async fn create_token_account_invoke_signed() { - let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( - false, - Some(vec![("native_ctoken_examples", ID)]), - )) - .await - .unwrap(); - - let payer = rpc.get_payer().insecure_clone(); - let mint_authority = payer.pubkey(); - - // Create compressed mint first (using helper) - let (mint_pda, _compression_address, _) = - setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; - - // Derive the PDA for the token account (same seeds as in the program) - let token_account_seed: &[u8] = b"token_account"; - let (ctoken_account_pda, _bump) = Pubkey::find_program_address(&[token_account_seed], &ID); - - let owner = payer.pubkey(); - - let create_token_account_data = CreateTokenAccountData { - owner, - pre_pay_num_epochs: 2, - lamports_per_write: 1, - }; - // Discriminator 3 = CreateTokenAccountInvokeSigned - let instruction_data = [vec![3u8], create_token_account_data.try_to_vec().unwrap()].concat(); - - use light_compressed_token_sdk::ctoken::{config_pda, rent_sponsor_pda}; - let config = config_pda(); - let rent_sponsor = rent_sponsor_pda(); - - let instruction = Instruction { - program_id: ID, - accounts: vec![ - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new(ctoken_account_pda, false), // PDA, not a signer - AccountMeta::new_readonly(mint_pda, false), - AccountMeta::new_readonly(config, false), - AccountMeta::new_readonly(Pubkey::default(), false), // system_program - AccountMeta::new(rent_sponsor, false), - AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), - ], - data: instruction_data, - }; - - // Note: only payer signs, the PDA account is signed by the program via invoke_signed - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - - // Verify ctoken account was created - let ctoken_account_data = rpc.get_account(ctoken_account_pda).await.unwrap().unwrap(); - - // Parse and verify account data - use light_ctoken_types::state::CToken; - let account_state = CToken::deserialize(&mut &ctoken_account_data.data[..]).unwrap(); - assert_eq!( - account_state.mint.to_bytes(), - mint_pda.to_bytes(), - "Mint should match" - ); - assert_eq!( - account_state.owner.to_bytes(), - owner.to_bytes(), - "Owner should match" - ); - assert_eq!(account_state.amount, 0, "Initial amount should be 0"); -} -``` - - - - -
- -# Next Steps - - - diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx new file mode 100644 index 00000000..2c6abcc3 --- /dev/null +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -0,0 +1,287 @@ +--- +title: Create Associated cToken Accounts +description: Program guide to create associated cToken accounts with step-by-step implementation and full code examples. +--- + +import CTokenCreateATAAccountsList from '/snippets/ctoken-create-ata-accounts-list.mdx'; +import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; +import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; + +## Key Points + +Associated cToken accounts (cATA) are Solana accounts and created via CPI to the cToken Program. + +1. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. +2. The rent-exemption is sponsored via the compressible extension. + * The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min). + * The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. + * The account will be compressed when inactive for a period of two epochs. + * When the account is written to, it’s automatically decompressed. + + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + + + +Find [full code examples at the end](#full-code-example). + + +# Implementation Guide + + + + +### Dependencies + +```toml +[dependencies] +light-compressed-token-sdk = "0.1.0" +solana-program = "2.2" +borsh = "0.10.0" +``` + + + + + +### Configure Rent & Instruction Data + +```rust +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateAtaData { + pub bump: u8, + pub pre_pay_num_epochs: u8, + pub lamports_per_write: u32, +} +``` + +The bump is passed to the cToken program to verify the address.
+
+1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** + * Customize N **based on expected account activity**. + * The account stays decompressed while it has rent for N epochs. +``` rust +rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) + +// For 260-byte cToken account: +// 128 + (260 * 1) = 388 lamports per epoch +``` + +2. **Set** in `lamports_per_write` the **top-up amount**. + * Top-ups are **made by** the **transaction fee payer** when the **lamports balance** falls **below two epochs of rent**. + + +* Accounts become compressible when they advance two epochs without new transactions that add lamports. +* A forester node compresses the account after these two epochs and the protocol can reclaim the rent. + + +
+ + + +### cToken Program CPI + +Build the instruction and CPI the cToken Program. + +1. Configure Rent Parameters +2. Pass the accounts to the cToken Program +3. CPI to cToken Program + + + +* When a CPI doesn't require PDA signers, the `invoke` function is used. +* When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. + + + +**Owner and mint are passed in accounts** (`accounts[0]` and `accounts[1]`), not in the instruction data. + +The address is derived with `[owner, ctoken_program_id, mint]` and passed via `accounts[3]`. + + + + +```rust +// Build the compressible params +let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[5].clone(), + accounts[6].clone(), + accounts[4].clone(), +); + +// Build the account infos struct +// Invoke with user signer +CreateAssociatedTokenAccount2Infos { + owner: accounts[0].clone(), + mint: accounts[1].clone(), + payer: accounts[2].clone(), + associated_token_account: accounts[3].clone(), + system_program: accounts[4].clone(), + bump: data.bump, + compressible: Some(compressible_params), + idempotent: false, +} +.invoke()?; +``` + + + + +```rust +// Build the compressible params +let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[5].clone(), + accounts[6].clone(), + accounts[4].clone(), +); + + +// Build the account infos struct +// Invoke with PDA signer +let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; +CreateAssociatedTokenAccount2Infos { + owner: accounts[0].clone(), + mint: accounts[1].clone(), + payer: accounts[2].clone(), // PDA + associated_token_account: accounts[3].clone(), + system_program: accounts[4].clone(), + bump: data.bump, + compressible: Some(compressible_params), + idempotent: false, +} +.invoke_signed(&[signer_seeds])?; +``` + + + + + +
+# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::{ + CompressibleParamsInfos, CreateAssociatedTokenAccount2Infos, +}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::{ATA_SEED, ID}; + +/// Instruction data for create ATA V2 (owner/mint as accounts) +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateAta2Data { + pub bump: u8, + pub pre_pay_num_epochs: u8, + pub lamports_per_write: u32, +} + +/// Handler for creating ATA using V2 variant (invoke) +/// +/// Account order: +/// - accounts[0]: owner (readonly) +/// - accounts[1]: mint (readonly) +/// - accounts[2]: payer (signer, writable) +/// - accounts[3]: associated_token_account (writable) +/// - accounts[4]: system_program +/// - accounts[5]: compressible_config +/// - accounts[6]: rent_sponsor (writable) +pub fn process_create_ata2_invoke( + accounts: &[AccountInfo], + data: CreateAta2Data, +) -> Result<(), ProgramError> { + if accounts.len() < 7 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[5].clone(), + accounts[6].clone(), + accounts[4].clone(), + ); + + CreateAssociatedTokenAccount2Infos { + owner: accounts[0].clone(), + mint: accounts[1].clone(), + payer: accounts[2].clone(), + associated_token_account: accounts[3].clone(), + system_program: accounts[4].clone(), + bump: data.bump, + compressible: Some(compressible_params), + idempotent: false, + } + .invoke()?; + + Ok(()) +} + +/// Handler for creating ATA using V2 variant with PDA ownership (invoke_signed) +/// +/// Account order: +/// - accounts[0]: owner (PDA, readonly) +/// - accounts[1]: mint (readonly) +/// - accounts[2]: payer (PDA, writable, not signer - program signs) +/// - accounts[3]: associated_token_account (writable) +/// - accounts[4]: system_program +/// - accounts[5]: compressible_config +/// - accounts[6]: rent_sponsor (writable) +pub fn process_create_ata2_invoke_signed( + accounts: &[AccountInfo], + data: CreateAta2Data, +) -> Result<(), ProgramError> { + if accounts.len() < 7 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA that will act as payer + let (pda, bump) = Pubkey::find_program_address(&[ATA_SEED], &ID); + + // Verify the payer is the PDA + if &pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); + } + + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[5].clone(), + accounts[6].clone(), + accounts[4].clone(), + ); + + let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; + CreateAssociatedTokenAccount2Infos { + owner: accounts[0].clone(), + mint: accounts[1].clone(), + payer: accounts[2].clone(), // PDA + associated_token_account: accounts[3].clone(), + system_program: accounts[4].clone(), + bump: data.bump, + compressible: Some(compressible_params), + idempotent: false, + } + .invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + +# Next Steps + + diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx new file mode 100644 index 00000000..07c582fa --- /dev/null +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -0,0 +1,279 @@ +--- +title: Create cToken Accounts +description: Program guide to create cToken accounts with step-by-step implementation and full code examples. +--- + +import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; +import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; +import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; + +## Key Points + +cToken accounts are Solana accounts and created via CPI to the cToken Program. + +1. cToken accounts hold token balances like SPL Token accounts. +2. The rent-exemption is sponsored via the compressible extension. + * The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min). + * The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. + * The account will be compressed when inactive for a period of two epochs. + * When the account is written to, it’s automatically decompressed. + + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + + + +Find [full code examples at the end](#full-code-example). + + +# Implementation Guide + + + + + +### Dependencies + +```toml +[dependencies] +light-compressed-token-sdk = "0.1.0" +solana-program = "2.2" +borsh = "0.10.0" +``` + + + + + +### Configure Rent & Instruction Data + +```rust +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateTokenAccountData { + pub owner: Pubkey, + pub pre_pay_num_epochs: u8, + pub lamports_per_write: u32, +} +``` +1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** + * Customize N **based on expected account activity**. + * The account stays decompressed while it has rent for N epochs. +``` rust +rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) + +// For 260-byte cToken account: +// 128 + (260 * 1) = 388 lamports per epoch +``` + +2. **Set** in `lamports_per_write` the **top-up amount**. + * Top-ups are **made by** the **transaction fee payer** when the **lamports balance** falls **below two epochs of rent**. + + +* Accounts become compressible when they advance two epochs without new transactions that add lamports. +* A forester node compresses the account after these two epochs and the protocol can reclaim the rent. + + + + + + +### cToken Program CPI + +Build the instruction and CPI the cToken Program. + +1. Configure Rent Parameters +2. Pass the accounts to the cToken Program +3. CPI to cToken Program + + + + + + + +* When a CPI doesn't require PDA signers, the `invoke` function is used. +* When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. + + + + +```rust +// Build the compressible params +let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), +); + +// Build the account infos struct +CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), +} +.invoke()?; +``` + + + + +```rust +// Build the compressible params +let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), +); + +// Build the account infos struct +// Invoke with PDA-signer +let account_infos = CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), +}; +let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; +account_infos.invoke_signed(&[signer_seeds])?; + +Ok(()) +``` + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::{ID, TOKEN_ACCOUNT_SEED}; + +/// Instruction data for create token account +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateTokenAccountData { + pub owner: Pubkey, + pub pre_pay_num_epochs: u8, + pub lamports_per_write: u32, +} + +/// Handler for creating a compressible token account (invoke) +/// +/// Uses the builder pattern from the ctoken module. This demonstrates how to: +/// 1. Build the account infos struct with compressible params +/// 2. Call the invoke() method which handles instruction building and CPI +/// +/// Account order: +/// - accounts[0]: payer (signer) +/// - accounts[1]: account to create (signer) +/// - accounts[2]: mint +/// - accounts[3]: compressible_config +/// - accounts[4]: system_program +/// - accounts[5]: rent_sponsor +pub fn process_create_token_account_invoke( + accounts: &[AccountInfo], + data: CreateTokenAccountData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Build the compressible params using constructor + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), + ); + + // Build the account infos struct + CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), + } + .invoke()?; + + Ok(()) +} + +/// Handler for creating a compressible token account with PDA ownership (invoke_signed) +/// +/// Account order: +/// - accounts[0]: payer (signer) +/// - accounts[1]: account to create (PDA, will be derived and verified) +/// - accounts[2]: mint +/// - accounts[3]: compressible_config +/// - accounts[4]: system_program +/// - accounts[5]: rent_sponsor +pub fn process_create_token_account_invoke_signed( + accounts: &[AccountInfo], + data: CreateTokenAccountData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the token account + let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); + + // Verify the account to create is the PDA + if &pda != accounts[1].key { + return Err(ProgramError::InvalidSeeds); + } + + // Build the compressible params using constructor + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), + ); + + // Build the account infos struct + let account_infos = CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), + }; + + // Invoke with PDA signing + let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; + account_infos.invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + +# Next Steps + + + diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx new file mode 100644 index 00000000..357e2f9d --- /dev/null +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -0,0 +1,29 @@ +--- +title: Mint cTokens +description: Program guide to mint cTokens with step-by-step implementation and full code examples. +--- + +import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; +import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; +import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; + +## Key Points + +cTokens are minted via CPI to the cToken Program. + +1. cTokens hold token balances like SPL Token accounts. +2. The rent-exemption is sponsored via the compressible extension. + * The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min). + * The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. + * The account will be compressed when inactive for a period of two epochs. + * When the account is written to, it’s automatically decompressed. + + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + + + +Find [full code examples at the end](#full-code-example). + + +# Implementation Guide \ No newline at end of file diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx new file mode 100644 index 00000000..91100e68 --- /dev/null +++ b/compressed-tokens/README.mdx @@ -0,0 +1,96 @@ +--- +title: Overview +description: "Overview to compressed tokens and guides with full code examples. Use for token distribution or storage of inactive token accounts" +--- + +import GuidesTable from '/snippets/compressed-tokens-guides-table.mdx'; +import AdvancedGuidesTable from '/snippets/compressed-tokens-advanced-guides-table.mdx'; +import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; + + + + + + + + + + + + + + + + +
Account TypeKey Features
**[Compressed Token](#compressed-token-account)**Compressed account +
    +
  • **Compressed account** with `TokenData` field
  • +
  • **Rent-free** and SPL-compatible
  • +
+
+ +Compressed token accounts store token balance, owner, and other information like SPL and cTokens. Any cToken or SPL token can be compressed/decompressed at will. + +## Recommended Usage of Compressed Tokens + + + #### [Token Distribution](#advanced-guides) + * Distribute tokens without paying up front rent per recipient. + * Cost reduction for airdrops, payments, rewards, etc: + + + + + + + + + + + + + + + +
SPL TokenCompressed Token
100,000 Token Accounts~ 200 SOL~ 0.004 SOL
+
+ + #### Storage of Inactive Token Accounts + * Most (associated) token accounts are not frequently written to. + * **Store token accounts rent-free** when inactive + * Use or migrate to [cTokens with compressible extension](/compressed-token-program/ctoken/ctoken) for **automatic compression/decompression** when inactive/active. + * The compressible extension allows users to **only pay rent, when token accounts are active**. + + +Leading **wallets** like Phantom and Backpack **support compressed tokens**. The UI does not distinguish between SPL and compressed tokens. + + + + + +### Install dependencies + + + + + + +### Set up your developer environment + + + + + + + +### Get started + + + + +## Basic Guides + + +## Advanced Guides + \ No newline at end of file diff --git a/docs.json b/docs.json index 63300b69..e54a76a5 100644 --- a/docs.json +++ b/docs.json @@ -41,15 +41,26 @@ ] }, { - "group": "Compressed Token Program", + "group": "cToken Program", "pages": [ "compressed-token-program/overview", "compressed-token-program/c-token-program", { "group": "cMint", "pages": [ - "compressed-token-program/cmint/cmint", - "compressed-token-program/cmint/update-metadata" + { + "group": "Client Guides", + "pages": [ + "compressed-token-program/cmint/client-guides/cmint", + "compressed-token-program/cmint/client-guides/update-metadata" + ] + }, + { + "group": "Program Guides", + "pages": [ + "compressed-token-program/cmint/program-guides/program-create-cmint" + ] + } ] }, { @@ -57,18 +68,19 @@ "pages": [ "compressed-token-program/ctoken/mint-actions", { - "group": "Guides", - "expanded": true, + "group": "Client Guides", "pages": [ - "compressed-token-program/ctoken/create-ctoken", - "compressed-token-program/ctoken/program-create-ctoken", - "compressed-token-program/ctoken/mint-ctokens", - "compressed-token-program/ctoken/create-ata", - "compressed-token-program/ctoken/ctoken-transfer", - "compressed-token-program/ctoken/SPL-to-ctoken-transfer", - "compressed-token-program/ctoken/ctoken-to-spl-decompress", - "compressed-token-program/ctoken/compress-and-close", - "compressed-token-program/ctoken/close-ctoken" + "compressed-token-program/ctoken/client-guides/client-create-ctoken", + "compressed-token-program/ctoken/client-guides/client-create-cATA", + "compressed-token-program/ctoken/client-guides/client-mint-to-ctoken" + ] + }, + { + "group": "Program Guides", + "pages": [ + "compressed-token-program/ctoken/program-guides/program-create-ctoken", + "compressed-token-program/ctoken/program-guides/program-create-cATA", + "compressed-token-program/ctoken/program-guides/program-mint-to-cToken" ] } ] @@ -76,19 +88,19 @@ { "group": "Compressed Token", "pages": [ - "compressed-token-program/compressed-tokens/compressed-token-overview", + "compressed-tokens/README", { "group": "Basic Guides", "pages": [ - "compressed-token-program/compressed-tokens/basic-guides/how-to-create-compressed-token-accounts", - "compressed-token-program/compressed-tokens/basic-guides/how-to-mint-compressed-tokens", - "compressed-token-program/compressed-tokens/basic-guides/how-to-transfer-compressed-token", - "compressed-token-program/compressed-tokens/basic-guides/how-to-compress-and-decompress-spl-tokens", - "compressed-token-program/compressed-tokens/basic-guides/how-to-compress-complete-spl-token-accounts", - "compressed-token-program/compressed-tokens/basic-guides/how-to-create-and-register-a-mint-account-for-compression", - "compressed-token-program/compressed-tokens/basic-guides/how-to-create-compressed-token-pools-for-mint-accounts", - "compressed-token-program/compressed-tokens/basic-guides/how-to-merge-compressed-token-accounts", - "compressed-token-program/compressed-tokens/basic-guides/how-to-approve-and-revoke-delegate-authority" + "compressed-tokens/basic-guides/how-to-create-compressed-token-accounts", + "compressed-tokens/basic-guides/how-to-mint-compressed-tokens", + "compressed-tokens/basic-guides/how-to-transfer-compressed-token", + "compressed-tokens/basic-guides/how-to-compress-and-decompress-spl-tokens", + "compressed-tokens/basic-guides/how-to-compress-complete-spl-token-accounts", + "compressed-tokens/basic-guides/how-to-create-and-register-a-mint-account-for-compression", + "compressed-tokens/basic-guides/how-to-create-compressed-token-pools-for-mint-accounts", + "compressed-tokens/basic-guides/how-to-merge-compressed-token-accounts", + "compressed-tokens/basic-guides/how-to-approve-and-revoke-delegate-authority" ] }, { @@ -468,17 +480,26 @@ "pages": [ "compressed-token-program/ctoken/mint-actions", { - "group": "Guides", + "group": "Client Guides", + "pages": [ + "compressed-token-program/ctoken/client-guides/client-create-ctoken", + "compressed-token-program/ctoken/client-guides/client-create-cmint", + "compressed-token-program/ctoken/client-guides/client-create-cATA", + "compressed-token-program/ctoken/client-guides/client-mint-to-ctoken" + ] + }, + { + "group": "Program Guides", "pages": [ - "compressed-token-program/ctoken/ctoken", - "compressed-token-program/ctoken/create-ctoken", - "compressed-token-program/ctoken/program-create-ctoken", - "compressed-token-program/ctoken/create-ata", - "compressed-token-program/ctoken/ctoken-transfer", - "compressed-token-program/ctoken/SPL-to-ctoken-transfer", - "compressed-token-program/ctoken/ctoken-to-spl-decompress", - "compressed-token-program/ctoken/compress-and-close", - "compressed-token-program/ctoken/close-ctoken" + "compressed-token-program/ctoken/program-guides/create-ctoken", + "compressed-token-program/ctoken/program-guides/program-create-ctoken", + "compressed-token-program/ctoken/program-guides/mint-ctokens", + "compressed-token-program/ctoken/program-guides/create-ata", + "compressed-token-program/ctoken/program-guides/ctoken-transfer", + "compressed-token-program/ctoken/program-guides/SPL-to-ctoken-transfer", + "compressed-token-program/ctoken/program-guides/ctoken-to-spl-decompress", + "compressed-token-program/ctoken/program-guides/compress-and-close", + "compressed-token-program/ctoken/program-guides/close-ctoken" ] } ] diff --git a/snippets/cmint-system-accounts-list.mdx b/snippets/cmint-system-accounts-list.mdx new file mode 100644 index 00000000..0002ffe0 --- /dev/null +++ b/snippets/cmint-system-accounts-list.mdx @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AccountDescription
1Light System ProgramVerifies validity proofs and executes compressed account state transitions.
2CPI Authority PDAPDA that authorizes CPIs from the Compressed Token Program to the Light System Program.
3Registered Program PDAProves the Compressed Token Program is registered to use compression.
4Account Compression AuthoritySigns CPI calls from the Light System Program to the Account Compression Program.
5Account Compression ProgramWrites to state and address Merkle tree accounts.
6System ProgramSolana System Program.
\ No newline at end of file diff --git a/snippets/compressible-vs-solana-rent.mdx b/snippets/compressible-vs-solana-rent.mdx index c66e7704..e860f3c7 100644 --- a/snippets/compressible-vs-solana-rent.mdx +++ b/snippets/compressible-vs-solana-rent.mdx @@ -1,5 +1,3 @@ - - ### Initial Rent Top-Up The **creator of compressible accounts** tops-up the account with **rent for at least two epochs**. Two epochs for compressible accounts have a length of **12,600 slots**. @@ -142,4 +140,3 @@ Compressible accounts can be closed by two parties. How rent and lamports balanc - \ No newline at end of file diff --git a/snippets/ctoken-create-ata-accounts-list.mdx b/snippets/ctoken-create-ata-accounts-list.mdx new file mode 100644 index 00000000..f5a6438c --- /dev/null +++ b/snippets/ctoken-create-ata-accounts-list.mdx @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ConstraintsDescription
0Owner- + - The wallet that will own this ATA.
+ - Used to derive the ATA address deterministically. +
1Mint- + - The SPL or cMint token mint.
+ - Used to derive the ATA address deterministically. +
2Payersigner, mutable + - Pays transaction fee and compression incentive (prepaid epochs).
+ - Does NOT pay rent exemption (fronted by `rent_sponsor`). +
3Associated Token Accountmutable + - The cATA being created.
+ - Address is derived from owner + mint + cToken program ID.
+
4System Program-Solana System Program. Required for CPI to create the on-chain account.
5Compressible Config- + Protocol PDA that stores rent config and compression authorities. +
6Rent Sponsormutable + - CToken program PDA that fronts rent exemption at creation.
+ - Claims rent when account compresses. +
\ No newline at end of file From 376dac7d0fffea7ff32c7ad68c430a21dd0e40be Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 28 Nov 2025 13:21:56 +0000 Subject: [PATCH 010/143] Refine program guides: improve structure and code formatting - Simplify program-create-cATA.mdx: consolidate steps and improve readability - Update program-create-ctoken.mdx: enhance code examples and documentation flow - Improve inline comments and parameter descriptions - Adjust formatting for better Mintlify rendering --- .../program-guides/program-create-cATA.mdx | 66 +++++++++---------- .../program-guides/program-create-ctoken.mdx | 48 ++++++++------ 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index 2c6abcc3..d049b7bb 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -30,8 +30,9 @@ Find [full code examples at the end](#full-code-example). +### Prerequisites -### Dependencies +#### Dependencies ```toml [dependencies] @@ -39,12 +40,7 @@ light-compressed-token-sdk = "0.1.0" solana-program = "2.2" borsh = "0.10.0" ``` - - - - - -### Configure Rent & Instruction Data +#### Instruction Data ```rust #[derive(BorshSerialize, BorshDeserialize, Debug)] @@ -54,9 +50,24 @@ pub struct CreateAtaData { pub lamports_per_write: u32, } ``` - -The bump is passed to the cToken program to verify the address.
-
+* Define the Associated cToken Account struct to deserialize the instruction data correctly. +* You will configure `pre_pay_num_epochs` and `lamports_per_write` in the next step. + +
+ + +### Configure Rent & Associated cToken Account + +```rust +let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[5].clone(), // Compressible Config + accounts[6].clone(), // Rent Sponsor + accounts[4].clone(), // System Program +); +``` + 1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** * Customize N **based on expected account activity**. * The account stays decompressed while it has rent for N epochs. @@ -75,22 +86,21 @@ rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) * A forester node compresses the account after these two epochs and the protocol can reclaim the rent.
+3. Accounts (add) + -### cToken Program CPI +### CPI to cToken Program Build the instruction and CPI the cToken Program. -1. Configure Rent Parameters -2. Pass the accounts to the cToken Program -3. CPI to cToken Program +1. Pass the accounts to the cToken Program +2. CPI to cToken Program -* When a CPI doesn't require PDA signers, the `invoke` function is used. -* When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. @@ -98,19 +108,13 @@ Build the instruction and CPI the cToken Program. The address is derived with `[owner, ctoken_program_id, mint]` and passed via `accounts[3]`. +* When a CPI doesn't require PDA signers, the `invoke` function is used. +* When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. + ```rust -// Build the compressible params -let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[5].clone(), - accounts[6].clone(), - accounts[4].clone(), -); - // Build the account infos struct // Invoke with user signer CreateAssociatedTokenAccount2Infos { @@ -127,19 +131,9 @@ CreateAssociatedTokenAccount2Infos { ``` - + ```rust -// Build the compressible params -let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[5].clone(), - accounts[6].clone(), - accounts[4].clone(), -); - - // Build the account infos struct // Invoke with PDA signer let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 07c582fa..345a7d47 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -28,11 +28,10 @@ Find [full code examples at the end](#full-code-example). # Implementation Guide - - -### Dependencies +### Prerequisites +#### Dependencies ```toml [dependencies] @@ -41,11 +40,7 @@ solana-program = "2.2" borsh = "0.10.0" ``` - - - - -### Configure Rent & Instruction Data +#### Instruction Data ```rust #[derive(BorshSerialize, BorshDeserialize, Debug)] @@ -55,6 +50,26 @@ pub struct CreateTokenAccountData { pub lamports_per_write: u32, } ``` + +* Define the cToken struct to deserialize the instruction data correctly. +* You will configure `pre_pay_num_epochs` and `lamports_per_write` in the next step. + + + + +### Configure Rent & cToken Account + +```rust +let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), // Compressible Config + accounts[5].clone(), // Rent Sponsor + accounts[4].clone(), // System Program +); +``` + + 1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** * Customize N **based on expected account activity**. * The account stays decompressed while it has rent for N epochs. @@ -73,17 +88,17 @@ rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) * A forester node compresses the account after these two epochs and the protocol can reclaim the rent. - +3. Accounts (add) + ### cToken Program CPI Build the instruction and CPI the cToken Program. -1. Configure Rent Parameters -2. Pass the accounts to the cToken Program -3. CPI to cToken Program +1. Pass the accounts to the cToken Program +2. CPI to cToken Program @@ -98,15 +113,6 @@ Build the instruction and CPI the cToken Program. ```rust -// Build the compressible params -let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), - accounts[5].clone(), - accounts[4].clone(), -); - // Build the account infos struct CreateCTokenAccountInfos { payer: accounts[0].clone(), From 4f4cfabea6c4fb3a4844a96e32282f0c243b290d Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sat, 29 Nov 2025 16:22:42 +0000 Subject: [PATCH 011/143] Add program transfer guides and rent calculator components - Add program-close-ctoken.mdx: close compressible token accounts - Add program-transfer-ctoken.mdx: transfer between cToken accounts - Add program-transfer-interface.mdx: transfer interface overview - Add program-transfer-spl-ctoken.mdx: SPL to cToken transfers - Add rent.mdx: rent configuration documentation - Add rent-calculator.jsx: interactive rent calculator component - Add solana-rent-calculator.jsx: Solana rent comparison calculator - Refine program-create-cmint: simplify account handling and structure - Refine program-create-cATA: improve rent config and CPI sections - Refine program-create-ctoken: add rent calculator and simplify structure - Complete program-mint-to-cToken: add full implementation guide - Simplify ctoken-create-accounts-list: remove protocol accounts - Update docs.json: add rent to Learn section --- .../program-guides/program-create-cmint.mdx | 105 ++---- .../program-guides/program-close-ctoken.mdx | 165 +++++++++ .../program-guides/program-create-cATA.mdx | 62 +--- .../program-guides/program-create-ctoken.mdx | 133 ++++--- .../program-guides/program-mint-to-cToken.mdx | 294 ++++++++++++++- .../program-transfer-ctoken.mdx | 161 ++++++++ .../program-transfer-interface.mdx | 233 ++++++++++++ .../program-transfer-spl-ctoken.mdx | 343 ++++++++++++++++++ docs.json | 3 +- rent.mdx | 33 ++ snippets/ctoken-create-accounts-list.mdx | 31 +- snippets/rent-calculator.jsx | 93 +++++ snippets/solana-rent-calculator.jsx | 59 +++ 13 files changed, 1496 insertions(+), 219 deletions(-) create mode 100644 compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx create mode 100644 compressed-token-program/ctoken/program-guides/program-transfer-ctoken.mdx create mode 100644 compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx create mode 100644 compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken.mdx create mode 100644 rent.mdx create mode 100644 snippets/rent-calculator.jsx create mode 100644 snippets/solana-rent-calculator.jsx diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index be8a6e6a..8e343d62 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -25,46 +25,6 @@ Your program's responsibility: 3. Call .invoke() or .invoke_signed() - - -### Dependencies - -```toml -[dependencies] -light-compressed-token-sdk = "0.1.0" -solana-program = "2.2" -borsh = "0.10.0" -``` - - - - -### Instruction Data - -```rust -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateCmintData { - pub decimals: u8, - pub address_merkle_tree_root_index: u16, - pub mint_authority: Pubkey, - pub proof: CompressedProof, - pub compression_address: [u8; 32], - pub mint: Pubkey, - pub freeze_authority: Option, - pub extensions: Option>, -} -``` - -* Define the cMint struct to deserialize the instruction data correctly. -* The client passes a validity proof that proves the address of the cMint does not exist yet in the address tree. -Your program validates this proof by passing it to the CPI. - - -The address of a cMint is stored in an address Merkle tree. -You can safely ignore the `address_merkle_tree_root_index` - it tells your program where in the address tree the address is stored. - - - @@ -73,6 +33,8 @@ You can safely ignore the `address_merkle_tree_root_index` - it tells your progr Set `decimals`, `mint_authority`, `freeze_authority`, and `extensions`. ```rust +use light_compressed_token_sdk::ctoken::CreateCMintParams; + let params = CreateCMintParams { decimals: data.decimals, address_merkle_tree_root_index: data.address_merkle_tree_root_index, @@ -98,14 +60,22 @@ The client includes them in the instruction. + + +The address of a cMint is stored in an address Merkle tree. +You can safely ignore the `address_merkle_tree_root_index` - it tells your program where in the address tree the address is stored. + + ```rust +use light_compressed_token_sdk::ctoken::SystemAccountInfos; + let system_accounts = SystemAccountInfos { - light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[5].clone(), - registered_program_pda: accounts[6].clone(), - account_compression_authority: accounts[7].clone(), - account_compression_program: accounts[8].clone(), - system_program: accounts[9].clone(), + light_system_program: light_system_program.clone(), + cpi_authority_pda: cpi_authority_pda.clone(), + registered_program_pda: registered_program_pda.clone(), + account_compression_authority: account_compression_authority.clone(), + account_compression_program: account_compression_program.clone(), + system_program: system_program.clone(), }; ``` @@ -126,14 +96,14 @@ Build the instruction and invoke the Compressed Token Program. // mint_signer is a keypair - user signs the transaction ```rust -// Build the account infos struct -// In this case, payer == authority (accounts[3]) +use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; + CreateCMintAccountInfos { - mint_signer: accounts[2].clone(), - authority: accounts[3].clone(), - payer: accounts[3].clone(), - address_tree: accounts[11].clone(), - output_queue: accounts[10].clone(), + mint_signer: mint_signer.clone(), + authority: authority.clone(), + payer: payer.clone(), + address_tree: address_tree.clone(), + output_queue: output_queue.clone(), system_accounts, cpi_context: None, cpi_context_account: None, @@ -147,21 +117,20 @@ CreateCMintAccountInfos { // mint_signer is a PDA - program signs via invoke_signed ```rust -// Build the account infos struct -// In this case, payer == authority (accounts[3]) +use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; + let account_infos = CreateCMintAccountInfos { - mint_signer: accounts[2].clone(), - authority: accounts[3].clone(), - payer: accounts[3].clone(), - address_tree: accounts[11].clone(), - output_queue: accounts[10].clone(), + mint_signer: mint_signer.clone(), + authority: authority.clone(), + payer: payer.clone(), + address_tree: address_tree.clone(), + output_queue: output_queue.clone(), system_accounts, cpi_context: None, cpi_context_account: None, params, }; -// Invoke with PDA signing let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]]; account_infos.invoke_signed(&[signer_seeds])?; ``` @@ -170,20 +139,20 @@ account_infos.invoke_signed(&[signer_seeds])?; ```rust -// Build the account infos struct using SDK +use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; + let account_infos = CreateCMintAccountInfos { - mint_signer: accounts[2].clone(), - authority: accounts[3].clone(), - payer: accounts[4].clone(), - address_tree: accounts[11].clone(), - output_queue: accounts[10].clone(), + mint_signer: mint_signer.clone(), + authority: authority.clone(), + payer: payer.clone(), + address_tree: address_tree.clone(), + output_queue: output_queue.clone(), system_accounts, cpi_context: None, cpi_context_account: None, params, }; -// Invoke with both PDAs signing let mint_signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_signer_bump]]; let authority_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[authority_bump]]; account_infos.invoke_signed(&[mint_signer_seeds, authority_seeds])?; diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx new file mode 100644 index 00000000..927d51dc --- /dev/null +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -0,0 +1,165 @@ +--- +title: Close cToken Accounts +description: Program guide to close cToken accounts with step-by-step implementation and full code examples. +--- + +## Key Points + +cToken accounts are closed via CPI to the cToken Program. + +1. Closing transfers remaining lamports to a destination account. +2. The account owner must sign the transaction (or be a PDA with invoke_signed). +3. The rent sponsor can reclaim sponsored rent. + + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + + + +Find [full code examples at the end](#full-code-example). + + +# Implementation Guide + + + + +### cToken Program CPI + +Build the close instruction and invoke the cToken Program. + +* When a CPI doesn't require PDA signers, the `invoke` function is used. +* When a CPI requires a PDA signer, the `invoke_signed` function is used. + + + + +```rust +use light_compressed_token_sdk::ctoken::CloseAccountInfos; + +CloseAccountInfos { + token_program: token_program.clone(), + account: account.clone(), + destination: destination.clone(), + owner: owner.clone(), + rent_sponsor: Some(rent_sponsor.clone()), // or None +} +.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::CloseAccountInfos; + +let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; +CloseAccountInfos { + token_program: token_program.clone(), + account: account.clone(), + destination: destination.clone(), + owner: owner.clone(), + rent_sponsor: Some(rent_sponsor.clone()), // or None +} +.invoke_signed(&[signer_seeds])?; +``` + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/close.rs). + + +```rust expandable +use light_compressed_token_sdk::ctoken::CloseAccountInfos; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::{ID, TOKEN_ACCOUNT_SEED}; + +/// Handler for closing a compressed token account (invoke) +/// +/// Account order: +/// - accounts[0]: token_program (ctoken program) +/// - accounts[1]: account to close (writable) +/// - accounts[2]: destination for lamports (writable) +/// - accounts[3]: owner/authority (signer) +/// - accounts[4]: rent_sponsor (optional, writable) +pub fn process_close_account_invoke(accounts: &[AccountInfo]) -> Result<(), ProgramError> { + if accounts.len() < 4 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let rent_sponsor = if accounts.len() > 4 { + Some(accounts[4].clone()) + } else { + None + }; + + CloseAccountInfos { + token_program: accounts[0].clone(), + account: accounts[1].clone(), + destination: accounts[2].clone(), + owner: accounts[3].clone(), + rent_sponsor, + } + .invoke()?; + + Ok(()) +} + +/// Handler for closing a PDA-owned compressed token account (invoke_signed) +/// +/// Account order: +/// - accounts[0]: token_program (ctoken program) +/// - accounts[1]: account to close (writable) +/// - accounts[2]: destination for lamports (writable) +/// - accounts[3]: PDA owner/authority (not signer, program signs) +/// - accounts[4]: rent_sponsor (optional, writable) +pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<(), ProgramError> { + if accounts.len() < 4 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the authority + let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); + + // Verify the authority account is the PDA + if &pda != accounts[3].key { + return Err(ProgramError::InvalidSeeds); + } + + let rent_sponsor = if accounts.len() > 4 { + Some(accounts[4].clone()) + } else { + None + }; + + let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; + CloseAccountInfos { + token_program: accounts[0].clone(), + account: accounts[1].clone(), + destination: accounts[2].clone(), + owner: accounts[3].clone(), + rent_sponsor, + } + .invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + +# Next Steps + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index d049b7bb..0619f85e 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -30,47 +30,25 @@ Find [full code examples at the end](#full-code-example). -### Prerequisites -#### Dependencies - -```toml -[dependencies] -light-compressed-token-sdk = "0.1.0" -solana-program = "2.2" -borsh = "0.10.0" -``` -#### Instruction Data - -```rust -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateAtaData { - pub bump: u8, - pub pre_pay_num_epochs: u8, - pub lamports_per_write: u32, -} -``` -* Define the Associated cToken Account struct to deserialize the instruction data correctly. -* You will configure `pre_pay_num_epochs` and `lamports_per_write` in the next step. - - - - ### Configure Rent & Associated cToken Account ```rust +use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; + let compressible_params = CompressibleParamsInfos::new( data.pre_pay_num_epochs, data.lamports_per_write, - accounts[5].clone(), // Compressible Config - accounts[6].clone(), // Rent Sponsor - accounts[4].clone(), // System Program + compressible_config.clone(), + rent_sponsor.clone(), + system_program.clone(), ); ``` 1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** * Customize N **based on expected account activity**. * The account stays decompressed while it has rent for N epochs. + ``` rust rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) @@ -115,14 +93,14 @@ The address is derived with `[owner, ctoken_program_id, mint]` and passed via `a ```rust -// Build the account infos struct -// Invoke with user signer +use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; + CreateAssociatedTokenAccount2Infos { - owner: accounts[0].clone(), - mint: accounts[1].clone(), - payer: accounts[2].clone(), - associated_token_account: accounts[3].clone(), - system_program: accounts[4].clone(), + owner: owner.clone(), + mint: mint.clone(), + payer: payer.clone(), + associated_token_account: associated_token_account.clone(), + system_program: system_program.clone(), bump: data.bump, compressible: Some(compressible_params), idempotent: false, @@ -134,15 +112,15 @@ CreateAssociatedTokenAccount2Infos { ```rust -// Build the account infos struct -// Invoke with PDA signer +use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; + let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; CreateAssociatedTokenAccount2Infos { - owner: accounts[0].clone(), - mint: accounts[1].clone(), - payer: accounts[2].clone(), // PDA - associated_token_account: accounts[3].clone(), - system_program: accounts[4].clone(), + owner: owner.clone(), + mint: mint.clone(), + payer: payer.clone(), + associated_token_account: associated_token_account.clone(), + system_program: system_program.clone(), bump: data.bump, compressible: Some(compressible_params), idempotent: false, diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 345a7d47..c75b0623 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -6,6 +6,7 @@ description: Program guide to create cToken accounts with step-by-step implement import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; +import { RentCalculator } from '/snippets/rent-calculator.jsx'; ## Key Points @@ -15,8 +16,7 @@ cToken accounts are Solana accounts and created via CPI to the cToken Program. 2. The rent-exemption is sponsored via the compressible extension. * The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min). * The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. - * The account will be compressed when inactive for a period of two epochs. - * When the account is written to, it’s automatically decompressed. + Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). @@ -30,65 +30,73 @@ Find [full code examples at the end](#full-code-example). -### Prerequisites -#### Dependencies - -```toml -[dependencies] -light-compressed-token-sdk = "0.1.0" -solana-program = "2.2" -borsh = "0.10.0" -``` - -#### Instruction Data - -```rust -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateTokenAccountData { - pub owner: Pubkey, - pub pre_pay_num_epochs: u8, - pub lamports_per_write: u32, -} -``` -* Define the cToken struct to deserialize the instruction data correctly. -* You will configure `pre_pay_num_epochs` and `lamports_per_write` in the next step. - - - +### Configure Rent -### Configure Rent & cToken Account +```rust +use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; -```rust let compressible_params = CompressibleParamsInfos::new( data.pre_pay_num_epochs, data.lamports_per_write, - accounts[3].clone(), // Compressible Config - accounts[5].clone(), // Rent Sponsor - accounts[4].clone(), // System Program + compressible_config.clone(), + rent_sponsor.clone(), + system_program.clone(), ); ``` -1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** - * Customize N **based on expected account activity**. - * The account stays decompressed while it has rent for N epochs. -``` rust -rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) - -// For 260-byte cToken account: -// 128 + (260 * 1) = 388 lamports per epoch -``` +#### Rent Config +1. `pre_pay_num_epochs` determines the number of epochs for which the account creator funds the account with rent. + * This is set to 2 epochs (12,600 slots / 84 min or 766 lamports) by default. +2. `lamports_per_write` determines the amount a the transaction payer top-ups the account with rent. + * This is set to 766 lamports by default. + * Top-ups are made when the lamports balance falls below two epochs of rent. +3. The account is compressed after it is inactive for 2 epochs. +4. When the account is written to, it’s automatically decompressed. + * The transaction payer tops up the account with 766 lamports. -2. **Set** in `lamports_per_write` the **top-up amount**. - * Top-ups are **made by** the **transaction fee payer** when the **lamports balance** falls **below two epochs of rent**. + +Learn the core concepts to the rent config [here](/compressed-token-program/rent-config). +You can customize the rent config based on expected account activity (must be 0 or ≥2). + - -* Accounts become compressible when they advance two epochs without new transactions that add lamports. -* A forester node compresses the account after these two epochs and the protocol can reclaim the rent. - + + +#### Accounts + + + + + + + + + + + + + + + + + + + + + + + + + + +
Compressible Config + Protocol PDA that stores rent config and compression authorities. +
Rent Sponsor + - CToken program PDA that fronts rent exemption at creation.
+ - Claims rent when account compresses. +
System ProgramSolana System Program. Required for CPI to create the on-chain account.
-3. Accounts (add)
@@ -113,11 +121,12 @@ Build the instruction and CPI the cToken Program. ```rust -// Build the account infos struct +use light_compressed_token_sdk::ctoken::CreateCTokenAccountInfos; + CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), + payer: payer.clone(), + account: account.clone(), + mint: mint.clone(), owner: data.owner, compressible: Some(compressible_params), } @@ -128,28 +137,18 @@ CreateCTokenAccountInfos { ```rust -// Build the compressible params -let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), - accounts[5].clone(), - accounts[4].clone(), -); +use light_compressed_token_sdk::ctoken::CreateCTokenAccountInfos; -// Build the account infos struct -// Invoke with PDA-signer let account_infos = CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), + payer: payer.clone(), + account: account.clone(), + mint: mint.clone(), owner: data.owner, compressible: Some(compressible_params), }; + let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; account_infos.invoke_signed(&[signer_seeds])?; - -Ok(()) ``` diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx index 357e2f9d..c2f7a43b 100644 --- a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -3,20 +3,14 @@ title: Mint cTokens description: Program guide to mint cTokens with step-by-step implementation and full code examples. --- -import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; -import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; -import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; +import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; ## Key Points -cTokens are minted via CPI to the cToken Program. +cTokens are minted via CPI to the Compressed Token Program and do not require rent-exemption. -1. cTokens hold token balances like SPL Token accounts. -2. The rent-exemption is sponsored via the compressible extension. - * The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min). - * The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. - * The account will be compressed when inactive for a period of two epochs. - * When the account is written to, it’s automatically decompressed. +1. Minting creates new token balances in existing cToken accounts. +2. The mint authority must sign the transaction (or be a PDA with invoke_signed). Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). @@ -26,4 +20,282 @@ Learn the [core concepts to the cToken Program here](/compressed-token-program/c Find [full code examples at the end](#full-code-example). -# Implementation Guide \ No newline at end of file +# Implementation Guide + + + + +### Configure Mint Parameters + +```rust +use light_compressed_token_sdk::ctoken::MintToCTokenParams; + +let params = MintToCTokenParams::new( + data.compressed_mint_inputs, + data.amount, + data.mint_authority, + data.proof, +); +``` + +1. `compressed_mint_inputs` - The compressed mint context with proof data +2. `amount` - Number of tokens to mint +3. `mint_authority` - The authority allowed to mint tokens +4. `proof` - Validity proof for the compressed state + + + + + +### System Accounts + +Include system accounts required to interact with compressed state. The client includes them in the instruction. + + + + + +```rust +use light_compressed_token_sdk::ctoken::SystemAccountInfos; + +let system_accounts = SystemAccountInfos { + light_system_program: light_system_program.clone(), + cpi_authority_pda: cpi_authority_pda.clone(), + registered_program_pda: registered_program_pda.clone(), + account_compression_authority: account_compression_authority.clone(), + account_compression_program: account_compression_program.clone(), + system_program: system_program.clone(), +}; +``` + + + + + +### CPI to Compressed Token Program + +Build the instruction and invoke the Compressed Token Program. + +* When a CPI doesn't require PDA signers, the `invoke` function is used. +* When a CPI requires a PDA signer, the `invoke_signed` function is used. + + + + +```rust +use light_compressed_token_sdk::ctoken::MintToCTokenInfos; + +MintToCTokenInfos { + authority: authority.clone(), + payer: payer.clone(), + state_tree: state_tree.clone(), + input_queue: input_queue.clone(), + output_queue: output_queue.clone(), + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, +} +.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::MintToCTokenInfos; + +let account_infos = MintToCTokenInfos { + authority: authority.clone(), + payer: payer.clone(), + state_tree: state_tree.clone(), + input_queue: input_queue.clone(), + output_queue: output_queue.clone(), + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, +}; + +let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; +account_infos.invoke_signed(&[signer_seeds])?; +``` + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/mint_to_ctoken.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::{ + MintToCTokenInfos, MintToCTokenParams, SystemAccountInfos, +}; +use light_ctoken_types::instructions::mint_action::CompressedMintWithContext; +use light_sdk::instruction::ValidityProof; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::ID; + +/// PDA seed for mint authority in invoke_signed variant +pub const MINT_AUTHORITY_SEED: &[u8] = b"mint_authority"; + +/// Instruction data for mint_to_ctoken operations +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct MintToCTokenData { + pub compressed_mint_inputs: CompressedMintWithContext, + pub amount: u64, + pub mint_authority: Pubkey, + pub proof: ValidityProof, +} + +/// Handler for minting tokens to compressed token accounts (invoke) +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: authority (mint_authority) +/// - accounts[3]: fee_payer +/// - accounts[4]: cpi_authority_pda +/// - accounts[5]: registered_program_pda +/// - accounts[6]: account_compression_authority +/// - accounts[7]: account_compression_program +/// - accounts[8]: system_program +/// - accounts[9]: output_queue +/// - accounts[10]: state_tree +/// - accounts[11]: input_queue +/// - accounts[12..]: ctoken_accounts (destination accounts) +pub fn process_mint_to_ctoken( + accounts: &[AccountInfo], + data: MintToCTokenData, +) -> Result<(), ProgramError> { + if accounts.len() < 13 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Build params using the constructor + let params = MintToCTokenParams::new( + data.compressed_mint_inputs, + data.amount, + data.mint_authority, + data.proof, + ); + + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[4].clone(), + registered_program_pda: accounts[5].clone(), + account_compression_authority: accounts[6].clone(), + account_compression_program: accounts[7].clone(), + system_program: accounts[8].clone(), + }; + + // Collect ctoken accounts from remaining accounts + let ctoken_accounts: Vec = accounts[12..].to_vec(); + + MintToCTokenInfos { + authority: accounts[2].clone(), + payer: accounts[3].clone(), + state_tree: accounts[10].clone(), + input_queue: accounts[11].clone(), + output_queue: accounts[9].clone(), + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + } + .invoke()?; + + Ok(()) +} + +/// Handler for minting tokens with PDA mint authority (invoke_signed) +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: authority (PDA mint_authority) +/// - accounts[3]: fee_payer +/// - accounts[4]: cpi_authority_pda +/// - accounts[5]: registered_program_pda +/// - accounts[6]: account_compression_authority +/// - accounts[7]: account_compression_program +/// - accounts[8]: system_program +/// - accounts[9]: output_queue +/// - accounts[10]: state_tree +/// - accounts[11]: input_queue +/// - accounts[12..]: ctoken_accounts (destination accounts) +pub fn process_mint_to_ctoken_invoke_signed( + accounts: &[AccountInfo], + data: MintToCTokenData, +) -> Result<(), ProgramError> { + if accounts.len() < 13 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the mint authority + let (pda, bump) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); + + // Verify the authority account is the PDA + if &pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); + } + + let params = MintToCTokenParams::new( + data.compressed_mint_inputs, + data.amount, + data.mint_authority, + data.proof, + ); + + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[4].clone(), + registered_program_pda: accounts[5].clone(), + account_compression_authority: accounts[6].clone(), + account_compression_program: accounts[7].clone(), + system_program: accounts[8].clone(), + }; + + let ctoken_accounts: Vec = accounts[12..].to_vec(); + + let account_infos = MintToCTokenInfos { + authority: accounts[2].clone(), + payer: accounts[3].clone(), + state_tree: accounts[10].clone(), + input_queue: accounts[11].clone(), + output_queue: accounts[9].clone(), + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + }; + + let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; + account_infos.invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + +# Next Steps + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-ctoken.mdx new file mode 100644 index 00000000..295d0368 --- /dev/null +++ b/compressed-token-program/ctoken/program-guides/program-transfer-ctoken.mdx @@ -0,0 +1,161 @@ +--- +title: Transfer cTokens +description: Program guide to transfer cTokens between accounts with step-by-step implementation and full code examples. +--- + +## Key Points + +cToken transfers are executed via CPI to the cToken Program. + +1. Transfer moves token balances between cToken accounts. +2. The source account owner must sign the transaction (or be a PDA with invoke_signed). +3. Simple interface: source, destination, amount, authority. + + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + + + +Find [full code examples at the end](#full-code-example). + + +# Implementation Guide + + + + +### cToken Program CPI + +Build the transfer instruction and invoke the cToken Program. + +* When a CPI doesn't require PDA signers, the `invoke` function is used. +* When a CPI requires a PDA signer, the `invoke_signed` function is used. + + + + +```rust +use light_compressed_token_sdk::ctoken::TransferCtokenAccountInfos; + +TransferCtokenAccountInfos { + source: source.clone(), + destination: destination.clone(), + amount: data.amount, + authority: authority.clone(), +} +.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::TransferCtokenAccountInfos; + +let transfer_accounts = TransferCtokenAccountInfos { + source: source.clone(), + destination: destination.clone(), + amount: data.amount, + authority: authority.clone(), +}; + +let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; +transfer_accounts.invoke_signed(&[signer_seeds])?; +``` + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/transfer.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::TransferCtokenAccountInfos; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::{ID, TOKEN_ACCOUNT_SEED}; + +/// Instruction data for transfer operations +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct TransferData { + pub amount: u64, +} + +/// Handler for transferring compressed tokens (invoke) +/// +/// Account order: +/// - accounts[0]: source ctoken account +/// - accounts[1]: destination ctoken account +/// - accounts[2]: authority (signer) +pub fn process_transfer_invoke( + accounts: &[AccountInfo], + data: TransferData, +) -> Result<(), ProgramError> { + if accounts.len() < 3 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + TransferCtokenAccountInfos { + source: accounts[0].clone(), + destination: accounts[1].clone(), + amount: data.amount, + authority: accounts[2].clone(), + } + .invoke()?; + + Ok(()) +} + +/// Handler for transferring compressed tokens from PDA-owned account (invoke_signed) +/// +/// Account order: +/// - accounts[0]: source ctoken account (PDA-owned) +/// - accounts[1]: destination ctoken account +/// - accounts[2]: authority (PDA) +pub fn process_transfer_invoke_signed( + accounts: &[AccountInfo], + data: TransferData, +) -> Result<(), ProgramError> { + if accounts.len() < 3 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the authority + let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); + + // Verify the authority account is the PDA + if &pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); + } + + let transfer_accounts = TransferCtokenAccountInfos { + source: accounts[0].clone(), + destination: accounts[1].clone(), + amount: data.amount, + authority: accounts[2].clone(), + }; + + // Invoke with PDA signing + let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; + transfer_accounts.invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + +# Next Steps + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx new file mode 100644 index 00000000..4dc68bd4 --- /dev/null +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -0,0 +1,233 @@ +--- +title: Unified Transfer Interface +description: Program guide for the unified transfer interface that auto-detects account types and routes transfers. +--- + +## Key Points + +The `TransferInterface` is a unified API that automatically detects account types and routes to the appropriate transfer: + +1. **cToken → cToken**: Direct compressed token transfer +2. **cToken → SPL**: Withdraw to SPL token account +3. **SPL → cToken**: Deposit from SPL token account + + +This is an advanced interface. For explicit transfers, see the individual guides: +- [cToken Transfer](/compressed-token-program/ctoken/program-guides/program-transfer-ctoken) +- [SPL ↔ cToken Transfer](/compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken) + + + +Find [full code examples at the end](#full-code-example). + + +# Implementation Guide + + + + +### Build Transfer Interface + +Create the transfer interface and optionally configure SPL bridge support. + + + + +```rust +use light_compressed_token_sdk::ctoken::TransferInterface; + +let mut transfer = TransferInterface::new( + data.amount, + source_account.clone(), + destination_account.clone(), + authority.clone(), + payer.clone(), + compressed_token_program_authority.clone(), +); + +// Add SPL bridge config if needed for SPL<->cToken transfers +transfer = transfer.with_spl_interface( + Some(mint.clone()), + Some(spl_token_program.clone()), + Some(token_pool_pda.clone()), + data.token_pool_pda_bump, +)?; + +transfer.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::TransferInterface; + +let mut transfer = TransferInterface::new( + data.amount, + source_account.clone(), + destination_account.clone(), + authority.clone(), + payer.clone(), + compressed_token_program_authority.clone(), +); + +// Add SPL bridge config if needed +transfer = transfer.with_spl_interface( + Some(mint.clone()), + Some(spl_token_program.clone()), + Some(token_pool_pda.clone()), + data.token_pool_pda_bump, +)?; + +let authority_seeds: &[&[u8]] = &[TRANSFER_INTERFACE_AUTHORITY_SEED, &[authority_bump]]; +transfer.invoke_signed(&[authority_seeds])?; +``` + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/transfer_interface.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::TransferInterface; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::ID; + +/// PDA seed for authority in invoke_signed variants +pub const TRANSFER_INTERFACE_AUTHORITY_SEED: &[u8] = b"transfer_interface_authority"; + +/// Instruction data for TransferInterface +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct TransferInterfaceData { + pub amount: u64, + /// Required for SPL<->CToken transfers, None for CToken->CToken + pub token_pool_pda_bump: Option, +} + +/// Handler for TransferInterface (invoke) +/// +/// This unified interface automatically detects account types and routes to: +/// - CToken -> CToken transfer +/// - CToken -> SPL transfer +/// - SPL -> CToken transfer +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: source_account (SPL or CToken) +/// - accounts[2]: destination_account (SPL or CToken) +/// - accounts[3]: authority (signer) +/// - accounts[4]: payer (signer) +/// - accounts[5]: compressed_token_program_authority +/// For SPL bridge (optional, required for SPL<->CToken): +/// - accounts[6]: mint +/// - accounts[7]: token_pool_pda +/// - accounts[8]: spl_token_program +pub fn process_transfer_interface_invoke( + accounts: &[AccountInfo], + data: TransferInterfaceData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let mut transfer = TransferInterface::new( + data.amount, + accounts[1].clone(), // source_account + accounts[2].clone(), // destination_account + accounts[3].clone(), // authority + accounts[4].clone(), // payer + accounts[5].clone(), // compressed_token_program_authority + ); + + // Add SPL bridge config if provided + if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { + transfer = transfer.with_spl_interface( + Some(accounts[6].clone()), // mint + Some(accounts[8].clone()), // spl_token_program + Some(accounts[7].clone()), // token_pool_pda + data.token_pool_pda_bump, + )?; + } + + transfer.invoke()?; + + Ok(()) +} + +/// Handler for TransferInterface with PDA authority (invoke_signed) +/// +/// The authority is a PDA derived from TRANSFER_INTERFACE_AUTHORITY_SEED. +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: source_account (SPL or CToken) +/// - accounts[2]: destination_account (SPL or CToken) +/// - accounts[3]: authority (PDA, not signer - program signs) +/// - accounts[4]: payer (signer) +/// - accounts[5]: compressed_token_program_authority +/// For SPL bridge (optional, required for SPL<->CToken): +/// - accounts[6]: mint +/// - accounts[7]: token_pool_pda +/// - accounts[8]: spl_token_program +pub fn process_transfer_interface_invoke_signed( + accounts: &[AccountInfo], + data: TransferInterfaceData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the authority + let (authority_pda, authority_bump) = + Pubkey::find_program_address(&[TRANSFER_INTERFACE_AUTHORITY_SEED], &ID); + + // Verify the authority account is the PDA + if &authority_pda != accounts[3].key { + return Err(ProgramError::InvalidSeeds); + } + + let mut transfer = TransferInterface::new( + data.amount, + accounts[1].clone(), // source_account + accounts[2].clone(), // destination_account + accounts[3].clone(), // authority (PDA) + accounts[4].clone(), // payer + accounts[5].clone(), // compressed_token_program_authority + ); + + // Add SPL bridge config if provided + if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { + transfer = transfer.with_spl_interface( + Some(accounts[6].clone()), // mint + Some(accounts[8].clone()), // spl_token_program + Some(accounts[7].clone()), // token_pool_pda + data.token_pool_pda_bump, + )?; + } + + // Invoke with PDA signing + let authority_seeds: &[&[u8]] = &[TRANSFER_INTERFACE_AUTHORITY_SEED, &[authority_bump]]; + transfer.invoke_signed(&[authority_seeds])?; + + Ok(()) +} +``` + +# Next Steps + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken.mdx new file mode 100644 index 00000000..ec8411ec --- /dev/null +++ b/compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken.mdx @@ -0,0 +1,343 @@ +--- +title: Transfer Between SPL and cTokens +description: Program guide to transfer tokens between SPL and cToken accounts with step-by-step implementation and full code examples. +--- + +## Key Points + +SPL ↔ cToken transfers are executed via CPI to the Compressed Token Program. + +1. **SPL → cToken**: Deposits SPL tokens into a cToken account. +2. **cToken → SPL**: Withdraws cTokens to an SPL token account. +3. Uses a token pool PDA to bridge between the two token types. + + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + + + +Find [full code examples at the end](#full-code-example). + + +# Implementation Guide + + + + +### SPL to cToken Transfer + +Transfer SPL tokens into a cToken account. + + + + +```rust +use light_compressed_token_sdk::ctoken::TransferSplToCtokenAccountInfos; + +TransferSplToCtokenAccountInfos { + source_spl_token_account: source_spl_token_account.clone(), + destination_ctoken_account: destination_ctoken_account.clone(), + amount: data.amount, + authority: authority.clone(), + mint: mint.clone(), + payer: payer.clone(), + token_pool_pda: token_pool_pda.clone(), + token_pool_pda_bump: data.token_pool_pda_bump, + spl_token_program: spl_token_program.clone(), + compressed_token_program_authority: compressed_token_program_authority.clone(), +} +.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::TransferSplToCtokenAccountInfos; + +let account_infos = TransferSplToCtokenAccountInfos { + source_spl_token_account: source_spl_token_account.clone(), + destination_ctoken_account: destination_ctoken_account.clone(), + amount: data.amount, + authority: authority.clone(), + mint: mint.clone(), + payer: payer.clone(), + token_pool_pda: token_pool_pda.clone(), + token_pool_pda_bump: data.token_pool_pda_bump, + spl_token_program: spl_token_program.clone(), + compressed_token_program_authority: compressed_token_program_authority.clone(), +}; + +let authority_seeds: &[&[u8]] = &[TRANSFER_AUTHORITY_SEED, &[authority_bump]]; +account_infos.invoke_signed(&[authority_seeds])?; +``` + + + + + + + + +### cToken to SPL Transfer + +Withdraw cTokens to an SPL token account. + + + + +```rust +use light_compressed_token_sdk::ctoken::TransferCtokenToSplAccountInfos; + +TransferCtokenToSplAccountInfos { + source_ctoken_account: source_ctoken_account.clone(), + destination_spl_token_account: destination_spl_token_account.clone(), + amount: data.amount, + authority: authority.clone(), + mint: mint.clone(), + payer: payer.clone(), + token_pool_pda: token_pool_pda.clone(), + token_pool_pda_bump: data.token_pool_pda_bump, + spl_token_program: spl_token_program.clone(), + compressed_token_program_authority: compressed_token_program_authority.clone(), +} +.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::TransferCtokenToSplAccountInfos; + +let account_infos = TransferCtokenToSplAccountInfos { + source_ctoken_account: source_ctoken_account.clone(), + destination_spl_token_account: destination_spl_token_account.clone(), + amount: data.amount, + authority: authority.clone(), + mint: mint.clone(), + payer: payer.clone(), + token_pool_pda: token_pool_pda.clone(), + token_pool_pda_bump: data.token_pool_pda_bump, + spl_token_program: spl_token_program.clone(), + compressed_token_program_authority: compressed_token_program_authority.clone(), +}; + +let authority_seeds: &[&[u8]] = &[TRANSFER_AUTHORITY_SEED, &[authority_bump]]; +account_infos.invoke_signed(&[authority_seeds])?; +``` + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/transfer_spl_ctoken.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::{ + TransferCtokenToSplAccountInfos, TransferSplToCtokenAccountInfos, +}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::ID; + +/// PDA seed for authority in invoke_signed variants +pub const TRANSFER_AUTHORITY_SEED: &[u8] = b"transfer_authority"; + +/// Instruction data for SPL to cToken transfer +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct TransferSplToCtokenData { + pub amount: u64, + pub token_pool_pda_bump: u8, +} + +/// Instruction data for cToken to SPL transfer +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct TransferCtokenToSplData { + pub amount: u64, + pub token_pool_pda_bump: u8, +} + +/// Handler for transferring SPL tokens to cToken (invoke) +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: source_spl_token_account +/// - accounts[2]: destination_ctoken_account (writable) +/// - accounts[3]: authority (signer) +/// - accounts[4]: mint +/// - accounts[5]: payer (signer) +/// - accounts[6]: token_pool_pda +/// - accounts[7]: spl_token_program +/// - accounts[8]: compressed_token_program_authority +pub fn process_spl_to_ctoken_invoke( + accounts: &[AccountInfo], + data: TransferSplToCtokenData, +) -> Result<(), ProgramError> { + if accounts.len() < 9 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + TransferSplToCtokenAccountInfos { + source_spl_token_account: accounts[1].clone(), + destination_ctoken_account: accounts[2].clone(), + amount: data.amount, + authority: accounts[3].clone(), + mint: accounts[4].clone(), + payer: accounts[5].clone(), + token_pool_pda: accounts[6].clone(), + token_pool_pda_bump: data.token_pool_pda_bump, + spl_token_program: accounts[7].clone(), + compressed_token_program_authority: accounts[8].clone(), + } + .invoke()?; + + Ok(()) +} + +/// Handler for transferring SPL tokens to cToken with PDA authority (invoke_signed) +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: source_spl_token_account +/// - accounts[2]: destination_ctoken_account (writable) +/// - accounts[3]: authority (PDA, not signer - program signs) +/// - accounts[4]: mint +/// - accounts[5]: payer (signer) +/// - accounts[6]: token_pool_pda +/// - accounts[7]: spl_token_program +/// - accounts[8]: compressed_token_program_authority +pub fn process_spl_to_ctoken_invoke_signed( + accounts: &[AccountInfo], + data: TransferSplToCtokenData, +) -> Result<(), ProgramError> { + if accounts.len() < 9 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let (authority_pda, authority_bump) = + Pubkey::find_program_address(&[TRANSFER_AUTHORITY_SEED], &ID); + + if &authority_pda != accounts[3].key { + return Err(ProgramError::InvalidSeeds); + } + + let account_infos = TransferSplToCtokenAccountInfos { + source_spl_token_account: accounts[1].clone(), + destination_ctoken_account: accounts[2].clone(), + amount: data.amount, + authority: accounts[3].clone(), + mint: accounts[4].clone(), + payer: accounts[5].clone(), + token_pool_pda: accounts[6].clone(), + token_pool_pda_bump: data.token_pool_pda_bump, + spl_token_program: accounts[7].clone(), + compressed_token_program_authority: accounts[8].clone(), + }; + + let authority_seeds: &[&[u8]] = &[TRANSFER_AUTHORITY_SEED, &[authority_bump]]; + account_infos.invoke_signed(&[authority_seeds])?; + + Ok(()) +} + +/// Handler for transferring cToken to SPL tokens (invoke) +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: source_ctoken_account +/// - accounts[2]: destination_spl_token_account +/// - accounts[3]: authority (signer) +/// - accounts[4]: mint +/// - accounts[5]: payer (signer) +/// - accounts[6]: token_pool_pda +/// - accounts[7]: spl_token_program +/// - accounts[8]: compressed_token_program_authority +pub fn process_ctoken_to_spl_invoke( + accounts: &[AccountInfo], + data: TransferCtokenToSplData, +) -> Result<(), ProgramError> { + if accounts.len() < 9 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + TransferCtokenToSplAccountInfos { + source_ctoken_account: accounts[1].clone(), + destination_spl_token_account: accounts[2].clone(), + amount: data.amount, + authority: accounts[3].clone(), + mint: accounts[4].clone(), + payer: accounts[5].clone(), + token_pool_pda: accounts[6].clone(), + token_pool_pda_bump: data.token_pool_pda_bump, + spl_token_program: accounts[7].clone(), + compressed_token_program_authority: accounts[8].clone(), + } + .invoke()?; + + Ok(()) +} + +/// Handler for transferring cToken to SPL tokens with PDA authority (invoke_signed) +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: source_ctoken_account +/// - accounts[2]: destination_spl_token_account +/// - accounts[3]: authority (PDA, not signer - program signs) +/// - accounts[4]: mint +/// - accounts[5]: payer (signer) +/// - accounts[6]: token_pool_pda +/// - accounts[7]: spl_token_program +/// - accounts[8]: compressed_token_program_authority +pub fn process_ctoken_to_spl_invoke_signed( + accounts: &[AccountInfo], + data: TransferCtokenToSplData, +) -> Result<(), ProgramError> { + if accounts.len() < 9 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let (authority_pda, authority_bump) = + Pubkey::find_program_address(&[TRANSFER_AUTHORITY_SEED], &ID); + + if &authority_pda != accounts[3].key { + return Err(ProgramError::InvalidSeeds); + } + + let account_infos = TransferCtokenToSplAccountInfos { + source_ctoken_account: accounts[1].clone(), + destination_spl_token_account: accounts[2].clone(), + amount: data.amount, + authority: accounts[3].clone(), + mint: accounts[4].clone(), + payer: accounts[5].clone(), + token_pool_pda: accounts[6].clone(), + token_pool_pda_bump: data.token_pool_pda_bump, + spl_token_program: accounts[7].clone(), + compressed_token_program_authority: accounts[8].clone(), + }; + + let authority_seeds: &[&[u8]] = &[TRANSFER_AUTHORITY_SEED, &[authority_bump]]; + account_infos.invoke_signed(&[authority_seeds])?; + + Ok(()) +} +``` + +# Next Steps + + \ No newline at end of file diff --git a/docs.json b/docs.json index e54a76a5..06568dcd 100644 --- a/docs.json +++ b/docs.json @@ -37,7 +37,8 @@ "learn/core-concepts/transaction-lifecycle", "learn/core-concepts/considerations" ] - } + }, + "rent" ] }, { diff --git a/rent.mdx b/rent.mdx new file mode 100644 index 00000000..df7c90dd --- /dev/null +++ b/rent.mdx @@ -0,0 +1,33 @@ +--- +title: Rent Calculator +description: Calculate rent for Solana vs Compressed Accounts. +--- + +import { SolanaRentCalculator } from '/snippets/solana-rent-calculator.jsx'; +import { RentCalculator } from '/snippets/rent-calculator.jsx'; + +## Solana Rent + +Standard Solana accounts require a rent-exempt balance equal to 2 years of rent, locked for the account's lifetime. + +```rust +minimum_balance = (ACCOUNT_STORAGE_OVERHEAD + data_len) * lamports_per_byte + +// Example 165-byte SPL token account: +// (128 + 165) * 6960 = 2,039,280 lamports +``` + + + +## Compressible Account Rent + +Compressible accounts only require rent for the prepaid epochs. Accounts auto-compress when inactive. + +```rust +rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) + +// For 260-byte cToken account: +// 128 + (260 * 1) = 388 lamports per epoch +``` + + diff --git a/snippets/ctoken-create-accounts-list.mdx b/snippets/ctoken-create-accounts-list.mdx index eae2b23a..7bf764b1 100644 --- a/snippets/ctoken-create-accounts-list.mdx +++ b/snippets/ctoken-create-accounts-list.mdx @@ -1,21 +1,17 @@ - - - - + - - - - - - - - - - - - - - - - - - - - -
ConstraintsDescription
0 Payer signer, mutable @@ -24,7 +20,6 @@
1 Token Account signer*, mutable @@ -33,33 +28,9 @@
2 Mint - The SPL or cMint token mint. Defines which token this account holds.
3Compressible Config- - Protocol PDA that stores rent config and compression authorities. -
4System Program-Solana System Program. Required for CPI to create the on-chain account.
5Rent Sponsormutable - - CToken program PDA that fronts rent exemption at creation.
- - Claims rent when account compresses. -
diff --git a/snippets/rent-calculator.jsx b/snippets/rent-calculator.jsx new file mode 100644 index 00000000..87df99cb --- /dev/null +++ b/snippets/rent-calculator.jsx @@ -0,0 +1,93 @@ +export const RentCalculator = () => { + const [numEpochs, setNumEpochs] = useState(2); + const [lamportsPerWrite, setLamportsPerWrite] = useState(766); + + const DATA_LEN = 260; + const BASE_RENT = 128; + const LAMPORTS_PER_BYTE_PER_EPOCH = 1; + + const rentPerEpoch = BASE_RENT + (DATA_LEN * LAMPORTS_PER_BYTE_PER_EPOCH); + const totalPrepaidRent = rentPerEpoch * numEpochs; + const MINUTES_PER_EPOCH = 42; + const totalMinutes = numEpochs * MINUTES_PER_EPOCH; + const timeDisplay = totalMinutes >= 60 + ? `${(totalMinutes / 60).toFixed(1)}h` + : `${totalMinutes} min`; + + return ( +
+
+ {/* Sliders */} +
+ + + +
+ + {/* Result Cards */} +
+
+
Rent per Epoch
+
+ {rentPerEpoch.toLocaleString()} +
+
lamports
+
+ +
+
Total Prepaid
+
+ {totalPrepaidRent.toLocaleString()} +
+
lamports
+
+ +
+
Top-up Amount
+
+ {lamportsPerWrite.toLocaleString()} +
+
lamports
+
≈ {(lamportsPerWrite / rentPerEpoch).toFixed(1)} epochs
+
+
+ + {/* Formula reference */} +
+ rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch)
+ + For 260-byte cToken account: + rent_per_epoch = {BASE_RENT} + ({DATA_LEN} × {LAMPORTS_PER_BYTE_PER_EPOCH}) = {rentPerEpoch} lamports +
+
+
+ ); +}; diff --git a/snippets/solana-rent-calculator.jsx b/snippets/solana-rent-calculator.jsx new file mode 100644 index 00000000..b6896c1f --- /dev/null +++ b/snippets/solana-rent-calculator.jsx @@ -0,0 +1,59 @@ +export const SolanaRentCalculator = () => { + const [dataLen, setDataLen] = useState(165); + + const ACCOUNT_STORAGE_OVERHEAD = 128; + const LAMPORTS_PER_BYTE = 6960; + + const minimumBalance = (ACCOUNT_STORAGE_OVERHEAD + dataLen) * LAMPORTS_PER_BYTE; + const solAmount = minimumBalance / 1_000_000_000; + + return ( +
+
+ {/* Slider */} +
+ +
+ + {/* Result Cards */} +
+
+
Rent-Exempt Balance
+
+ {minimumBalance.toLocaleString()} +
+
lamports
+
+ +
+
SOL Amount
+
+ {solAmount.toFixed(6)} +
+
SOL
+
+
+ + {/* Formula reference */} +
+ minimum_balance = (ACCOUNT_STORAGE_OVERHEAD + data_len) × lamports_per_byte
+ minimum_balance = ({ACCOUNT_STORAGE_OVERHEAD} + {dataLen}) × {LAMPORTS_PER_BYTE.toLocaleString()} = {minimumBalance.toLocaleString()} lamports +
+
+
+ ); +}; From dc1661fac13a907d666c09b050aa490c38be70b5 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sat, 29 Nov 2025 19:15:12 +0000 Subject: [PATCH 012/143] Refactor rent calculators and update program-create-ctoken guide - Rename rent-calculator.jsx to compressible-rent-calculator.jsx - Add break-even-calculator.jsx: calculate break-even point for cToken vs SPL - Add cost-comparison-calculator.jsx: compare costs between account types - Update program-create-ctoken.mdx: use CompressibleRentCalculator component - Update rent.mdx: integrate new calculator components - Refine solana-rent-calculator.jsx: improve UI and calculations --- .../program-guides/program-create-ctoken.mdx | 4 +- rent.mdx | 19 +- snippets/break-even-calculator.jsx | 284 ++++++++++++++++++ ...r.jsx => compressible-rent-calculator.jsx} | 60 ++-- snippets/cost-comparison-calculator.jsx | 158 ++++++++++ snippets/solana-rent-calculator.jsx | 69 ++++- 6 files changed, 560 insertions(+), 34 deletions(-) create mode 100644 snippets/break-even-calculator.jsx rename snippets/{rent-calculator.jsx => compressible-rent-calculator.jsx} (53%) create mode 100644 snippets/cost-comparison-calculator.jsx diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index c75b0623..7a5b271f 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -6,7 +6,7 @@ description: Program guide to create cToken accounts with step-by-step implement import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; -import { RentCalculator } from '/snippets/rent-calculator.jsx'; +import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; ## Key Points @@ -61,7 +61,7 @@ Learn the core concepts to the rent config [here](/compressed-token-program/rent You can customize the rent config based on expected account activity (must be 0 or ≥2).
- + #### Accounts diff --git a/rent.mdx b/rent.mdx index df7c90dd..1f12c947 100644 --- a/rent.mdx +++ b/rent.mdx @@ -4,12 +4,15 @@ description: Calculate rent for Solana vs Compressed Accounts. --- import { SolanaRentCalculator } from '/snippets/solana-rent-calculator.jsx'; -import { RentCalculator } from '/snippets/rent-calculator.jsx'; +import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; +import { CostComparisonCalculator } from '/snippets/cost-comparison-calculator.jsx'; +import { BreakEvenCalculator } from '/snippets/break-even-calculator.jsx'; ## Solana Rent Standard Solana accounts require a rent-exempt balance equal to 2 years of rent, locked for the account's lifetime. + ```rust minimum_balance = (ACCOUNT_STORAGE_OVERHEAD + data_len) * lamports_per_byte @@ -17,12 +20,12 @@ minimum_balance = (ACCOUNT_STORAGE_OVERHEAD + data_len) * lamports_per_byte // (128 + 165) * 6960 = 2,039,280 lamports ``` - - ## Compressible Account Rent Compressible accounts only require rent for the prepaid epochs. Accounts auto-compress when inactive. + + ```rust rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) @@ -30,4 +33,12 @@ rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) // 128 + (260 * 1) = 388 lamports per epoch ``` - +## Cost Comparison + + + +## Break-Even Analysis + +Find the break-even point where compressed accounts become more expensive than Solana accounts based on expected write frequency. + + diff --git a/snippets/break-even-calculator.jsx b/snippets/break-even-calculator.jsx new file mode 100644 index 00000000..91a7112f --- /dev/null +++ b/snippets/break-even-calculator.jsx @@ -0,0 +1,284 @@ +export const BreakEvenCalculator = () => { + const [dataLen, setDataLen] = useState(100); + const [numAccounts, setNumAccounts] = useState(1000); + const [numWrites, setNumWrites] = useState(10); + const [priorityFeeRate, setPriorityFeeRate] = useState(1000); + const [showPrioritySlider, setShowPrioritySlider] = useState(false); + + const ACCOUNT_STORAGE_OVERHEAD = 128; + const LAMPORTS_PER_BYTE = 6960; + const BASE_COST_PER_WRITE = 10300; + const COMPRESSED_CU_PER_WRITE = 300000; + const LAMPORTS_PER_SOL = 1_000_000_000; + + const priorityFeePerWrite = Math.floor((COMPRESSED_CU_PER_WRITE * priorityFeeRate) / 1_000_000); + const costPerWrite = BASE_COST_PER_WRITE + priorityFeePerWrite; + + const solanaCostPerAccount = (ACCOUNT_STORAGE_OVERHEAD + dataLen) * LAMPORTS_PER_BYTE; + const solanaCost = numAccounts * solanaCostPerAccount; + const compressedCostPerAccount = costPerWrite * (1 + numWrites); + const compressedCost = numAccounts * compressedCostPerAccount; + const breakEvenWrites = Math.floor((solanaCostPerAccount / costPerWrite) - 1); + const useCompressed = numWrites < breakEvenWrites; + + const handleDataLenChange = (value) => { + const num = Math.max(0, Math.min(10000, Number.parseInt(value) || 0)); + setDataLen(num); + }; + + const handleAccountsChange = (value) => { + const num = Math.max(1, Math.min(1000000, Number.parseInt(value) || 1)); + setNumAccounts(num); + }; + + const handleWritesChange = (value) => { + const num = Math.max(0, Math.min(10000, Number.parseInt(value) || 0)); + setNumWrites(num); + }; + + const handlePriorityFeeChange = (value) => { + const num = Math.max(0, Math.min(100000, Number.parseInt(value) || 0)); + setPriorityFeeRate(num); + }; + + const formatSOL = (lamports) => { + const sol = lamports / LAMPORTS_PER_SOL; + if (sol >= 1000) { + return sol.toLocaleString(undefined, { maximumFractionDigits: 2 }); + } else if (sol >= 1) { + return sol.toFixed(4); + } + return sol.toFixed(6); + }; + + return ( +
+
+ {/* Inputs */} +
+
+ + Account Size + + handleDataLenChange(e.target.value)} + className="w-20 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + /> + bytes + + + setDataLen(Number.parseInt(e.target.value))} + className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" + /> +
+ + {/* Preset buttons */} +
+ + + +
+ +
+ + Number of Accounts + handleAccountsChange(e.target.value)} + className="w-28 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + /> + + setNumAccounts(Number.parseInt(e.target.value))} + className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" + /> +
+ +
+ + Expected Writes (per account) + handleWritesChange(e.target.value)} + className="w-20 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + /> + + setNumWrites(Number.parseInt(e.target.value))} + className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" + /> +
+ +
+ + Priority Fee + + +{priorityFeePerWrite.toLocaleString()} lamports/write + + +
+ + + + +
+ {showPrioritySlider && ( +
+
+ setPriorityFeeRate(Number.parseInt(e.target.value))} + className="flex-1 h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" + /> + + handlePriorityFeeChange(e.target.value)} + className="w-20 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + /> + μL/CU + +
+ + {COMPRESSED_CU_PER_WRITE.toLocaleString()} CU × {priorityFeeRate.toLocaleString()} μL/CU + +
+ )} +
+
+ + {/* Results */} +
Results
+
+
+
Solana Cost
+
+ {formatSOL(solanaCost)} +
+
SOL
+
+ +
+
Compressed Cost
+
+ {formatSOL(compressedCost)} +
+
SOL
+
+ +
+
Break-even
+
+ {breakEvenWrites.toLocaleString()} +
+
writes
+
+ +
+
Recommendation
+
+ {useCompressed ? 'Use Compressed' : 'Use Solana'} +
+
+
+ + {/* Formula reference */} +
+ Solana: ({ACCOUNT_STORAGE_OVERHEAD} + {dataLen}) × {LAMPORTS_PER_BYTE.toLocaleString()} = {solanaCostPerAccount.toLocaleString()} lamports/account
+ Compressed: ({BASE_COST_PER_WRITE.toLocaleString()} + {priorityFeePerWrite.toLocaleString()}) × (1 + {numWrites}) = {compressedCostPerAccount.toLocaleString()} lamports/account
+ Cost per write: {BASE_COST_PER_WRITE.toLocaleString()} base + {priorityFeePerWrite.toLocaleString()} priority ({COMPRESSED_CU_PER_WRITE.toLocaleString()} CU) = {costPerWrite.toLocaleString()} lamports +
+
+
+ ); +}; diff --git a/snippets/rent-calculator.jsx b/snippets/compressible-rent-calculator.jsx similarity index 53% rename from snippets/rent-calculator.jsx rename to snippets/compressible-rent-calculator.jsx index 87df99cb..a945338c 100644 --- a/snippets/rent-calculator.jsx +++ b/snippets/compressible-rent-calculator.jsx @@ -1,4 +1,4 @@ -export const RentCalculator = () => { +export const CompressibleRentCalculator = () => { const [numEpochs, setNumEpochs] = useState(2); const [lamportsPerWrite, setLamportsPerWrite] = useState(766); @@ -14,47 +14,71 @@ export const RentCalculator = () => { ? `${(totalMinutes / 60).toFixed(1)}h` : `${totalMinutes} min`; + const handleEpochsChange = (value) => { + const num = Math.max(2, Math.min(1000, Number.parseInt(value) || 2)); + setNumEpochs(num); + }; + + const handleLamportsChange = (value) => { + const num = Math.max(0, Math.min(100000, Number.parseInt(value) || 0)); + setLamportsPerWrite(num); + }; + return ( -
+
{/* Sliders */}
-
{/* Result Cards */}
-
+
Rent per Epoch
{rentPerEpoch.toLocaleString()} @@ -62,7 +86,7 @@ export const RentCalculator = () => {
lamports
-
+
Total Prepaid
{totalPrepaidRent.toLocaleString()} @@ -70,7 +94,7 @@ export const RentCalculator = () => {
lamports
-
+
Top-up Amount
{lamportsPerWrite.toLocaleString()} @@ -81,7 +105,7 @@ export const RentCalculator = () => {
{/* Formula reference */} -
+
rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch)
For 260-byte cToken account: diff --git a/snippets/cost-comparison-calculator.jsx b/snippets/cost-comparison-calculator.jsx new file mode 100644 index 00000000..688dd99f --- /dev/null +++ b/snippets/cost-comparison-calculator.jsx @@ -0,0 +1,158 @@ +export const CostComparisonCalculator = () => { + const [numAccounts, setNumAccounts] = useState(10000); + const [dataLen, setDataLen] = useState(165); + + const ACCOUNT_STORAGE_OVERHEAD = 128; + const LAMPORTS_PER_BYTE = 6960; + const COMPRESSED_COST_PER_ACCOUNT = 10300; + const LAMPORTS_PER_SOL = 1_000_000_000; + + const solanaCost = numAccounts * (ACCOUNT_STORAGE_OVERHEAD + dataLen) * LAMPORTS_PER_BYTE; + const compressedCost = numAccounts * COMPRESSED_COST_PER_ACCOUNT; + const savings = solanaCost - compressedCost; + const savingsPercent = ((savings / solanaCost) * 100).toFixed(1); + + const handleAccountsChange = (value) => { + const num = Math.max(1, Math.min(1000000, Number.parseInt(value) || 1)); + setNumAccounts(num); + }; + + const handleDataLenChange = (value) => { + const num = Math.max(0, Math.min(10000, Number.parseInt(value) || 0)); + setDataLen(num); + }; + + const formatSOL = (lamports) => { + const sol = lamports / LAMPORTS_PER_SOL; + if (sol >= 1000) { + return sol.toLocaleString(undefined, { maximumFractionDigits: 2 }); + } else if (sol >= 1) { + return sol.toFixed(4); + } + return sol.toFixed(6); + }; + + return ( +
+
+ {/* Inputs */} +
+
+ + Number of Accounts + handleAccountsChange(e.target.value)} + className="w-28 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + /> + + setNumAccounts(Number.parseInt(e.target.value))} + className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" + /> +
+ +
+ + Data Length per Account + + handleDataLenChange(e.target.value)} + className="w-20 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + /> + bytes + + + setDataLen(Number.parseInt(e.target.value))} + className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" + /> +
+ + {/* Preset buttons */} +
+ + + +
+
+ + {/* Result Cards */} +
+
+
Solana Cost
+
+ {formatSOL(solanaCost)} +
+
SOL
+
+ +
+
Compressed Cost
+
+ {formatSOL(compressedCost)} +
+
SOL
+
+ +
+
Savings
+
+ {savingsPercent}% +
+
{formatSOL(savings)} SOL
+
+
+ + {/* Formula reference */} +
+ Solana: {numAccounts.toLocaleString()} × ({ACCOUNT_STORAGE_OVERHEAD} + {dataLen}) × {LAMPORTS_PER_BYTE.toLocaleString()} = {solanaCost.toLocaleString()} lamports
+ Compressed: {numAccounts.toLocaleString()} × {COMPRESSED_COST_PER_ACCOUNT.toLocaleString()} = {compressedCost.toLocaleString()} lamports +
+
+
+ ); +}; diff --git a/snippets/solana-rent-calculator.jsx b/snippets/solana-rent-calculator.jsx index b6896c1f..5afaa868 100644 --- a/snippets/solana-rent-calculator.jsx +++ b/snippets/solana-rent-calculator.jsx @@ -7,31 +7,80 @@ export const SolanaRentCalculator = () => { const minimumBalance = (ACCOUNT_STORAGE_OVERHEAD + dataLen) * LAMPORTS_PER_BYTE; const solAmount = minimumBalance / 1_000_000_000; + const handleDataLenChange = (value) => { + const num = Math.max(0, Math.min(10000, Number.parseInt(value) || 0)); + setDataLen(num); + }; + return ( -
+
{/* Slider */}
-
{/* Result Cards */}
-
+
Rent-Exempt Balance
{minimumBalance.toLocaleString()} @@ -39,7 +88,7 @@ export const SolanaRentCalculator = () => {
lamports
-
+
SOL Amount
{solAmount.toFixed(6)} @@ -49,7 +98,7 @@ export const SolanaRentCalculator = () => {
{/* Formula reference */} -
+
minimum_balance = (ACCOUNT_STORAGE_OVERHEAD + data_len) × lamports_per_byte
minimum_balance = ({ACCOUNT_STORAGE_OVERHEAD} + {dataLen}) × {LAMPORTS_PER_BYTE.toLocaleString()} = {minimumBalance.toLocaleString()} lamports
From 9b60839c20ff11912960930e219de2c8e1c64ed7 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 00:08:54 +0000 Subject: [PATCH 013/143] Refine program-create-ctoken guide and rent calculator UX - Clarify compressible account lifecycle in key points - Fix grammar: 'a the' to 'a transaction payer' - Improve rent config explanation precision - Reorder calculator before Info block for better flow - Update CompressibleRentCalculator: hours input instead of epochs - Add preset buttons (2h, 4h, 6h, 12h, 24h) for common durations - Display hours alongside epochs for better UX --- .../program-guides/program-create-ctoken.mdx | 21 +++--- snippets/compressible-rent-calculator.jsx | 71 ++++++++++++------- 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 7a5b271f..8779372b 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -13,10 +13,10 @@ import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculat cToken accounts are Solana accounts and created via CPI to the cToken Program. 1. cToken accounts hold token balances like SPL Token accounts. -2. The rent-exemption is sponsored via the compressible extension. - * The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min). - * The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. - +2. Top-up the account with rent for two epochs (12,600 slots / 84 min) to create the account, instead of funding the rent-exemption. +3. The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. +4. When no transactions top up the lamports balance for two epochs, the account gets compressed. +5. When the account is written to, it’s automatically decompressed and the transaction payer tops-up the lamports balance. Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). @@ -49,22 +49,21 @@ let compressible_params = CompressibleParamsInfos::new( #### Rent Config 1. `pre_pay_num_epochs` determines the number of epochs for which the account creator funds the account with rent. * This is set to 2 epochs (12,600 slots / 84 min or 766 lamports) by default. -2. `lamports_per_write` determines the amount a the transaction payer top-ups the account with rent. +2. `lamports_per_write` determines the amount a transaction payer top-ups the account with rent. * This is set to 766 lamports by default. * Top-ups are made when the lamports balance falls below two epochs of rent. -3. The account is compressed after it is inactive for 2 epochs. -4. When the account is written to, it’s automatically decompressed. - * The transaction payer tops up the account with 766 lamports. +3. The account is compressed when the lamports balance is at zero epochs. +4. When the account is written to, the account gets decompressed and the transaction payer tops up the accounts with `lamports_per_write` again. +You can calculate the parameters below based on the expected account activity. + Learn the core concepts to the rent config [here](/compressed-token-program/rent-config). You can customize the rent config based on expected account activity (must be 0 or ≥2). - - - #### Accounts +The rent config requires the following accounts: diff --git a/snippets/compressible-rent-calculator.jsx b/snippets/compressible-rent-calculator.jsx index a945338c..a4c0bbb3 100644 --- a/snippets/compressible-rent-calculator.jsx +++ b/snippets/compressible-rent-calculator.jsx @@ -1,22 +1,19 @@ export const CompressibleRentCalculator = () => { - const [numEpochs, setNumEpochs] = useState(2); + const [hours, setHours] = useState(2); const [lamportsPerWrite, setLamportsPerWrite] = useState(766); const DATA_LEN = 260; const BASE_RENT = 128; const LAMPORTS_PER_BYTE_PER_EPOCH = 1; + const MINUTES_PER_EPOCH = 42; + const numEpochs = Math.ceil((hours * 60) / MINUTES_PER_EPOCH); const rentPerEpoch = BASE_RENT + (DATA_LEN * LAMPORTS_PER_BYTE_PER_EPOCH); const totalPrepaidRent = rentPerEpoch * numEpochs; - const MINUTES_PER_EPOCH = 42; - const totalMinutes = numEpochs * MINUTES_PER_EPOCH; - const timeDisplay = totalMinutes >= 60 - ? `${(totalMinutes / 60).toFixed(1)}h` - : `${totalMinutes} min`; - const handleEpochsChange = (value) => { - const num = Math.max(2, Math.min(1000, Number.parseInt(value) || 2)); - setNumEpochs(num); + const handleHoursChange = (value) => { + const num = Math.max(1, Math.min(168, Number.parseInt(value) || 2)); + setHours(num); }; const handleLamportsChange = (value) => { @@ -31,25 +28,45 @@ export const CompressibleRentCalculator = () => {
- Prepaid Epochs - handleEpochsChange(e.target.value)} - className="w-20 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" - /> + Prepaid Epochs in Hours + + handleHoursChange(e.target.value)} + className="w-16 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + /> + hours + setNumEpochs(Number.parseInt(e.target.value))} + min="1" + max="48" + value={Math.min(hours, 48)} + onChange={(e) => setHours(Number.parseInt(e.target.value))} className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" /> - ≈ {timeDisplay} + ≈ {((hours * 60) / MINUTES_PER_EPOCH).toFixed(1)} epochs + + {/* Preset buttons */} +
+ {[2, 4, 6, 12, 24].map((h) => ( + + ))} +
@@ -92,6 +109,7 @@ export const CompressibleRentCalculator = () => { {totalPrepaidRent.toLocaleString()}
lamports
+
≈ {((hours * 60) / MINUTES_PER_EPOCH).toFixed(1)} epochs / {hours}h
@@ -100,15 +118,14 @@ export const CompressibleRentCalculator = () => { {lamportsPerWrite.toLocaleString()}
lamports
-
≈ {(lamportsPerWrite / rentPerEpoch).toFixed(1)} epochs
+
≈ {(lamportsPerWrite / rentPerEpoch).toFixed(1)} epochs / {((lamportsPerWrite / rentPerEpoch) * MINUTES_PER_EPOCH / 60).toFixed(1)}h
{/* Formula reference */}
- rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch)
- - For 260-byte cToken account: + rent_per_epoch = base_rent + (data_len × lamports_per_byte_per_epoch)

+ For 260-byte cToken account:
rent_per_epoch = {BASE_RENT} + ({DATA_LEN} × {LAMPORTS_PER_BYTE_PER_EPOCH}) = {rentPerEpoch} lamports
From 61c8f1ee52d4c2a3bc810b19e36a501c91019341 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 00:09:33 +0000 Subject: [PATCH 014/143] Improve CPI section structure and account list clarity - Add intro text for CompressibleParamsInfos configuration - Restructure CPI section: simplify steps, move account list into tab - Integrate invoke vs invoke_signed explanation into numbered steps - Rename 'Token Account' to 'cToken Account' for precision - Add Owner account to cToken create accounts list - Simplify Mint account description --- .../program-guides/program-create-ctoken.mdx | 23 ++++++++----------- snippets/ctoken-create-accounts-list.mdx | 9 ++++++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 8779372b..6fdce348 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -33,6 +33,8 @@ Find [full code examples at the end](#full-code-example). ### Configure Rent +Use `CompressibleParamsInfos`to configure rent: + ```rust use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; @@ -100,21 +102,13 @@ The rent config requires the following accounts: -### cToken Program CPI - -Build the instruction and CPI the cToken Program. - -1. Pass the accounts to the cToken Program -2. CPI to cToken Program - - +### Build Account Infos and CPI the cToken Program - - - - -* When a CPI doesn't require PDA signers, the `invoke` function is used. -* When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. +1. Pass the required accounts +2. Include rent config from `compressible_params` +3. Use `invoke` or `invoke_signed`: + * When a CPI doesn't require PDA signers, the `invoke` function is used. + * When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. @@ -131,6 +125,7 @@ CreateCTokenAccountInfos { } .invoke()?; ``` + diff --git a/snippets/ctoken-create-accounts-list.mdx b/snippets/ctoken-create-accounts-list.mdx index 7bf764b1..701ade8b 100644 --- a/snippets/ctoken-create-accounts-list.mdx +++ b/snippets/ctoken-create-accounts-list.mdx @@ -20,7 +20,7 @@ - + - + + + + + +
Token AccountcToken Account signer*, mutable - The cToken account being created.
@@ -30,7 +30,12 @@
Mint -The SPL or cMint token mint. Defines which token this account holds.The SPL or cMint token mint.
OwnerPubkeyThe owner of the token account. Controls transfers and other operations.
From 0f11fe9e9c9254657dc7c76bf85fe9d886667b0f Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 00:59:50 +0000 Subject: [PATCH 015/143] Extract rent config to reusable snippet and refine program guides - Create ctoken-configure-rent.mdx snippet for DRY documentation - Update program-create-ctoken.mdx: use rent config snippet - Update program-create-cATA.mdx: use rent config snippet, align structure - Refine ctoken-create-ata-accounts-list.mdx: add Bump and Idempotent - Update docs.json: add SEO metatags and custom 404 --- .../program-guides/program-create-cATA.mdx | 72 +++++-------------- .../program-guides/program-create-ctoken.mdx | 69 +----------------- docs.json | 13 ++++ snippets/ctoken-configure-rent.mdx | 65 +++++++++++++++++ snippets/ctoken-create-ata-accounts-list.mdx | 40 ++++------- 5 files changed, 113 insertions(+), 146 deletions(-) create mode 100644 snippets/ctoken-configure-rent.mdx diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index 0619f85e..f27626cd 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -6,17 +6,18 @@ description: Program guide to create associated cToken accounts with step-by-ste import CTokenCreateATAAccountsList from '/snippets/ctoken-create-ata-accounts-list.mdx'; import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; +import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; +import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; ## Key Points Associated cToken accounts (cATA) are Solana accounts and created via CPI to the cToken Program. 1. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -2. The rent-exemption is sponsored via the compressible extension. - * The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min). - * The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. - * The account will be compressed when inactive for a period of two epochs. - * When the account is written to, it’s automatically decompressed. +2. Top-up the account with rent for two epochs (12,600 slots / 84 min) to create the account, instead of funding the full rent-exemption. +3. The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. +4. When no transactions top up the lamports balance for two epochs, the account gets compressed. +5. When the account is written to, it’s automatically decompressed and the transaction payer tops-up the lamports balance. Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). @@ -31,66 +32,28 @@ Find [full code examples at the end](#full-code-example). -### Configure Rent & Associated cToken Account +### Configure Rent -```rust -use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; - -let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - compressible_config.clone(), - rent_sponsor.clone(), - system_program.clone(), -); -``` - -1. Set `pre_pay_num_epochs` to **N epochs** to **top-up the account with rent (must be 0 or ≥2)** - * Customize N **based on expected account activity**. - * The account stays decompressed while it has rent for N epochs. - -``` rust -rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) - -// For 260-byte cToken account: -// 128 + (260 * 1) = 388 lamports per epoch -``` - -2. **Set** in `lamports_per_write` the **top-up amount**. - * Top-ups are **made by** the **transaction fee payer** when the **lamports balance** falls **below two epochs of rent**. - - -* Accounts become compressible when they advance two epochs without new transactions that add lamports. -* A forester node compresses the account after these two epochs and the protocol can reclaim the rent. - - -3. Accounts (add) + -### CPI to cToken Program +### Build Account Infos and CPI the cToken Program -Build the instruction and CPI the cToken Program. +1. Pass the required accounts +2. Include rent config from `compressible_params` +3. Use `invoke` or `invoke_signed`: + * When a CPI doesn't require PDA signers, the `invoke` function is used. + * When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. -1. Pass the accounts to the cToken Program -2. CPI to cToken Program - - - - - -**Owner and mint are passed in accounts** (`accounts[0]` and `accounts[1]`), not in the instruction data. - -The address is derived with `[owner, ctoken_program_id, mint]` and passed via `accounts[3]`. +The cATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike cToken accounts, owner and mint are passed as accounts, not in instruction data. -* When a CPI doesn't require PDA signers, the `invoke` function is used. -* When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. - + ```rust use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; @@ -107,9 +70,10 @@ CreateAssociatedTokenAccount2Infos { } .invoke()?; ``` + - + ```rust use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 6fdce348..884acbc0 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -7,13 +7,14 @@ import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx' import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; +import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; ## Key Points cToken accounts are Solana accounts and created via CPI to the cToken Program. 1. cToken accounts hold token balances like SPL Token accounts. -2. Top-up the account with rent for two epochs (12,600 slots / 84 min) to create the account, instead of funding the rent-exemption. +2. Top-up the account with rent for two epochs (12,600 slots / 84 min) to create the account, instead of funding the full rent-exemption. 3. The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. 4. When no transactions top up the lamports balance for two epochs, the account gets compressed. 5. When the account is written to, it’s automatically decompressed and the transaction payer tops-up the lamports balance. @@ -33,71 +34,7 @@ Find [full code examples at the end](#full-code-example). ### Configure Rent -Use `CompressibleParamsInfos`to configure rent: - -```rust -use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; - -let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - compressible_config.clone(), - rent_sponsor.clone(), - system_program.clone(), -); -``` - - -#### Rent Config -1. `pre_pay_num_epochs` determines the number of epochs for which the account creator funds the account with rent. - * This is set to 2 epochs (12,600 slots / 84 min or 766 lamports) by default. -2. `lamports_per_write` determines the amount a transaction payer top-ups the account with rent. - * This is set to 766 lamports by default. - * Top-ups are made when the lamports balance falls below two epochs of rent. -3. The account is compressed when the lamports balance is at zero epochs. -4. When the account is written to, the account gets decompressed and the transaction payer tops up the accounts with `lamports_per_write` again. - -You can calculate the parameters below based on the expected account activity. - - -Learn the core concepts to the rent config [here](/compressed-token-program/rent-config). -You can customize the rent config based on expected account activity (must be 0 or ≥2). - -#### Accounts - -The rent config requires the following accounts: - - - - - - - - - - - - - - - - - - - - - - - - - -
Compressible Config - Protocol PDA that stores rent config and compression authorities. -
Rent Sponsor - - CToken program PDA that fronts rent exemption at creation.
- - Claims rent when account compresses. -
System ProgramSolana System Program. Required for CPI to create the on-chain account.
- +
diff --git a/docs.json b/docs.json index 06568dcd..571364b2 100644 --- a/docs.json +++ b/docs.json @@ -12,6 +12,19 @@ "default": "light" }, "customCSS": "/custom.css", + "seo": { + "metatags": { + "canonical": "https://www.zkcompression.com", + "keywords": "solana, compression, zk compression, solana rent, solana rent free, account rent, compressed tokens, compressed accounts, solana pda, program derived address, airdrop, cheap airdrop, solana aidrop, SPL tokens, token distribution, stablecoin payments, solana pay, solana mobile, state compression, zero-knowledge proofs, zk proofs, validity proofs, merkle trees, rent-free accounts, solana scalability, L1 scalability, Light Protocol, Helius, Solana Foundation, solana SDK, solana development, web3 infrastructure, blockchain scalability, cheap token accounts, token account compression, scale Solana, Solana problems, Solana token accounts, compressed accounts, compressed NFTs, state compression, Metaplex, Solana Superteam, DePin, getMinimumBalanceForRentExemption, fetchSysvarRent(rpc, config?), getAccountInfo" + } + }, + "errors": { + "404": { + "redirect": false, + "title": "Oops ... rent not found", + "description": "Use our AI assistant to find what you are looking for!" + } + }, "navigation": { "tabs": [ { diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx new file mode 100644 index 00000000..d1ee25ef --- /dev/null +++ b/snippets/ctoken-configure-rent.mdx @@ -0,0 +1,65 @@ +Use `CompressibleParamsInfos` to configure rent: + +```rust +use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; + +let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + compressible_config.clone(), + rent_sponsor.clone(), + system_program.clone(), +); +``` + +#### Rent Config + +1. At creation set `pre_pay_num_epochs` to determine the number of epochs to fund the account with rent. + * This is set to 2 epochs (12,600 slots / 84 min or 766 lamports) by default. +2. Set `lamports_per_write` to determine the amount a transaction payer top-ups the account with rent. + * This is set to 766 lamports by default. + * Top-ups are made when the lamports balance falls below two epochs of rent. +3. The account is compressed when the lamports balance is at zero epochs. +4. When the account is written to, the account gets decompressed and the transaction payer tops up the accounts with `lamports_per_write` again. + +You can calculate the parameters below based on the expected account activity. + + +Learn the core concepts to the rent config [here](/compressed-token-program/rent-config). +You can customize the rent config based on expected account activity (must be 0 or ≥2). + + +#### Accounts + +The rent config requires the following accounts: + + + + + + + + + + + + + + + + + + + + + + + + + +
Compressible Config + Protocol PDA that stores account rent config. +
Rent Sponsor + - CToken program PDA that fronts rent exemption at creation.
+ - Claims rent when account compresses. +
System ProgramSolana System Program to create the on-chain account.
diff --git a/snippets/ctoken-create-ata-accounts-list.mdx b/snippets/ctoken-create-ata-accounts-list.mdx index f5a6438c..b6392116 100644 --- a/snippets/ctoken-create-ata-accounts-list.mdx +++ b/snippets/ctoken-create-ata-accounts-list.mdx @@ -1,39 +1,33 @@ - - - - + - - - - - + - - - - - + + + - - - + + -
ConstraintsDescription
0 Owner - - - The wallet that will own this ATA.
- - Used to derive the ATA address deterministically. + - The wallet that will own this cATA.
+ - Used to derive the cATA address deterministically.
1 Mint - - The SPL or cMint token mint.
- - Used to derive the ATA address deterministically. + - Used to derive the cATA address deterministically.
2 Payer signer, mutable @@ -42,36 +36,30 @@
3Associated Token AccountcATA Account mutable - The cATA being created.
- - Address is derived from owner + mint + cToken program ID.
+ - Address is derived from `[owner, ctoken_program_id, mint]`.
4 System Program - Solana System Program. Required for CPI to create the on-chain account.
5Compressible Config- - Protocol PDA that stores rent config and compression authorities. - Bumpu8The PDA bump seed for the cATA address derivation.
6Rent SponsormutableIdempotentbool - - CToken program PDA that fronts rent exemption at creation.
- - Claims rent when account compresses. + - When `true`, silently succeeds if account already exists.
+ - When `false`, fails if account already exists.
\ No newline at end of file + From 1b30ad1bb40ebc5ddf111724ed34824c778fef34 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 01:37:48 +0000 Subject: [PATCH 016/143] Refactor CompressibleRentCalculator UX and update cost examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace slider-first UX with preset buttons and custom input option - Add visual slider markers for better granularity feedback - Update default lamports_per_write: 766 → 800 - Add total creation cost calculation (rent + compression incentive + tx) - Display SOL equivalents in result cards - Change grid from 3 to 2 columns for clearer layout - Update c-token-program.mdx: fix 24h rent cost, add calculator - Update compressible-vs-solana-rent.mdx: clarify tx fee excludes priority --- compressed-token-program/c-token-program.mdx | 7 +- snippets/compressible-rent-calculator.jsx | 191 +++++++++++++------ snippets/compressible-vs-solana-rent.mdx | 4 +- 3 files changed, 135 insertions(+), 67 deletions(-) diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx index 5584363b..27254cfc 100644 --- a/compressed-token-program/c-token-program.mdx +++ b/compressed-token-program/c-token-program.mdx @@ -5,6 +5,7 @@ description: Overview of cMints, cTokens, and compressed token accounts. --- import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; +import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, cTokens and compressed tokens. @@ -293,14 +294,16 @@ This extension makes cToken accounts 200x cheaper than SPL token accounts | | cToken | SPL | |-----------|--------|-----| | allocate | 0.000011 | 0.002 | - | rent for 24h | 0.000005 | - | - + | rent for 24h | 0.00005 | - | + + + # Associated cToken Account **Associated cToken** accounts follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): diff --git a/snippets/compressible-rent-calculator.jsx b/snippets/compressible-rent-calculator.jsx index a4c0bbb3..0f676507 100644 --- a/snippets/compressible-rent-calculator.jsx +++ b/snippets/compressible-rent-calculator.jsx @@ -1,15 +1,24 @@ export const CompressibleRentCalculator = () => { const [hours, setHours] = useState(2); - const [lamportsPerWrite, setLamportsPerWrite] = useState(766); + const [lamportsPerWrite, setLamportsPerWrite] = useState(800); + const [showCustomHours, setShowCustomHours] = useState(false); + const [showCustomLamports, setShowCustomLamports] = useState(false); const DATA_LEN = 260; const BASE_RENT = 128; const LAMPORTS_PER_BYTE_PER_EPOCH = 1; const MINUTES_PER_EPOCH = 42; + const COMPRESSION_INCENTIVE = 11000; + const TX_COST = 5000; + const LAMPORTS_PER_SOL = 1_000_000_000; + + const HOURS_MAX = 36; + const LAMPORTS_MAX = 6400; const numEpochs = Math.ceil((hours * 60) / MINUTES_PER_EPOCH); const rentPerEpoch = BASE_RENT + (DATA_LEN * LAMPORTS_PER_BYTE_PER_EPOCH); const totalPrepaidRent = rentPerEpoch * numEpochs; + const totalCreationCost = totalPrepaidRent + COMPRESSION_INCENTIVE + TX_COST; const handleHoursChange = (value) => { const num = Math.max(1, Math.min(168, Number.parseInt(value) || 2)); @@ -21,95 +30,149 @@ export const CompressibleRentCalculator = () => { setLamportsPerWrite(num); }; + const hoursPresets = [2, 8, 24]; + const lamportsPresets = [800, 1600, 3200]; + + const SliderMarkers = ({ max, step }) => { + const marks = []; + for (let i = step; i < max; i += step) { + const percent = (i / max) * 100; + marks.push( +
+ ); + } + return <>{marks}; + }; + return (
- {/* Sliders */} -
-
- - Prepaid Epochs in Hours - + {/* Prepaid Epochs in Hours */} +
+
+ Prepaid Epochs in Hours +
+ {hoursPresets.map((h) => ( + + ))} + {showCustomHours ? ( handleHoursChange(e.target.value)} - className="w-16 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + className="w-16 px-2 py-1 text-right text-xs font-mono font-medium bg-blue-500/10 dark:bg-blue-500/20 border border-blue-500/50 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + autoFocus /> - hours - + ) : ( + + )} +
+
+
+ + ≈ {numEpochs} epochs / {hours}h - setHours(Number.parseInt(e.target.value))} - className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" - /> - ≈ {((hours * 60) / MINUTES_PER_EPOCH).toFixed(1)} epochs +
+ + { setHours(Number.parseInt(e.target.value)); setShowCustomHours(false); }} + className="relative w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm z-10" + /> +
+
+
- {/* Preset buttons */} -
- {[2, 4, 6, 12, 24].map((h) => ( + {/* Lamports per Write */} +
+
+ Lamports per Write +
+ {lamportsPresets.map((l) => ( ))} + {showCustomLamports ? ( + handleLamportsChange(e.target.value)} + className="w-20 px-2 py-1 text-right text-xs font-mono font-medium bg-blue-500/10 dark:bg-blue-500/20 border border-blue-500/50 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + autoFocus + /> + ) : ( + + )}
- -
- - Lamports per Write +
+ + ≈ {(lamportsPerWrite / rentPerEpoch).toFixed(1)} epochs / {((lamportsPerWrite / rentPerEpoch) * MINUTES_PER_EPOCH / 60).toFixed(1)}h + +
+ handleLamportsChange(e.target.value)} - className="w-24 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + max={LAMPORTS_MAX} + step="100" + value={Math.min(lamportsPerWrite, LAMPORTS_MAX)} + onChange={(e) => { setLamportsPerWrite(Number.parseInt(e.target.value)); setShowCustomLamports(false); }} + className="relative w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm z-10" /> - - setLamportsPerWrite(Number.parseInt(e.target.value))} - className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" - /> +
{/* Result Cards */} -
-
-
Rent per Epoch
-
- {rentPerEpoch.toLocaleString()} -
-
lamports
-
- +
-
Total Prepaid
+
Total Creation Cost
- {totalPrepaidRent.toLocaleString()} + {totalCreationCost.toLocaleString()}
lamports
-
≈ {((hours * 60) / MINUTES_PER_EPOCH).toFixed(1)} epochs / {hours}h
+
≈ {(totalCreationCost / LAMPORTS_PER_SOL).toFixed(6)} SOL
@@ -118,15 +181,17 @@ export const CompressibleRentCalculator = () => { {lamportsPerWrite.toLocaleString()}
lamports
-
≈ {(lamportsPerWrite / rentPerEpoch).toFixed(1)} epochs / {((lamportsPerWrite / rentPerEpoch) * MINUTES_PER_EPOCH / 60).toFixed(1)}h
+
≈ {(lamportsPerWrite / LAMPORTS_PER_SOL).toFixed(6)} SOL
{/* Formula reference */}
- rent_per_epoch = base_rent + (data_len × lamports_per_byte_per_epoch)

- For 260-byte cToken account:
- rent_per_epoch = {BASE_RENT} + ({DATA_LEN} × {LAMPORTS_PER_BYTE_PER_EPOCH}) = {rentPerEpoch} lamports +
Total cost for {DATA_LEN}-byte cToken account:
+ total_creation_cost = prepaid_rent + compression_incentive + tx_cost

+ rent_per_epoch = base_rent + (data_len × lamports_per_byte_per_epoch)
+ rent_per_epoch = {BASE_RENT} + ({DATA_LEN} × {LAMPORTS_PER_BYTE_PER_EPOCH}) = {rentPerEpoch} lamports
+ compression_incentive = {COMPRESSION_INCENTIVE.toLocaleString()} lamports
diff --git a/snippets/compressible-vs-solana-rent.mdx b/snippets/compressible-vs-solana-rent.mdx index e860f3c7..b4f25495 100644 --- a/snippets/compressible-vs-solana-rent.mdx +++ b/snippets/compressible-vs-solana-rent.mdx @@ -36,8 +36,8 @@ rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) Transaction fee - 5,000-10,000 lamports - Standard Solana tx fees + 5,000 lamports + Standard Solana tx fees (excl. priority fee) From ee0a8871e88c79dd3ecbe990e835b2a7d62c1347 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 02:37:14 +0000 Subject: [PATCH 017/143] Refine program-mint-to-cToken guide and add accounts list - Add Tip block: distinguish MintToCToken vs MintToCompressed use cases - Clarify mint params: include validity proof explanation with Tooltip - Improve step headers and numbered instructions for clarity - Rename 'params' to 'mint_params' for consistency - Add cmint-to-ctoken-accounts-list.mdx with detailed account descriptions - Update ctoken-configure-rent.mdx: improve calculator intro text --- .../program-guides/program-mint-to-cToken.mdx | 42 +++++++------ snippets/cmint-to-ctoken-accounts-list.mdx | 61 +++++++++++++++++++ snippets/ctoken-configure-rent.mdx | 4 +- 3 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 snippets/cmint-to-ctoken-accounts-list.mdx diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx index c2f7a43b..6d753dbc 100644 --- a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -4,17 +4,20 @@ description: Program guide to mint cTokens with step-by-step implementation and --- import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; +import CMintToCTokenAccountsList from '/snippets/cmint-to-ctoken-accounts-list.mdx'; ## Key Points + cTokens are minted via CPI to the Compressed Token Program and do not require rent-exemption. -1. Minting creates new token balances in existing cToken accounts. -2. The mint authority must sign the transaction (or be a PDA with invoke_signed). +1. Mint cTokens from an existing cMint to existing cToken accounts. +2. The mint authority must sign the transaction (or be a PDA with `invoke_signed`). - -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). - + +* `MintToCToken` mints from a cMint to cToken Solana accounts. Use when you need SPL-compatible accounts for interoperability with DeFi, etc. +* `MintToCompressed` mints from a cMint to compressed token accounts. Use for token distribution (Airdrops, payments, ...). + Find [full code examples at the end](#full-code-example). @@ -30,7 +33,7 @@ Find [full code examples at the end](#full-code-example). ```rust use light_compressed_token_sdk::ctoken::MintToCTokenParams; -let params = MintToCTokenParams::new( +let mint_params = MintToCTokenParams::new( data.compressed_mint_inputs, data.amount, data.mint_authority, @@ -38,10 +41,8 @@ let params = MintToCTokenParams::new( ); ``` -1. `compressed_mint_inputs` - The compressed mint context with proof data -2. `amount` - Number of tokens to mint -3. `mint_authority` - The authority allowed to mint tokens -4. `proof` - Validity proof for the compressed state +Include your mint, the amount of tokens to be minted and the pubkey of the mint authority. +The client passes a validity proof that proves the cMint exists. @@ -49,7 +50,7 @@ let params = MintToCTokenParams::new( ### System Accounts -Include system accounts required to interact with compressed state. The client includes them in the instruction. +Build system accounts info required to interact with compressed state. The client includes them in the instruction. @@ -72,12 +73,13 @@ let system_accounts = SystemAccountInfos { -### CPI to Compressed Token Program +### Build Mint to cToken Infos and CPI the cToken Program -Build the instruction and invoke the Compressed Token Program. - -* When a CPI doesn't require PDA signers, the `invoke` function is used. -* When a CPI requires a PDA signer, the `invoke_signed` function is used. +1. Pass the required accounts, including the destination cToken accounts. +2. Include `mint_params` and `system_accounts` from the previous steps +3. Use `invoke` or `invoke_signed`: + * When a CPI doesn't require PDA signers, the `invoke` function is used. + * When a CPI requires a PDA signer, the `invoke_signed` function is used. @@ -95,7 +97,7 @@ MintToCTokenInfos { system_accounts, cpi_context: None, cpi_context_account: None, - params, + mint_params, } .invoke()?; ``` @@ -116,7 +118,7 @@ let account_infos = MintToCTokenInfos { system_accounts, cpi_context: None, cpi_context_account: None, - params, + mint_params, }; let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; @@ -126,6 +128,10 @@ account_infos.invoke_signed(&[signer_seeds])?; + + + + diff --git a/snippets/cmint-to-ctoken-accounts-list.mdx b/snippets/cmint-to-ctoken-accounts-list.mdx new file mode 100644 index 00000000..4cb6ed02 --- /dev/null +++ b/snippets/cmint-to-ctoken-accounts-list.mdx @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AccountTypeDescription
AuthoritysignerThe mint authority. Must match the cMint's mint authority.
Payersigner, mutablePays transaction fees.
State TreemutableAccount of the state Merkle tree storing the cMint.
Input QueuemutableAccount of the input queue associated with the state tree the cMint is stored in. The existing hash of the cMint is inserted to this queue to mark it as spent.
Output QueuemutableAccount of the output queue associated with the state tree the cMint will be stored in. The updated hash of the cMint is inserted to this queue.
cToken AccountsmutableDestination cToken Solana accounts to receive minted tokens.
System Accounts-See System Accounts List above.
CPI Contextoptional
(none for most)
Enables batched compressed account operations across multiple programs with a single validity proof. Set to `None` for most operations.
CPI Context Accountoptional, mutableOn-chain account that temporarily stores instruction data from multiple CPIs for combined execution. Set to `None` for most operations.
diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index d1ee25ef..7b5ed72f 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -22,8 +22,10 @@ let compressible_params = CompressibleParamsInfos::new( 3. The account is compressed when the lamports balance is at zero epochs. 4. When the account is written to, the account gets decompressed and the transaction payer tops up the accounts with `lamports_per_write` again. -You can calculate the parameters below based on the expected account activity. +You can customize the parameters based on the expected account activity. + + Learn the core concepts to the rent config [here](/compressed-token-program/rent-config). You can customize the rent config based on expected account activity (must be 0 or ≥2). From 824cb0fcfe918ef291be3b0de8124012f53a62c4 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 18:24:02 +0000 Subject: [PATCH 018/143] Refine cToken rent config docs and add mint-to-cToken accounts list --- .../program-guides/program-create-cATA.mdx | 6 ++-- .../program-guides/program-create-ctoken.mdx | 6 ++-- snippets/ctoken-configure-rent.mdx | 29 ++++++++++--------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index f27626cd..3556feb8 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -14,9 +14,9 @@ import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; Associated cToken accounts (cATA) are Solana accounts and created via CPI to the cToken Program. 1. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -2. Top-up the account with rent for two epochs (12,600 slots / 84 min) to create the account, instead of funding the full rent-exemption. -3. The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. -4. When no transactions top up the lamports balance for two epochs, the account gets compressed. +2. Fund the account with rent for 24h (~0.000030 SOL) when creating the account, instead of the full rent-exemption. +3. The transaction payer tops-up the lamports balance when the account’s lamports balance is below 2 epochs (12,600 slots / 84 min). +4. When no transactions top up the lamports balance for 2 epochs, the account gets compressed. 5. When the account is written to, it’s automatically decompressed and the transaction payer tops-up the lamports balance. diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 884acbc0..0a8bf2f7 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -14,9 +14,9 @@ import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; cToken accounts are Solana accounts and created via CPI to the cToken Program. 1. cToken accounts hold token balances like SPL Token accounts. -2. Top-up the account with rent for two epochs (12,600 slots / 84 min) to create the account, instead of funding the full rent-exemption. -3. The transaction payer top-ups the lamports balance when the account’s lamports balance is below 2 epochs. -4. When no transactions top up the lamports balance for two epochs, the account gets compressed. +2. Fund the account with rent for 24h (~0.000030 SOL) when creating the account, instead of the full rent-exemption. +3. The transaction payer tops-up the lamports balance when the account’s lamports balance is below 2 epochs (12,600 slots / 84 min). +4. When no transactions top up the lamports balance for 2 epochs, the account gets compressed. 5. When the account is written to, it’s automatically decompressed and the transaction payer tops-up the lamports balance. diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index 7b5ed72f..97ea27f4 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -14,23 +14,24 @@ let compressible_params = CompressibleParamsInfos::new( #### Rent Config -1. At creation set `pre_pay_num_epochs` to determine the number of epochs to fund the account with rent. - * This is set to 2 epochs (12,600 slots / 84 min or 766 lamports) by default. -2. Set `lamports_per_write` to determine the amount a transaction payer top-ups the account with rent. - * This is set to 766 lamports by default. - * Top-ups are made when the lamports balance falls below two epochs of rent. -3. The account is compressed when the lamports balance is at zero epochs. -4. When the account is written to, the account gets decompressed and the transaction payer tops up the accounts with `lamports_per_write` again. - -You can customize the parameters based on the expected account activity. - - - +The rent for compressible accounts is configured with default values: +1. At creation, the account creator covers rent for 24 hours (~0.000030 SOL).
`pre_pay_num_epochs` is set to ~35 epochs by default. +2. The transaction payer top-ups the account with rent for ~1.5h of rent (2 epochs), set in `lamports_per_write` to 766 lamports by default. +* Top ups are made when the account's lamports balance is below two epochs of rent (766 lamports). +* The account gets compressed when the account is not topped up for two epochs and the lamports balance is below two epochs of rent (766 lamports). +* When the account is written to, the account gets decompressed and the transaction payer tops up the accounts with `lamports_per_write` again. +* The account creation and first write to decompress include another 11000 lamports for compression & protocol incentive. -Learn the core concepts to the rent config [here](/compressed-token-program/rent-config). -You can customize the rent config based on expected account activity (must be 0 or ≥2). +We recommend to use default values, but you can customize the parameters based on the expected account activity. +Learn the [core concepts of the rent config here](/compressed-token-program/rent-config). + +1. Set the hours you will prepay rent +2. Set the lamports per write a transaction payer will pay for top ups + + + #### Accounts The rent config requires the following accounts: From d47e2317de2fd788f65475e236c554bb9c4cc046 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 18:33:14 +0000 Subject: [PATCH 019/143] Add compression incentive tooltip to rent config snippet --- snippets/ctoken-configure-rent.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index 97ea27f4..02714a5f 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -20,14 +20,14 @@ The rent for compressible accounts is configured with default values: * Top ups are made when the account's lamports balance is below two epochs of rent (766 lamports). * The account gets compressed when the account is not topped up for two epochs and the lamports balance is below two epochs of rent (766 lamports). * When the account is written to, the account gets decompressed and the transaction payer tops up the accounts with `lamports_per_write` again. -* The account creation and first write to decompress include another 11000 lamports for compression & protocol incentive. +* The account creation and writes to decompress include additional 11,000 lamports for compression & protocol incentive. We recommend to use default values, but you can customize the parameters based on the expected account activity. Learn the [core concepts of the rent config here](/compressed-token-program/rent-config). -1. Set the hours you will prepay rent +1. Set the hours to determine the amount of prepaid rent 2. Set the lamports per write a transaction payer will pay for top ups From 65366185d01986e72b8479afbcb3b2da3cde2fab Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 21:08:27 +0000 Subject: [PATCH 020/143] Update epoch duration to 27k slots and refine rent costs --- compressed-token-program/c-token-program.mdx | 8 +++---- .../program-guides/program-create-cmint.mdx | 5 +++-- .../client-guides/client-create-ctoken.mdx | 2 +- .../program-guides/program-create-cATA.mdx | 4 ++-- .../program-guides/program-create-ctoken.mdx | 14 +++++++----- docs.json | 3 ++- snippets/compressible-rent-calculator.jsx | 22 +++++++++---------- snippets/compressible-vs-solana-rent.mdx | 2 +- snippets/ctoken-configure-rent.mdx | 17 ++++++-------- 9 files changed, 40 insertions(+), 37 deletions(-) diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx index 27254cfc..ee611a4b 100644 --- a/compressed-token-program/c-token-program.mdx +++ b/compressed-token-program/c-token-program.mdx @@ -284,9 +284,9 @@ Here is how cTokens and SPL tokens compare: cToken accounts include the compressible extension with rent-exemption sponsorship: 1. The protocol funds the account's rent exemption at creation. -2. The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min) +2. The account creator top-ups the lamport balance with rent for at least two epochs (27,000 slots / 3h) 3. The transaction payer top-ups the lamports balance when the account's lamports balance is below 2 epochs. -4. The account will be compressed when inactive for a period of 12,600 slots (84 min). +4. The account will be compressed when inactive for a period of 27,000 slots (3h). 5. When the account is written to, it's automatically decompressed. This extension makes cToken accounts 200x cheaper than SPL token accounts @@ -294,7 +294,7 @@ This extension makes cToken accounts 200x cheaper than SPL token accounts | | cToken | SPL | |-----------|--------|-----| | allocate | 0.000011 | 0.002 | - | rent for 24h | 0.00005 | - | + | rent for 24h | 0.000006 | - | @@ -336,7 +336,7 @@ Compressed token accounts store token balance, owner, and other information like We recommend to use compressed tokens for **token distribution** or **storage of inactive tokens**. -**cToken accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 12,600 slots (84 min) and decompressed with new writes. +**cToken accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index 8e343d62..27edfe2e 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -11,14 +11,15 @@ cMints are compressed accounts and created via CPI to the Compressed Token Progr * cMints uniquely represent a token on Solana and store its global metadata. * cMints are rent-free. -* The `mint_signer` keypair/PDA derives both the SPL mint address and the cMint's `compression_address`. +* The `mint_signer` keypair/PDA derives the mint address. Find [full code examples at the end](#full-code-example). # Implementation Guide - +The mint_signer keypair/PDA derives both: +The SPL mint address Your program's responsibility: 1. Deserialize the instruction data 2. Build the SDK structs (CreateCMintParams, SystemAccountInfos, CreateCMintAccountInfos) diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx index 21a85d5b..4a95427c 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx @@ -23,7 +23,7 @@ Create a cToken account with a new keypair (non-ATA). -cToken accounts are compressible with rent-exemption sponsorship. The protocol funds the account's rent exemption at creation, and the account compresses automatically when inactive for ~84 minutes (12,600 slots). +cToken accounts are compressible with rent-exemption sponsorship. The protocol funds the account's rent exemption at creation, and the account compresses automatically when inactive for ~3 hours (27,000 slots). # Full Code Example diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index 3556feb8..ce7bb64e 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -14,8 +14,8 @@ import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; Associated cToken accounts (cATA) are Solana accounts and created via CPI to the cToken Program. 1. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -2. Fund the account with rent for 24h (~0.000030 SOL) when creating the account, instead of the full rent-exemption. -3. The transaction payer tops-up the lamports balance when the account’s lamports balance is below 2 epochs (12,600 slots / 84 min). +2. Fund the account with rent for 24h (~0.000022 SOL) when creating the account, instead of the full rent-exemption. +3. The transaction payer tops-up the lamports balance when the account's lamports balance is below 2 epochs (27,000 slots / 3h). 4. When no transactions top up the lamports balance for 2 epochs, the account gets compressed. 5. When the account is written to, it’s automatically decompressed and the transaction payer tops-up the lamports balance. diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 0a8bf2f7..86d7cf2e 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -13,11 +13,15 @@ import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; cToken accounts are Solana accounts and created via CPI to the cToken Program. -1. cToken accounts hold token balances like SPL Token accounts. -2. Fund the account with rent for 24h (~0.000030 SOL) when creating the account, instead of the full rent-exemption. -3. The transaction payer tops-up the lamports balance when the account’s lamports balance is below 2 epochs (12,600 slots / 84 min). -4. When no transactions top up the lamports balance for 2 epochs, the account gets compressed. -5. When the account is written to, it’s automatically decompressed and the transaction payer tops-up the lamports balance. +1. cToken accounts hold token balances similar to SPL Token accounts. +2. cToken accounts are compressible, which means + * The rent-exemption to create the Solana account is sponsored by Light Protocol. + * At creation, the account creator pays 24h / ~16 epochs worth of rent by default (~11,000 lamports). You can customize this amount. + * The account gets compressed when the lamports balance does not cover rent for the current epoch (388 lamports) and the protocol reclaims the rent. + * When a compressed cToken account is written to, the account gets decompressed with the same state. +3. To continously fund the account with rent, the transaction payer tops-up the lamports balance, when the account's lamports balance falls below two epochs of rent (766 lamports). + * The top-up is configured to 766 lamports by default to cover rent for another ~3h / 2 epochs. You can customize this amount. +4. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). diff --git a/docs.json b/docs.json index 571364b2..f87da4d8 100644 --- a/docs.json +++ b/docs.json @@ -94,7 +94,8 @@ "pages": [ "compressed-token-program/ctoken/program-guides/program-create-ctoken", "compressed-token-program/ctoken/program-guides/program-create-cATA", - "compressed-token-program/ctoken/program-guides/program-mint-to-cToken" + "compressed-token-program/ctoken/program-guides/program-mint-to-cToken", + "compressed-token-program/ctoken/program-guides/program-transfer-ctoken" ] } ] diff --git a/snippets/compressible-rent-calculator.jsx b/snippets/compressible-rent-calculator.jsx index 0f676507..08111d8d 100644 --- a/snippets/compressible-rent-calculator.jsx +++ b/snippets/compressible-rent-calculator.jsx @@ -1,13 +1,13 @@ export const CompressibleRentCalculator = () => { - const [hours, setHours] = useState(2); - const [lamportsPerWrite, setLamportsPerWrite] = useState(800); + const [hours, setHours] = useState(24); + const [lamportsPerWrite, setLamportsPerWrite] = useState(766); const [showCustomHours, setShowCustomHours] = useState(false); const [showCustomLamports, setShowCustomLamports] = useState(false); const DATA_LEN = 260; const BASE_RENT = 128; const LAMPORTS_PER_BYTE_PER_EPOCH = 1; - const MINUTES_PER_EPOCH = 42; + const MINUTES_PER_EPOCH = 90; const COMPRESSION_INCENTIVE = 11000; const TX_COST = 5000; const LAMPORTS_PER_SOL = 1_000_000_000; @@ -21,7 +21,7 @@ export const CompressibleRentCalculator = () => { const totalCreationCost = totalPrepaidRent + COMPRESSION_INCENTIVE + TX_COST; const handleHoursChange = (value) => { - const num = Math.max(1, Math.min(168, Number.parseInt(value) || 2)); + const num = Math.max(3, Math.min(168, Number.parseInt(value) || 3)); setHours(num); }; @@ -30,8 +30,8 @@ export const CompressibleRentCalculator = () => { setLamportsPerWrite(num); }; - const hoursPresets = [2, 8, 24]; - const lamportsPresets = [800, 1600, 3200]; + const hoursPresets = [24]; + const lamportsPresets = [766]; const SliderMarkers = ({ max, step }) => { const marks = []; @@ -66,13 +66,13 @@ export const CompressibleRentCalculator = () => { : 'bg-black/[0.015] dark:bg-white/5 border-black/[0.04] dark:border-white/20 text-zinc-600 dark:text-white/70 hover:bg-black/[0.03]' }`} > - {h}h + {h === 24 ? 'Default' : `${h}h`} ))} {showCustomHours ? ( handleHoursChange(e.target.value)} @@ -91,13 +91,13 @@ export const CompressibleRentCalculator = () => {
- ≈ {numEpochs} epochs / {hours}h + ≈ {((hours * 60) / MINUTES_PER_EPOCH).toFixed(1)} epochs / {hours.toFixed(1)}h
{ setHours(Number.parseInt(e.target.value)); setShowCustomHours(false); }} @@ -122,7 +122,7 @@ export const CompressibleRentCalculator = () => { : 'bg-black/[0.015] dark:bg-white/5 border-black/[0.04] dark:border-white/20 text-zinc-600 dark:text-white/70 hover:bg-black/[0.03]' }`} > - {l.toLocaleString()} + {l === 766 ? 'Default' : l.toLocaleString()} ))} {showCustomLamports ? ( diff --git a/snippets/compressible-vs-solana-rent.mdx b/snippets/compressible-vs-solana-rent.mdx index b4f25495..3ee9ba7a 100644 --- a/snippets/compressible-vs-solana-rent.mdx +++ b/snippets/compressible-vs-solana-rent.mdx @@ -1,6 +1,6 @@ ### Initial Rent Top-Up The **creator of compressible accounts** tops-up the account with **rent for at least two epochs**. -Two epochs for compressible accounts have a length of **12,600 slots**. +Two epochs for compressible accounts have a length of **27,000 slots**. ``` rust rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index 02714a5f..020e6d56 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -1,11 +1,9 @@ -Use `CompressibleParamsInfos` to configure rent: +Use `CompressibleParamsInfos` to pass the default rent config and accounts: ```rust use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, compressible_config.clone(), rent_sponsor.clone(), system_program.clone(), @@ -14,13 +12,12 @@ let compressible_params = CompressibleParamsInfos::new( #### Rent Config -The rent for compressible accounts is configured with default values: -1. At creation, the account creator covers rent for 24 hours (~0.000030 SOL).
`pre_pay_num_epochs` is set to ~35 epochs by default. -2. The transaction payer top-ups the account with rent for ~1.5h of rent (2 epochs), set in `lamports_per_write` to 766 lamports by default. -* Top ups are made when the account's lamports balance is below two epochs of rent (766 lamports). -* The account gets compressed when the account is not topped up for two epochs and the lamports balance is below two epochs of rent (766 lamports). -* When the account is written to, the account gets decompressed and the transaction payer tops up the accounts with `lamports_per_write` again. -* The account creation and writes to decompress include additional 11,000 lamports for compression & protocol incentive. +The rent for compressible accounts is configured with default values for the initial rent payment. +You don't need to pass the rent config in this struct. + +1. At creation, the account creator covers rent for 24 hours (~0.000022 SOL) ~16 epochs, by default. +2. The transaction payer top-ups the account with 766 lamports to cover rent for ~3h / 2 epochs by default, when the account's lamports balance falls below two epochs of rent (766 lamports). + We recommend to use default values, but you can customize the parameters based on the expected account activity. Learn the [core concepts of the rent config here](/compressed-token-program/rent-config). From ba87161dd28b507de380890bda787438070c30fa Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 21:29:30 +0000 Subject: [PATCH 021/143] Refine compressible rent config docs and cmint guide structure --- .../program-guides/program-create-cmint.mdx | 28 ++++++++----------- .../program-guides/program-create-cATA.mdx | 12 +++++--- .../program-guides/program-create-ctoken.mdx | 6 ++-- snippets/ctoken-configure-rent.mdx | 11 +++----- 4 files changed, 26 insertions(+), 31 deletions(-) diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index 27edfe2e..5b648aef 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -18,12 +18,6 @@ Find [full code examples at the end](#full-code-example). # Implementation Guide -The mint_signer keypair/PDA derives both: -The SPL mint address -Your program's responsibility: -1. Deserialize the instruction data -2. Build the SDK structs (CreateCMintParams, SystemAccountInfos, CreateCMintAccountInfos) -3. Call .invoke() or .invoke_signed() @@ -36,7 +30,7 @@ Set `decimals`, `mint_authority`, `freeze_authority`, and `extensions`. ```rust use light_compressed_token_sdk::ctoken::CreateCMintParams; -let params = CreateCMintParams { +let cmint_params = CreateCMintParams { decimals: data.decimals, address_merkle_tree_root_index: data.address_merkle_tree_root_index, mint_authority: data.mint_authority, @@ -61,7 +55,6 @@ The client includes them in the instruction. - The address of a cMint is stored in an address Merkle tree. You can safely ignore the `address_merkle_tree_root_index` - it tells your program where in the address tree the address is stored. @@ -86,16 +79,17 @@ let system_accounts = SystemAccountInfos { ### CPI to Compressed Token Program Build the instruction and invoke the Compressed Token Program. - - -* Both mint_signer and authority are PDAs. +1. Pass the required accounts +2. Include `cmint_params` and `system_accounts` from the previous steps +3. Use `invoke` or `invoke_signed`: + * When `mint_signer` is an external keypair, use `invoke`. + * When `mint_signer` is a PDA, use `invoke_signed` with its seeds. + * When both `mint_signer` and `authority` are PDAs, use `invoke_signed` with both seeds. -// mint_signer is a keypair - user signs the transaction - ```rust use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; @@ -108,7 +102,7 @@ CreateCMintAccountInfos { system_accounts, cpi_context: None, cpi_context_account: None, - params, + cmint_params, } .invoke()?; ``` @@ -129,7 +123,7 @@ let account_infos = CreateCMintAccountInfos { system_accounts, cpi_context: None, cpi_context_account: None, - params, + cmint_params, }; let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]]; @@ -137,7 +131,7 @@ account_infos.invoke_signed(&[signer_seeds])?; ``` - + ```rust use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; @@ -151,7 +145,7 @@ let account_infos = CreateCMintAccountInfos { system_accounts, cpi_context: None, cpi_context_account: None, - params, + cmint_params, }; let mint_signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_signer_bump]]; diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index ce7bb64e..db2492d3 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -14,10 +14,14 @@ import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; Associated cToken accounts (cATA) are Solana accounts and created via CPI to the cToken Program. 1. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -2. Fund the account with rent for 24h (~0.000022 SOL) when creating the account, instead of the full rent-exemption. -3. The transaction payer tops-up the lamports balance when the account's lamports balance is below 2 epochs (27,000 slots / 3h). -4. When no transactions top up the lamports balance for 2 epochs, the account gets compressed. -5. When the account is written to, it’s automatically decompressed and the transaction payer tops-up the lamports balance. +2. cATAs are compressible, which means + * The rent-exemption to create the Solana account is sponsored by Light Protocol. + * At creation, the account creator pays 24h / ~16 epochs worth of rent by default (~11,000 lamports). + * The account gets compressed when the lamports balance does not cover rent for the current epoch (388 lamports) and the protocol reclaims the rent. + * When a compressed cATA is written to, the account gets decompressed with the same state. +3. To continously fund the account with rent, the transaction payer tops-up the lamports balance, when the account's lamports balance falls below two epochs of rent (766 lamports). + * The top-up is configured to 766 lamports by default to cover rent for another ~3h / 2 epochs. +4. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 86d7cf2e..6c10e576 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -16,11 +16,11 @@ cToken accounts are Solana accounts and created via CPI to the cToken Program. 1. cToken accounts hold token balances similar to SPL Token accounts. 2. cToken accounts are compressible, which means * The rent-exemption to create the Solana account is sponsored by Light Protocol. - * At creation, the account creator pays 24h / ~16 epochs worth of rent by default (~11,000 lamports). You can customize this amount. + * At creation, the account creator pays 24h / ~16 epochs worth of rent by default (~11,000 lamports). * The account gets compressed when the lamports balance does not cover rent for the current epoch (388 lamports) and the protocol reclaims the rent. * When a compressed cToken account is written to, the account gets decompressed with the same state. 3. To continously fund the account with rent, the transaction payer tops-up the lamports balance, when the account's lamports balance falls below two epochs of rent (766 lamports). - * The top-up is configured to 766 lamports by default to cover rent for another ~3h / 2 epochs. You can customize this amount. + * The top-up is configured to 766 lamports by default to cover rent for another ~3h / 2 epochs. 4. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. @@ -36,7 +36,7 @@ Find [full code examples at the end](#full-code-example). -### Configure Rent +### Rent Config Accounts diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index 020e6d56..f4d54946 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -10,14 +10,11 @@ let compressible_params = CompressibleParamsInfos::new( ); ``` -#### Rent Config - -The rent for compressible accounts is configured with default values for the initial rent payment. -You don't need to pass the rent config in this struct. - -1. At creation, the account creator covers rent for 24 hours (~0.000022 SOL) ~16 epochs, by default. -2. The transaction payer top-ups the account with 766 lamports to cover rent for ~3h / 2 epochs by default, when the account's lamports balance falls below two epochs of rent (766 lamports). +#### Default Rent Config +You don't need to pass the rent config in this struct, the SDK sets default values for the rent config of compressible accounts: +- Initial rent payment to 24 hours / ~16 epochs (~11,000 lamports) +- Top-up amount to 766 lamports to cover rent for 3h / ~2 epochs We recommend to use default values, but you can customize the parameters based on the expected account activity. Learn the [core concepts of the rent config here](/compressed-token-program/rent-config). From bacad3de207a5717ce33e895c6c7f866a7c3f726 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 22:15:49 +0000 Subject: [PATCH 022/143] Add token metadata section to cmint creation guide --- .../program-guides/program-create-cmint.mdx | 70 ++++++++++++++----- .../program-guides/program-mint-to-cToken.mdx | 2 +- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index 5b648aef..86eb3435 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -1,6 +1,6 @@ --- -title: Create cMint Accounts -description: Program guide to create cMint accounts with step-by-step implementation and full code examples. +title: Create cMint Accounts with Token Metadata +description: Program guide to create cMint accounts with token metadata. Includes step-by-step implementation and full code examples. --- import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; @@ -9,9 +9,13 @@ import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; cMints are compressed accounts and created via CPI to the Compressed Token Program. -* cMints uniquely represent a token on Solana and store its global metadata. -* cMints are rent-free. -* The `mint_signer` keypair/PDA derives the mint address. +1. cMints uniquely represent a token on Solana and store its global metadata. +2. cMints are rent-free. +3. The `mint_signer` keypair/PDA derives the mint address. + + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + Find [full code examples at the end](#full-code-example). @@ -22,10 +26,45 @@ Find [full code examples at the end](#full-code-example). +### Configure Token Metadata +```rust +use light_ctoken_types::{ + instructions::extensions::{ + token_metadata::TokenMetadataInstructionData, + ExtensionInstructionData, + }, + state::AdditionalMetadata, +}; + +let token_metadata = ExtensionInstructionData::TokenMetadata( + TokenMetadataInstructionData { + update_authority: Some(authority.to_bytes().into()), + name: b"My Token".to_vec(), + symbol: b"MTK".to_vec(), + uri: b"https://example.com/metadata.json".to_vec(), + additional_metadata: Some(vec![ + AdditionalMetadata { + key: b"category".to_vec(), + value: b"utility".to_vec(), + }, + ]), + }, +); +``` + + +**Fields must be set at cMint creation.** +* Standard fields (`name`, `symbol`, `uri`) can be updated by `update_authority`. +* For `additional_metadata`, only existing keys can be modified or removed. New keys cannot be added after creation. + + + + + ### Configure cMint -Set `decimals`, `mint_authority`, `freeze_authority`, and `extensions`. +Set `decimals`, `mint_authority`, `freeze_authority`, and pass the `token_metadata` from the previous step. ```rust use light_compressed_token_sdk::ctoken::CreateCMintParams; @@ -38,9 +77,13 @@ let cmint_params = CreateCMintParams { compression_address: data.compression_address, mint: data.mint, freeze_authority: data.freeze_authority, - extensions: data.extensions, + extensions: data.token_metadata, }; ``` + +* The client passes a validity proof that proves the cMint address does not exist in the address tree where it will be stored. +* You can safely ignore `compression_address` and `address_merkle_tree_root_index`. The client passes these for proof verification. + @@ -55,11 +98,6 @@ The client includes them in the instruction. - -The address of a cMint is stored in an address Merkle tree. -You can safely ignore the `address_merkle_tree_root_index` - it tells your program where in the address tree the address is stored. - - ```rust use light_compressed_token_sdk::ctoken::SystemAccountInfos; @@ -76,9 +114,8 @@ let system_accounts = SystemAccountInfos { -### CPI to Compressed Token Program +### Build Account Infos and CPI the cToken Program -Build the instruction and invoke the Compressed Token Program. 1. Pass the required accounts 2. Include `cmint_params` and `system_accounts` from the previous steps 3. Use `invoke` or `invoke_signed`: @@ -87,7 +124,7 @@ Build the instruction and invoke the Compressed Token Program. * When both `mint_signer` and `authority` are PDAs, use `invoke_signed` with both seeds. - + ```rust @@ -109,7 +146,6 @@ CreateCMintAccountInfos { -// mint_signer is a PDA - program signs via invoke_signed ```rust use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; @@ -164,7 +200,7 @@ account_infos.invoke_signed(&[mint_signer_seeds, authority_seeds])?; # Full Code Example -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_cmint.rs). ```rust expandable diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx index 6d753dbc..cf77710a 100644 --- a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -73,7 +73,7 @@ let system_accounts = SystemAccountInfos { -### Build Mint to cToken Infos and CPI the cToken Program +### Build Account Infos and CPI the cToken Program 1. Pass the required accounts, including the destination cToken accounts. 2. Include `mint_params` and `system_accounts` from the previous steps From 36cae710ff82f1fda8c7f2316b539850c015c819 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 30 Nov 2025 22:31:22 +0000 Subject: [PATCH 023/143] Add cmint overview page and refine program guide --- compressed-token-program/cmint/cmint.mdx | 45 +++++++++++++++++++ .../program-guides/program-create-cmint.mdx | 5 ++- docs.json | 1 + 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 compressed-token-program/cmint/cmint.mdx diff --git a/compressed-token-program/cmint/cmint.mdx b/compressed-token-program/cmint/cmint.mdx new file mode 100644 index 00000000..94d19ba6 --- /dev/null +++ b/compressed-token-program/cmint/cmint.mdx @@ -0,0 +1,45 @@ +--- +title: cMint Accounts +sidebarTitle: Overview +description: Overview of cMint accounts. +--- + +import { SolanaRentCalculator } from '/snippets/solana-rent-calculator.jsx'; + +## Key Points + +A cMint is a compressed account that represents a token mint on Solana. cMints store the same global metadata as SPL token mints but without requiring rent. + + + +## Client Guides + +table for client guides + +## Program Guides + +table for program guides +## Examples + +add github link + +## Next Steps + +Follow our client or program guides for cMints. + + + + + \ No newline at end of file diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index 86eb3435..b63e5517 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -7,11 +7,12 @@ import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; ## Key Points -cMints are compressed accounts and created via CPI to the Compressed Token Program. +cMints are created via CPI to the Compressed Token Program. 1. cMints uniquely represent a token on Solana and store its global metadata. -2. cMints are rent-free. +2. cMints are compressed accounts and rent-free. 3. The `mint_signer` keypair/PDA derives the mint address. +4. Tokens created from cMints are cTokens. Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). diff --git a/docs.json b/docs.json index f87da4d8..cad0323d 100644 --- a/docs.json +++ b/docs.json @@ -62,6 +62,7 @@ { "group": "cMint", "pages": [ + "compressed-token-program/cmint/cmint", { "group": "Client Guides", "pages": [ From be058d7823b654cf6f86c6035abcaefc6d0f3a60 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 00:26:32 +0000 Subject: [PATCH 024/143] Consolidate transfer guides into unified interface doc --- .../program-transfer-ctoken.mdx | 161 -------- .../program-transfer-interface.mdx | 110 +++--- .../program-transfer-spl-ctoken.mdx | 343 ------------------ docs.json | 3 +- ...nsfer-interface-accounts-list-ctoken-1.mdx | 43 +++ ...transfer-interface-accounts-list-spl-2.mdx | 30 ++ snippets/transfer-interface-accounts-list.mdx | 61 ++++ 7 files changed, 200 insertions(+), 551 deletions(-) delete mode 100644 compressed-token-program/ctoken/program-guides/program-transfer-ctoken.mdx delete mode 100644 compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken.mdx create mode 100644 snippets/transfer-interface-accounts-list-ctoken-1.mdx create mode 100644 snippets/transfer-interface-accounts-list-spl-2.mdx create mode 100644 snippets/transfer-interface-accounts-list.mdx diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-ctoken.mdx deleted file mode 100644 index 295d0368..00000000 --- a/compressed-token-program/ctoken/program-guides/program-transfer-ctoken.mdx +++ /dev/null @@ -1,161 +0,0 @@ ---- -title: Transfer cTokens -description: Program guide to transfer cTokens between accounts with step-by-step implementation and full code examples. ---- - -## Key Points - -cToken transfers are executed via CPI to the cToken Program. - -1. Transfer moves token balances between cToken accounts. -2. The source account owner must sign the transaction (or be a PDA with invoke_signed). -3. Simple interface: source, destination, amount, authority. - - -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). - - - -Find [full code examples at the end](#full-code-example). - - -# Implementation Guide - - - - -### cToken Program CPI - -Build the transfer instruction and invoke the cToken Program. - -* When a CPI doesn't require PDA signers, the `invoke` function is used. -* When a CPI requires a PDA signer, the `invoke_signed` function is used. - - - - -```rust -use light_compressed_token_sdk::ctoken::TransferCtokenAccountInfos; - -TransferCtokenAccountInfos { - source: source.clone(), - destination: destination.clone(), - amount: data.amount, - authority: authority.clone(), -} -.invoke()?; -``` - - - - -```rust -use light_compressed_token_sdk::ctoken::TransferCtokenAccountInfos; - -let transfer_accounts = TransferCtokenAccountInfos { - source: source.clone(), - destination: destination.clone(), - amount: data.amount, - authority: authority.clone(), -}; - -let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; -transfer_accounts.invoke_signed(&[signer_seeds])?; -``` - - - - - - - -# Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/transfer.rs). - - -```rust expandable -use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::TransferCtokenAccountInfos; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::{ID, TOKEN_ACCOUNT_SEED}; - -/// Instruction data for transfer operations -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct TransferData { - pub amount: u64, -} - -/// Handler for transferring compressed tokens (invoke) -/// -/// Account order: -/// - accounts[0]: source ctoken account -/// - accounts[1]: destination ctoken account -/// - accounts[2]: authority (signer) -pub fn process_transfer_invoke( - accounts: &[AccountInfo], - data: TransferData, -) -> Result<(), ProgramError> { - if accounts.len() < 3 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - TransferCtokenAccountInfos { - source: accounts[0].clone(), - destination: accounts[1].clone(), - amount: data.amount, - authority: accounts[2].clone(), - } - .invoke()?; - - Ok(()) -} - -/// Handler for transferring compressed tokens from PDA-owned account (invoke_signed) -/// -/// Account order: -/// - accounts[0]: source ctoken account (PDA-owned) -/// - accounts[1]: destination ctoken account -/// - accounts[2]: authority (PDA) -pub fn process_transfer_invoke_signed( - accounts: &[AccountInfo], - data: TransferData, -) -> Result<(), ProgramError> { - if accounts.len() < 3 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive the PDA for the authority - let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); - - // Verify the authority account is the PDA - if &pda != accounts[2].key { - return Err(ProgramError::InvalidSeeds); - } - - let transfer_accounts = TransferCtokenAccountInfos { - source: accounts[0].clone(), - destination: accounts[1].clone(), - amount: data.amount, - authority: accounts[2].clone(), - }; - - // Invoke with PDA signing - let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; - transfer_accounts.invoke_signed(&[signer_seeds])?; - - Ok(()) -} -``` - -# Next Steps - - \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index 4dc68bd4..d20d6445 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -1,20 +1,21 @@ --- -title: Unified Transfer Interface -description: Program guide for the unified transfer interface that auto-detects account types and routes transfers. +title: Transfer Interface for cToken <> SPL Token +description: Program guide for the unified transfer interface that auto-detects account types and routes cToken <> SPL token transfers. --- +import TransferInterfaceAccountsListCtoken1 from '/snippets/transfer-interface-accounts-list-ctoken-1.mdx'; +import TransferInterfaceAccountsListSpl2 from '/snippets/transfer-interface-accounts-list-spl-2.mdx'; + ## Key Points -The `TransferInterface` is a unified API that automatically detects account types and routes to the appropriate transfer: +The `TransferInterface` is a unified API that automatically detects account types and routes to the appropriate transfer via CPI to the cToken Program: -1. **cToken → cToken**: Direct compressed token transfer -2. **cToken → SPL**: Withdraw to SPL token account -3. **SPL → cToken**: Deposit from SPL token account +1. **cToken → cToken**: Transfer of cTokens between cToken accounts. +2. **cToken → SPL**: Withdraw cTokens to an SPL token account +3. **SPL → cToken**: Deposit SPL tokens to a cToken account -This is an advanced interface. For explicit transfers, see the individual guides: -- [cToken Transfer](/compressed-token-program/ctoken/program-guides/program-transfer-ctoken) -- [SPL ↔ cToken Transfer](/compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken) +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). @@ -26,12 +27,12 @@ Find [full code examples at the end](#full-code-example). -### Build Transfer Interface - -Create the transfer interface and optionally configure SPL bridge support. +### cToken Transfer Interface - - + +* Define the number of cTokens / SPL tokens to transfer, +* from which SPL or cToken account to transfer, and +* to which SPL or cToken account to transfer. ```rust use light_compressed_token_sdk::ctoken::TransferInterface; @@ -44,15 +45,50 @@ let mut transfer = TransferInterface::new( payer.clone(), compressed_token_program_authority.clone(), ); +``` + + + -// Add SPL bridge config if needed for SPL<->cToken transfers + + + + +### SPL Interface + +```rust transfer = transfer.with_spl_interface( Some(mint.clone()), Some(spl_token_program.clone()), Some(token_pool_pda.clone()), data.token_pool_pda_bump, )?; +``` + +The SPL interface requires for SPL ↔ cToken transfers the `token_pool_pda` as escrow account: + * **SPL → cToken**: SPL tokens are transferred locked in this pool, cTokens are minted to cToken accounts + * **cToken → SPL**: cTokens are burned, SPL tokens withdrawn to SPL token accounts + +The token pool is derived from the `mint` pubkey and pool seed. + + + + + + + + + +### CPI to the cToken Program + +Use `invoke` or `invoke_signed`: + * When a CPI doesn't require PDA signers, the `invoke` function is used. + * When a CPI requires a PDA signer, the `invoke_signed` function is used. + + + +```rust transfer.invoke()?; ``` @@ -60,25 +96,6 @@ transfer.invoke()?; ```rust -use light_compressed_token_sdk::ctoken::TransferInterface; - -let mut transfer = TransferInterface::new( - data.amount, - source_account.clone(), - destination_account.clone(), - authority.clone(), - payer.clone(), - compressed_token_program_authority.clone(), -); - -// Add SPL bridge config if needed -transfer = transfer.with_spl_interface( - Some(mint.clone()), - Some(spl_token_program.clone()), - Some(token_pool_pda.clone()), - data.token_pool_pda_bump, -)?; - let authority_seeds: &[&[u8]] = &[TRANSFER_INTERFACE_AUTHORITY_SEED, &[authority_bump]]; transfer.invoke_signed(&[authority_seeds])?; ``` @@ -86,6 +103,7 @@ transfer.invoke_signed(&[authority_seeds])?; + @@ -109,25 +127,25 @@ pub const TRANSFER_INTERFACE_AUTHORITY_SEED: &[u8] = b"transfer_interface_author #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct TransferInterfaceData { pub amount: u64, - /// Required for SPL<->CToken transfers, None for CToken->CToken + /// Required for SPL<->cToken transfers, None for cToken->cToken pub token_pool_pda_bump: Option, } /// Handler for TransferInterface (invoke) /// /// This unified interface automatically detects account types and routes to: -/// - CToken -> CToken transfer -/// - CToken -> SPL transfer -/// - SPL -> CToken transfer +/// - cToken -> cToken transfer +/// - cToken -> SPL transfer +/// - SPL -> cToken transfer /// /// Account order: /// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or CToken) -/// - accounts[2]: destination_account (SPL or CToken) +/// - accounts[1]: source_account (SPL or cToken) +/// - accounts[2]: destination_account (SPL or cToken) /// - accounts[3]: authority (signer) /// - accounts[4]: payer (signer) /// - accounts[5]: compressed_token_program_authority -/// For SPL bridge (optional, required for SPL<->CToken): +/// For SPL interface (required for SPL<->cToken): /// - accounts[6]: mint /// - accounts[7]: token_pool_pda /// - accounts[8]: spl_token_program @@ -148,7 +166,7 @@ pub fn process_transfer_interface_invoke( accounts[5].clone(), // compressed_token_program_authority ); - // Add SPL bridge config if provided + // Add SPL interface config if provided if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { transfer = transfer.with_spl_interface( Some(accounts[6].clone()), // mint @@ -169,12 +187,12 @@ pub fn process_transfer_interface_invoke( /// /// Account order: /// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or CToken) -/// - accounts[2]: destination_account (SPL or CToken) +/// - accounts[1]: source_account (SPL or cToken) +/// - accounts[2]: destination_account (SPL or cToken) /// - accounts[3]: authority (PDA, not signer - program signs) /// - accounts[4]: payer (signer) /// - accounts[5]: compressed_token_program_authority -/// For SPL bridge (optional, required for SPL<->CToken): +/// For SPL interface (required for SPL<->cToken): /// - accounts[6]: mint /// - accounts[7]: token_pool_pda /// - accounts[8]: spl_token_program @@ -204,7 +222,7 @@ pub fn process_transfer_interface_invoke_signed( accounts[5].clone(), // compressed_token_program_authority ); - // Add SPL bridge config if provided + // Add SPL interface config if provided if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { transfer = transfer.with_spl_interface( Some(accounts[6].clone()), // mint diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken.mdx deleted file mode 100644 index ec8411ec..00000000 --- a/compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken.mdx +++ /dev/null @@ -1,343 +0,0 @@ ---- -title: Transfer Between SPL and cTokens -description: Program guide to transfer tokens between SPL and cToken accounts with step-by-step implementation and full code examples. ---- - -## Key Points - -SPL ↔ cToken transfers are executed via CPI to the Compressed Token Program. - -1. **SPL → cToken**: Deposits SPL tokens into a cToken account. -2. **cToken → SPL**: Withdraws cTokens to an SPL token account. -3. Uses a token pool PDA to bridge between the two token types. - - -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). - - - -Find [full code examples at the end](#full-code-example). - - -# Implementation Guide - - - - -### SPL to cToken Transfer - -Transfer SPL tokens into a cToken account. - - - - -```rust -use light_compressed_token_sdk::ctoken::TransferSplToCtokenAccountInfos; - -TransferSplToCtokenAccountInfos { - source_spl_token_account: source_spl_token_account.clone(), - destination_ctoken_account: destination_ctoken_account.clone(), - amount: data.amount, - authority: authority.clone(), - mint: mint.clone(), - payer: payer.clone(), - token_pool_pda: token_pool_pda.clone(), - token_pool_pda_bump: data.token_pool_pda_bump, - spl_token_program: spl_token_program.clone(), - compressed_token_program_authority: compressed_token_program_authority.clone(), -} -.invoke()?; -``` - - - - -```rust -use light_compressed_token_sdk::ctoken::TransferSplToCtokenAccountInfos; - -let account_infos = TransferSplToCtokenAccountInfos { - source_spl_token_account: source_spl_token_account.clone(), - destination_ctoken_account: destination_ctoken_account.clone(), - amount: data.amount, - authority: authority.clone(), - mint: mint.clone(), - payer: payer.clone(), - token_pool_pda: token_pool_pda.clone(), - token_pool_pda_bump: data.token_pool_pda_bump, - spl_token_program: spl_token_program.clone(), - compressed_token_program_authority: compressed_token_program_authority.clone(), -}; - -let authority_seeds: &[&[u8]] = &[TRANSFER_AUTHORITY_SEED, &[authority_bump]]; -account_infos.invoke_signed(&[authority_seeds])?; -``` - - - - - - - - -### cToken to SPL Transfer - -Withdraw cTokens to an SPL token account. - - - - -```rust -use light_compressed_token_sdk::ctoken::TransferCtokenToSplAccountInfos; - -TransferCtokenToSplAccountInfos { - source_ctoken_account: source_ctoken_account.clone(), - destination_spl_token_account: destination_spl_token_account.clone(), - amount: data.amount, - authority: authority.clone(), - mint: mint.clone(), - payer: payer.clone(), - token_pool_pda: token_pool_pda.clone(), - token_pool_pda_bump: data.token_pool_pda_bump, - spl_token_program: spl_token_program.clone(), - compressed_token_program_authority: compressed_token_program_authority.clone(), -} -.invoke()?; -``` - - - - -```rust -use light_compressed_token_sdk::ctoken::TransferCtokenToSplAccountInfos; - -let account_infos = TransferCtokenToSplAccountInfos { - source_ctoken_account: source_ctoken_account.clone(), - destination_spl_token_account: destination_spl_token_account.clone(), - amount: data.amount, - authority: authority.clone(), - mint: mint.clone(), - payer: payer.clone(), - token_pool_pda: token_pool_pda.clone(), - token_pool_pda_bump: data.token_pool_pda_bump, - spl_token_program: spl_token_program.clone(), - compressed_token_program_authority: compressed_token_program_authority.clone(), -}; - -let authority_seeds: &[&[u8]] = &[TRANSFER_AUTHORITY_SEED, &[authority_bump]]; -account_infos.invoke_signed(&[authority_seeds])?; -``` - - - - - - - -# Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/transfer_spl_ctoken.rs). - - -```rust expandable -use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::{ - TransferCtokenToSplAccountInfos, TransferSplToCtokenAccountInfos, -}; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::ID; - -/// PDA seed for authority in invoke_signed variants -pub const TRANSFER_AUTHORITY_SEED: &[u8] = b"transfer_authority"; - -/// Instruction data for SPL to cToken transfer -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct TransferSplToCtokenData { - pub amount: u64, - pub token_pool_pda_bump: u8, -} - -/// Instruction data for cToken to SPL transfer -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct TransferCtokenToSplData { - pub amount: u64, - pub token_pool_pda_bump: u8, -} - -/// Handler for transferring SPL tokens to cToken (invoke) -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_spl_token_account -/// - accounts[2]: destination_ctoken_account (writable) -/// - accounts[3]: authority (signer) -/// - accounts[4]: mint -/// - accounts[5]: payer (signer) -/// - accounts[6]: token_pool_pda -/// - accounts[7]: spl_token_program -/// - accounts[8]: compressed_token_program_authority -pub fn process_spl_to_ctoken_invoke( - accounts: &[AccountInfo], - data: TransferSplToCtokenData, -) -> Result<(), ProgramError> { - if accounts.len() < 9 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - TransferSplToCtokenAccountInfos { - source_spl_token_account: accounts[1].clone(), - destination_ctoken_account: accounts[2].clone(), - amount: data.amount, - authority: accounts[3].clone(), - mint: accounts[4].clone(), - payer: accounts[5].clone(), - token_pool_pda: accounts[6].clone(), - token_pool_pda_bump: data.token_pool_pda_bump, - spl_token_program: accounts[7].clone(), - compressed_token_program_authority: accounts[8].clone(), - } - .invoke()?; - - Ok(()) -} - -/// Handler for transferring SPL tokens to cToken with PDA authority (invoke_signed) -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_spl_token_account -/// - accounts[2]: destination_ctoken_account (writable) -/// - accounts[3]: authority (PDA, not signer - program signs) -/// - accounts[4]: mint -/// - accounts[5]: payer (signer) -/// - accounts[6]: token_pool_pda -/// - accounts[7]: spl_token_program -/// - accounts[8]: compressed_token_program_authority -pub fn process_spl_to_ctoken_invoke_signed( - accounts: &[AccountInfo], - data: TransferSplToCtokenData, -) -> Result<(), ProgramError> { - if accounts.len() < 9 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - let (authority_pda, authority_bump) = - Pubkey::find_program_address(&[TRANSFER_AUTHORITY_SEED], &ID); - - if &authority_pda != accounts[3].key { - return Err(ProgramError::InvalidSeeds); - } - - let account_infos = TransferSplToCtokenAccountInfos { - source_spl_token_account: accounts[1].clone(), - destination_ctoken_account: accounts[2].clone(), - amount: data.amount, - authority: accounts[3].clone(), - mint: accounts[4].clone(), - payer: accounts[5].clone(), - token_pool_pda: accounts[6].clone(), - token_pool_pda_bump: data.token_pool_pda_bump, - spl_token_program: accounts[7].clone(), - compressed_token_program_authority: accounts[8].clone(), - }; - - let authority_seeds: &[&[u8]] = &[TRANSFER_AUTHORITY_SEED, &[authority_bump]]; - account_infos.invoke_signed(&[authority_seeds])?; - - Ok(()) -} - -/// Handler for transferring cToken to SPL tokens (invoke) -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_ctoken_account -/// - accounts[2]: destination_spl_token_account -/// - accounts[3]: authority (signer) -/// - accounts[4]: mint -/// - accounts[5]: payer (signer) -/// - accounts[6]: token_pool_pda -/// - accounts[7]: spl_token_program -/// - accounts[8]: compressed_token_program_authority -pub fn process_ctoken_to_spl_invoke( - accounts: &[AccountInfo], - data: TransferCtokenToSplData, -) -> Result<(), ProgramError> { - if accounts.len() < 9 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - TransferCtokenToSplAccountInfos { - source_ctoken_account: accounts[1].clone(), - destination_spl_token_account: accounts[2].clone(), - amount: data.amount, - authority: accounts[3].clone(), - mint: accounts[4].clone(), - payer: accounts[5].clone(), - token_pool_pda: accounts[6].clone(), - token_pool_pda_bump: data.token_pool_pda_bump, - spl_token_program: accounts[7].clone(), - compressed_token_program_authority: accounts[8].clone(), - } - .invoke()?; - - Ok(()) -} - -/// Handler for transferring cToken to SPL tokens with PDA authority (invoke_signed) -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_ctoken_account -/// - accounts[2]: destination_spl_token_account -/// - accounts[3]: authority (PDA, not signer - program signs) -/// - accounts[4]: mint -/// - accounts[5]: payer (signer) -/// - accounts[6]: token_pool_pda -/// - accounts[7]: spl_token_program -/// - accounts[8]: compressed_token_program_authority -pub fn process_ctoken_to_spl_invoke_signed( - accounts: &[AccountInfo], - data: TransferCtokenToSplData, -) -> Result<(), ProgramError> { - if accounts.len() < 9 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - let (authority_pda, authority_bump) = - Pubkey::find_program_address(&[TRANSFER_AUTHORITY_SEED], &ID); - - if &authority_pda != accounts[3].key { - return Err(ProgramError::InvalidSeeds); - } - - let account_infos = TransferCtokenToSplAccountInfos { - source_ctoken_account: accounts[1].clone(), - destination_spl_token_account: accounts[2].clone(), - amount: data.amount, - authority: accounts[3].clone(), - mint: accounts[4].clone(), - payer: accounts[5].clone(), - token_pool_pda: accounts[6].clone(), - token_pool_pda_bump: data.token_pool_pda_bump, - spl_token_program: accounts[7].clone(), - compressed_token_program_authority: accounts[8].clone(), - }; - - let authority_seeds: &[&[u8]] = &[TRANSFER_AUTHORITY_SEED, &[authority_bump]]; - account_infos.invoke_signed(&[authority_seeds])?; - - Ok(()) -} -``` - -# Next Steps - - \ No newline at end of file diff --git a/docs.json b/docs.json index cad0323d..c4c327ce 100644 --- a/docs.json +++ b/docs.json @@ -96,7 +96,7 @@ "compressed-token-program/ctoken/program-guides/program-create-ctoken", "compressed-token-program/ctoken/program-guides/program-create-cATA", "compressed-token-program/ctoken/program-guides/program-mint-to-cToken", - "compressed-token-program/ctoken/program-guides/program-transfer-ctoken" + "compressed-token-program/ctoken/program-guides/program-transfer-interface" ] } ] @@ -514,6 +514,7 @@ "compressed-token-program/ctoken/program-guides/ctoken-transfer", "compressed-token-program/ctoken/program-guides/SPL-to-ctoken-transfer", "compressed-token-program/ctoken/program-guides/ctoken-to-spl-decompress", + "compressed-token-program/ctoken/program-guides/program-transfer-interface", "compressed-token-program/ctoken/program-guides/compress-and-close", "compressed-token-program/ctoken/program-guides/close-ctoken" ] diff --git a/snippets/transfer-interface-accounts-list-ctoken-1.mdx b/snippets/transfer-interface-accounts-list-ctoken-1.mdx new file mode 100644 index 00000000..ada4349f --- /dev/null +++ b/snippets/transfer-interface-accounts-list-ctoken-1.mdx @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source AccountmutableThe source account (SPL token account or cToken account).
Destination AccountmutableThe destination account (SPL token account or cToken account).
Authoritysigner* + - Owner of the source account.
+ - *Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds. +
Payersigner, mutablePays transaction fees.
Compressed Token Program Authority-The cToken program authority PDA.
diff --git a/snippets/transfer-interface-accounts-list-spl-2.mdx b/snippets/transfer-interface-accounts-list-spl-2.mdx new file mode 100644 index 00000000..95a7bb58 --- /dev/null +++ b/snippets/transfer-interface-accounts-list-spl-2.mdx @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Mint-The SPL token mint.
Token Pool PDAmutableEscrow account for SPL ↔ cToken transfers.
SPL Token Program-The SPL Token program.
diff --git a/snippets/transfer-interface-accounts-list.mdx b/snippets/transfer-interface-accounts-list.mdx new file mode 100644 index 00000000..49fe7395 --- /dev/null +++ b/snippets/transfer-interface-accounts-list.mdx @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source AccountmutableThe source account (SPL token account or cToken account).
Destination AccountmutableThe destination account (SPL token account or cToken account).
Authoritysigner* + - Owner of the source account.
+ - *Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds. +
Payersigner, mutablePays transaction fees.
Compressed Token Program Authority-The cToken program authority PDA.
SPL Interface (for SPL↔cToken transfers)
Mint-The SPL token mint.
Token Pool PDAmutableHolds SPL tokens for SPL↔cToken transfers.
SPL Token Program-The SPL Token program.
From 7435796f3827d33b522d9bdddff89aed69373ba1 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 00:33:08 +0000 Subject: [PATCH 025/143] Fix typo in close guide and refine transfer interface --- .../ctoken/program-guides/program-close-ctoken.mdx | 4 ++-- .../program-guides/program-transfer-interface.mdx | 2 +- snippets/transfer-interface-accounts-list-spl-2.mdx | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index 927d51dc..ec59cf79 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -7,7 +7,7 @@ description: Program guide to close cToken accounts with step-by-step implementa cToken accounts are closed via CPI to the cToken Program. -1. Closing transfers remaining lamports to a destination account. +1. Closing a cToken accounts transfers remaining lamports to a destination account. 2. The account owner must sign the transaction (or be a PDA with invoke_signed). 3. The rent sponsor can reclaim sponsored rent. @@ -24,7 +24,7 @@ Find [full code examples at the end](#full-code-example). -### cToken Program CPI +### Build Account Infos and CPI the cToken Program Build the close instruction and invoke the cToken Program. diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index d20d6445..32398bd2 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -79,7 +79,7 @@ The token pool is derived from the `mint` pubkey and pool seed. -### CPI to the cToken Program +### CPI the cToken Program Use `invoke` or `invoke_signed`: * When a CPI doesn't require PDA signers, the `invoke` function is used. diff --git a/snippets/transfer-interface-accounts-list-spl-2.mdx b/snippets/transfer-interface-accounts-list-spl-2.mdx index 95a7bb58..8dd50847 100644 --- a/snippets/transfer-interface-accounts-list-spl-2.mdx +++ b/snippets/transfer-interface-accounts-list-spl-2.mdx @@ -16,15 +16,15 @@ - The SPL token mint. - - Token Pool PDA - mutable - Escrow account for SPL ↔ cToken transfers. - SPL Token Program - The SPL Token program. + + Token Pool PDA + mutable + Escrow account for SPL ↔ cToken transfers. + From e382d4781a0dac82977ff2959b75eeb99f9bfc84 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 00:49:03 +0000 Subject: [PATCH 026/143] Refine transfer interface and add close guide to nav --- .../program-guides/program-close-ctoken.mdx | 1 - .../program-guides/program-mint-to-cToken.mdx | 3 ++- .../program-transfer-interface.mdx | 18 +++++++++--------- docs.json | 3 ++- resources/addresses-and-urls.mdx | 8 ++++++-- .../transfer-interface-accounts-list-spl-2.mdx | 4 ++-- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index ec59cf79..e96bcada 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -26,7 +26,6 @@ Find [full code examples at the end](#full-code-example). ### Build Account Infos and CPI the cToken Program -Build the close instruction and invoke the cToken Program. * When a CPI doesn't require PDA signers, the `invoke` function is used. * When a CPI requires a PDA signer, the `invoke_signed` function is used. diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx index cf77710a..7b51e83b 100644 --- a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -50,7 +50,8 @@ The client passes a validity proof that diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index 32398bd2..e88f55d4 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -1,5 +1,5 @@ --- -title: Transfer Interface for cToken <> SPL Token +title: Transfer Interface for cToken <> SPL Tokens description: Program guide for the unified transfer interface that auto-detects account types and routes cToken <> SPL token transfers. --- @@ -11,8 +11,8 @@ import TransferInterfaceAccountsListSpl2 from '/snippets/transfer-interface-acco The `TransferInterface` is a unified API that automatically detects account types and routes to the appropriate transfer via CPI to the cToken Program: 1. **cToken → cToken**: Transfer of cTokens between cToken accounts. -2. **cToken → SPL**: Withdraw cTokens to an SPL token account -3. **SPL → cToken**: Deposit SPL tokens to a cToken account +2. **cToken → SPL**: Transfer cTokens to an SPL token account +3. **SPL → cToken**: Transfer SPL tokens to a cToken account Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). @@ -54,7 +54,7 @@ let mut transfer = TransferInterface::new( -### SPL Interface +### SPL Transfer Interface ```rust transfer = transfer.with_spl_interface( @@ -65,12 +65,12 @@ transfer = transfer.with_spl_interface( )?; ``` -The SPL interface requires for SPL ↔ cToken transfers the `token_pool_pda` as escrow account: - * **SPL → cToken**: SPL tokens are transferred locked in this pool, cTokens are minted to cToken accounts - * **cToken → SPL**: cTokens are burned, SPL tokens withdrawn to SPL token accounts - +SPL ↔ cToken transfers require a `token_pool_pda`: + * **SPL → cToken**: SPL tokens are locked by the cToken Program in the PDA, cTokens are minted to cToken accounts + * **cToken → SPL**: cTokens are burned, SPL tokens transferred to SPL token accounts + The token pool is derived from the `mint` pubkey and pool seed. - + diff --git a/docs.json b/docs.json index c4c327ce..72518411 100644 --- a/docs.json +++ b/docs.json @@ -96,7 +96,8 @@ "compressed-token-program/ctoken/program-guides/program-create-ctoken", "compressed-token-program/ctoken/program-guides/program-create-cATA", "compressed-token-program/ctoken/program-guides/program-mint-to-cToken", - "compressed-token-program/ctoken/program-guides/program-transfer-interface" + "compressed-token-program/ctoken/program-guides/program-transfer-interface", + "compressed-token-program/ctoken/program-guides/program-close-ctoken" ] } ] diff --git a/resources/addresses-and-urls.mdx b/resources/addresses-and-urls.mdx index 4518fd00..cdfd4d13 100644 --- a/resources/addresses-and-urls.mdx +++ b/resources/addresses-and-urls.mdx @@ -104,11 +104,15 @@ Find all JSON RPC Methods for ZK Compression [here](/resources/json-rpc-methods)
-## Token Escrow PDA +## Interface PDA + +The account to convert format between: +* compressed token ↔ SPL token +* cToken ↔ SPL token | | | |:-|:-| -| Token Escrow Owner PDA | **GXtd2izAiMJPwMEjfgTRH3d7k9mjn4Jq3JrWFv9gySYy** | +| Interface PDA | **GXtd2izAiMJPwMEjfgTRH3d7k9mjn4Jq3JrWFv9gySYy** | ## Lookup Tables diff --git a/snippets/transfer-interface-accounts-list-spl-2.mdx b/snippets/transfer-interface-accounts-list-spl-2.mdx index 8dd50847..c8a0496f 100644 --- a/snippets/transfer-interface-accounts-list-spl-2.mdx +++ b/snippets/transfer-interface-accounts-list-spl-2.mdx @@ -22,9 +22,9 @@ The SPL Token program. - Token Pool PDA + Interface PDA mutable - Escrow account for SPL ↔ cToken transfers. + Interface PDA for SPL ↔ cToken transfers. From c8694ce1a8b5217dfcff1295398cc23fd3a24b85 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 01:48:51 +0000 Subject: [PATCH 027/143] Add cost calculator and refine program guide navigation --- .../program-guides/program-close-ctoken.mdx | 58 ++++--- .../program-guides/program-create-cATA.mdx | 15 +- .../program-guides/program-create-ctoken.mdx | 16 +- .../program-guides/program-mint-to-cToken.mdx | 17 +- .../program-transfer-interface.mdx | 30 ++-- compressed-tokens/README.mdx | 31 +--- rent.mdx | 4 +- .../close-account-infos-accounts-list.mdx | 46 +++++ snippets/compressible-vs-solana-rent.mdx | 47 ------ snippets/cost-comparison-calculator.jsx | 158 ------------------ snippets/token-account-compressed-vs-spl.jsx | 2 +- snippets/transfer-interface-accounts-list.mdx | 61 ------- 12 files changed, 152 insertions(+), 333 deletions(-) create mode 100644 snippets/close-account-infos-accounts-list.mdx delete mode 100644 snippets/cost-comparison-calculator.jsx delete mode 100644 snippets/transfer-interface-accounts-list.mdx diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index e96bcada..ef0031ae 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -3,12 +3,16 @@ title: Close cToken Accounts description: Program guide to close cToken accounts with step-by-step implementation and full code examples. --- +import CloseAccountInfosAccountsList from '/snippets/close-account-infos-accounts-list.mdx'; + ## Key Points cToken accounts are closed via CPI to the cToken Program. -1. Closing a cToken accounts transfers remaining lamports to a destination account. -2. The account owner must sign the transaction (or be a PDA with invoke_signed). +1. Closing a cToken account transfers remaining lamports to a destination account. +2. cToken accounts are compressible and can be closed: + * by the account owner at any time. + * by the rent authority when account becomes compressible. The account can be reinstatiated with the same state. 3. The rent sponsor can reclaim sponsored rent. @@ -24,43 +28,49 @@ Find [full code examples at the end](#full-code-example). -### Build Account Infos and CPI the cToken Program - - -* When a CPI doesn't require PDA signers, the `invoke` function is used. -* When a CPI requires a PDA signer, the `invoke_signed` function is used. +### Build Account Infos - - +Define the cToken account to close and where remaining lamports should go. ```rust use light_compressed_token_sdk::ctoken::CloseAccountInfos; -CloseAccountInfos { +let close_account_infos = CloseAccountInfos { token_program: token_program.clone(), account: account.clone(), destination: destination.clone(), owner: owner.clone(), - rent_sponsor: Some(rent_sponsor.clone()), // or None -} -.invoke()?; + rent_sponsor: Some(rent_sponsor.clone()), +}; +``` + + + + + + + + + +### CPI the cToken Program + +Use `invoke` or `invoke_signed`: +* When a CPI doesn't require PDA signers, the `invoke` function is used. +* When a CPI requires a PDA signer, the `invoke_signed` function is used. + + + + +```rust +close_account_infos.invoke()?; ``` ```rust -use light_compressed_token_sdk::ctoken::CloseAccountInfos; - let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; -CloseAccountInfos { - token_program: token_program.clone(), - account: account.clone(), - destination: destination.clone(), - owner: owner.clone(), - rent_sponsor: Some(rent_sponsor.clone()), // or None -} -.invoke_signed(&[signer_seeds])?; +close_account_infos.invoke_signed(&[signer_seeds])?; ``` @@ -155,6 +165,8 @@ pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<( # Next Steps +Learn how to use the transfer interface for cToken ↔ SPL transfers. + + + diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 6c10e576..6c773cb6 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -209,11 +209,21 @@ pub fn process_create_token_account_invoke_signed( # Next Steps +Learn how to mint cTokens to cToken accounts. + + - + + diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx index 7b51e83b..31a4f1a9 100644 --- a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -299,10 +299,21 @@ pub fn process_mint_to_ctoken_invoke_signed( # Next Steps +Learn how to use the transfer interface for cToken ↔ SPL transfers. + + + \ No newline at end of file +/> + diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index e88f55d4..3e62e13f 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -1,6 +1,6 @@ --- title: Transfer Interface for cToken <> SPL Tokens -description: Program guide for the unified transfer interface that auto-detects account types and routes cToken <> SPL token transfers. +description: Program guide for the unified transfer interface for cToken <> SPL token transfers and to migrate SPL to cTokens. --- import TransferInterfaceAccountsListCtoken1 from '/snippets/transfer-interface-accounts-list-ctoken-1.mdx'; @@ -8,11 +8,11 @@ import TransferInterfaceAccountsListSpl2 from '/snippets/transfer-interface-acco ## Key Points -The `TransferInterface` is a unified API that automatically detects account types and routes to the appropriate transfer via CPI to the cToken Program: +The `TransferInterface` is a unified API that detects account types and routes to the appropriate transfer via CPI to the cToken Program: 1. **cToken → cToken**: Transfer of cTokens between cToken accounts. 2. **cToken → SPL**: Transfer cTokens to an SPL token account -3. **SPL → cToken**: Transfer SPL tokens to a cToken account +3. **SPL → cToken**: Transfer SPL tokens to cToken accounts to migrate SPL tokens Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). @@ -29,10 +29,9 @@ Find [full code examples at the end](#full-code-example). ### cToken Transfer Interface - -* Define the number of cTokens / SPL tokens to transfer, -* from which SPL or cToken account to transfer, and -* to which SPL or cToken account to transfer. +Define the number of cTokens / SPL tokens to transfer +- from which SPL or cToken account, and +- to which SPL or cToken account. ```rust use light_compressed_token_sdk::ctoken::TransferInterface; @@ -242,10 +241,21 @@ pub fn process_transfer_interface_invoke_signed( # Next Steps +Learn how to close cToken accounts. + + + \ No newline at end of file +/> + diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx index 91100e68..0bcf93ca 100644 --- a/compressed-tokens/README.mdx +++ b/compressed-tokens/README.mdx @@ -7,6 +7,7 @@ import GuidesTable from '/snippets/compressed-tokens-guides-table.mdx'; import AdvancedGuidesTable from '/snippets/compressed-tokens-advanced-guides-table.mdx'; import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; +import { TokenAccountCompressedVsSpl } from '/snippets/token-account-compressed-vs-spl.jsx'; @@ -34,33 +35,17 @@ Compressed token accounts store token balance, owner, and other information like ## Recommended Usage of Compressed Tokens - - #### [Token Distribution](#advanced-guides) - * Distribute tokens without paying up front rent per recipient. - * Cost reduction for airdrops, payments, rewards, etc: -
- - - - - - - - - - - - - - -
SPL TokenCompressed Token
100,000 Token Accounts~ 200 SOL~ 0.004 SOL
-
#### Storage of Inactive Token Accounts * Most (associated) token accounts are not frequently written to. * **Store token accounts rent-free** when inactive - * Use or migrate to [cTokens with compressible extension](/compressed-token-program/ctoken/ctoken) for **automatic compression/decompression** when inactive/active. - * The compressible extension allows users to **only pay rent, when token accounts are active**. + * Use or migrate to [cTokens](/compressed-token-program/ctoken/ctoken) for **sponsored rent-exemption**. + + + #### [Token Distribution](#advanced-guides) + * Distribute tokens without paying up front rent per recipient. + * Cost reduction for airdrops, payments, rewards, etc: + Leading **wallets** like Phantom and Backpack **support compressed tokens**. The UI does not distinguish between SPL and compressed tokens. diff --git a/rent.mdx b/rent.mdx index 1f12c947..bd42a369 100644 --- a/rent.mdx +++ b/rent.mdx @@ -5,7 +5,7 @@ description: Calculate rent for Solana vs Compressed Accounts. import { SolanaRentCalculator } from '/snippets/solana-rent-calculator.jsx'; import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; -import { CostComparisonCalculator } from '/snippets/cost-comparison-calculator.jsx'; +import { TokenAccountCompressedVsSpl } from '/snippets/token-account-compressed-vs-spl.jsx'; import { BreakEvenCalculator } from '/snippets/break-even-calculator.jsx'; ## Solana Rent @@ -35,7 +35,7 @@ rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) ## Cost Comparison - + ## Break-Even Analysis diff --git a/snippets/close-account-infos-accounts-list.mdx b/snippets/close-account-infos-accounts-list.mdx new file mode 100644 index 00000000..3ca98eb6 --- /dev/null +++ b/snippets/close-account-infos-accounts-list.mdx @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Token Program-The cToken program for CPI.
AccountmutableThe cToken account to close.
DestinationmutableReceives remaining lamports from the closed account.
Ownersigner* + - Owner of the cToken account.
+ - *Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds. +
Rent Sponsormutable, optional + * cToken program PDA that fronts rent exemption at creation. + * Claims rent when account compresses and/or is closed. +
diff --git a/snippets/compressible-vs-solana-rent.mdx b/snippets/compressible-vs-solana-rent.mdx index 3ee9ba7a..50f52bdb 100644 --- a/snippets/compressible-vs-solana-rent.mdx +++ b/snippets/compressible-vs-solana-rent.mdx @@ -93,50 +93,3 @@ The **transaction payer tops-up** the account with rent **when the account's lam Top-ups are additional to standard Solana transaction fees (typically <10,000 lamports). - -### Closing compressible accounts - -Compressible accounts can be closed by two parties. How rent and lamports balance are distributed depends on which party closes the account. - -1. The **Account Owner** can close the account at any time, regardless of rent status. The account is not compressed in this case. - - - - - - - - - - - - - - - - - - -
Component and AmountRecipient
Rent exemption + completed epoch rentRent sponsor
Remaining lamports from top-up (partial epoch)Account Owner
- -2. The **Compression Authority** can compress and close the account when it becomes compressible - - - - - - - - - - - - - - - - - - -
Component and AmountRecipient
Rent exemption + remaining epoch rent
Rent sponsor
Compression incentive
(11,000 lamports)
Forester node
- diff --git a/snippets/cost-comparison-calculator.jsx b/snippets/cost-comparison-calculator.jsx deleted file mode 100644 index 688dd99f..00000000 --- a/snippets/cost-comparison-calculator.jsx +++ /dev/null @@ -1,158 +0,0 @@ -export const CostComparisonCalculator = () => { - const [numAccounts, setNumAccounts] = useState(10000); - const [dataLen, setDataLen] = useState(165); - - const ACCOUNT_STORAGE_OVERHEAD = 128; - const LAMPORTS_PER_BYTE = 6960; - const COMPRESSED_COST_PER_ACCOUNT = 10300; - const LAMPORTS_PER_SOL = 1_000_000_000; - - const solanaCost = numAccounts * (ACCOUNT_STORAGE_OVERHEAD + dataLen) * LAMPORTS_PER_BYTE; - const compressedCost = numAccounts * COMPRESSED_COST_PER_ACCOUNT; - const savings = solanaCost - compressedCost; - const savingsPercent = ((savings / solanaCost) * 100).toFixed(1); - - const handleAccountsChange = (value) => { - const num = Math.max(1, Math.min(1000000, Number.parseInt(value) || 1)); - setNumAccounts(num); - }; - - const handleDataLenChange = (value) => { - const num = Math.max(0, Math.min(10000, Number.parseInt(value) || 0)); - setDataLen(num); - }; - - const formatSOL = (lamports) => { - const sol = lamports / LAMPORTS_PER_SOL; - if (sol >= 1000) { - return sol.toLocaleString(undefined, { maximumFractionDigits: 2 }); - } else if (sol >= 1) { - return sol.toFixed(4); - } - return sol.toFixed(6); - }; - - return ( -
-
- {/* Inputs */} -
-
- - Number of Accounts - handleAccountsChange(e.target.value)} - className="w-28 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" - /> - - setNumAccounts(Number.parseInt(e.target.value))} - className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" - /> -
- -
- - Data Length per Account - - handleDataLenChange(e.target.value)} - className="w-20 px-2 py-1 text-right font-mono font-medium bg-black/[0.015] dark:bg-white/10 border border-black/[0.04] dark:border-white/20 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" - /> - bytes - - - setDataLen(Number.parseInt(e.target.value))} - className="w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm" - /> -
- - {/* Preset buttons */} -
- - - -
-
- - {/* Result Cards */} -
-
-
Solana Cost
-
- {formatSOL(solanaCost)} -
-
SOL
-
- -
-
Compressed Cost
-
- {formatSOL(compressedCost)} -
-
SOL
-
- -
-
Savings
-
- {savingsPercent}% -
-
{formatSOL(savings)} SOL
-
-
- - {/* Formula reference */} -
- Solana: {numAccounts.toLocaleString()} × ({ACCOUNT_STORAGE_OVERHEAD} + {dataLen}) × {LAMPORTS_PER_BYTE.toLocaleString()} = {solanaCost.toLocaleString()} lamports
- Compressed: {numAccounts.toLocaleString()} × {COMPRESSED_COST_PER_ACCOUNT.toLocaleString()} = {compressedCost.toLocaleString()} lamports -
-
-
- ); -}; diff --git a/snippets/token-account-compressed-vs-spl.jsx b/snippets/token-account-compressed-vs-spl.jsx index 2798bedd..00a8c190 100644 --- a/snippets/token-account-compressed-vs-spl.jsx +++ b/snippets/token-account-compressed-vs-spl.jsx @@ -136,4 +136,4 @@ export const TokenAccountCompressedVsSpl = () => {
); -}; \ No newline at end of file +}; diff --git a/snippets/transfer-interface-accounts-list.mdx b/snippets/transfer-interface-accounts-list.mdx deleted file mode 100644 index 49fe7395..00000000 --- a/snippets/transfer-interface-accounts-list.mdx +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source AccountmutableThe source account (SPL token account or cToken account).
Destination AccountmutableThe destination account (SPL token account or cToken account).
Authoritysigner* - - Owner of the source account.
- - *Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds. -
Payersigner, mutablePays transaction fees.
Compressed Token Program Authority-The cToken program authority PDA.
SPL Interface (for SPL↔cToken transfers)
Mint-The SPL token mint.
Token Pool PDAmutableHolds SPL tokens for SPL↔cToken transfers.
SPL Token Program-The SPL Token program.
From 0d9ef0a7462e13e383b0f7c0f5a123839576311b Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 01:57:47 +0000 Subject: [PATCH 028/143] Fix compressed token guide paths in navigation --- docs.json | 64 +++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/docs.json b/docs.json index 72518411..11781161 100644 --- a/docs.json +++ b/docs.json @@ -109,15 +109,15 @@ { "group": "Basic Guides", "pages": [ - "compressed-tokens/basic-guides/how-to-create-compressed-token-accounts", - "compressed-tokens/basic-guides/how-to-mint-compressed-tokens", - "compressed-tokens/basic-guides/how-to-transfer-compressed-token", - "compressed-tokens/basic-guides/how-to-compress-and-decompress-spl-tokens", - "compressed-tokens/basic-guides/how-to-compress-complete-spl-token-accounts", - "compressed-tokens/basic-guides/how-to-create-and-register-a-mint-account-for-compression", - "compressed-tokens/basic-guides/how-to-create-compressed-token-pools-for-mint-accounts", - "compressed-tokens/basic-guides/how-to-merge-compressed-token-accounts", - "compressed-tokens/basic-guides/how-to-approve-and-revoke-delegate-authority" + "compressed-tokens/guides/how-to-create-compressed-token-accounts", + "compressed-tokens/guides/how-to-mint-compressed-tokens", + "compressed-tokens/guides/how-to-transfer-compressed-token", + "compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens", + "compressed-tokens/guides/how-to-compress-complete-spl-token-accounts", + "compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression", + "compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts", + "compressed-tokens/guides/how-to-merge-compressed-token-accounts", + "compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority" ] }, { @@ -127,19 +127,19 @@ "group": "Integration", "expanded": true, "pages": [ - "compressed-token-program/compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", - "compressed-token-program/compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", - "compressed-token-program/compressed-tokens/advanced-guides/use-token-2022-with-compression" + "compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", + "compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", + "compressed-tokens/advanced-guides/use-token-2022-with-compression" ] }, { "group": "Examples", "expanded": true, "pages": [ - "compressed-token-program/compressed-tokens/advanced-guides/create-an-airdrop", - "compressed-token-program/compressed-tokens/advanced-guides/create-an-airdrop-with-claim", - "compressed-token-program/compressed-tokens/advanced-guides/example-web-client", - "compressed-token-program/compressed-tokens/advanced-guides/example-node-js" + "compressed-tokens/advanced-guides/create-an-airdrop", + "compressed-tokens/advanced-guides/create-an-airdrop-with-claim", + "compressed-tokens/advanced-guides/example-web-client", + "compressed-tokens/advanced-guides/example-node-js" ] } ] @@ -529,15 +529,15 @@ { "group": "Basic Guides", "pages": [ - "compressed-token-program/compressed-tokens/basic-guides/how-to-create-compressed-token-accounts", - "compressed-token-program/compressed-tokens/basic-guides/how-to-mint-compressed-tokens", - "compressed-token-program/compressed-tokens/basic-guides/how-to-transfer-compressed-token", - "compressed-token-program/compressed-tokens/basic-guides/how-to-compress-and-decompress-spl-tokens", - "compressed-token-program/compressed-tokens/basic-guides/how-to-compress-complete-spl-token-accounts", - "compressed-token-program/compressed-tokens/basic-guides/how-to-create-and-register-a-mint-account-for-compression", - "compressed-token-program/compressed-tokens/basic-guides/how-to-create-compressed-token-pools-for-mint-accounts", - "compressed-token-program/compressed-tokens/basic-guides/how-to-merge-compressed-token-accounts", - "compressed-token-program/compressed-tokens/basic-guides/how-to-approve-and-revoke-delegate-authority" + "compressed-tokens/guides/how-to-create-compressed-token-accounts", + "compressed-tokens/guides/how-to-mint-compressed-tokens", + "compressed-tokens/guides/how-to-transfer-compressed-token", + "compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens", + "compressed-tokens/guides/how-to-compress-complete-spl-token-accounts", + "compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression", + "compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts", + "compressed-tokens/guides/how-to-merge-compressed-token-accounts", + "compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority" ] }, { @@ -547,19 +547,19 @@ "group": "Integration", "expanded": true, "pages": [ - "compressed-token-program/compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", - "compressed-token-program/compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", - "compressed-token-program/compressed-tokens/advanced-guides/use-token-2022-with-compression" + "compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", + "compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", + "compressed-tokens/advanced-guides/use-token-2022-with-compression" ] }, { "group": "Examples", "expanded": true, "pages": [ - "compressed-token-program/compressed-tokens/advanced-guides/create-an-airdrop", - "compressed-token-program/compressed-tokens/advanced-guides/create-an-airdrop-with-claim", - "compressed-token-program/compressed-tokens/advanced-guides/example-web-client", - "compressed-token-program/compressed-tokens/advanced-guides/example-node-js" + "compressed-tokens/advanced-guides/create-an-airdrop", + "compressed-tokens/advanced-guides/create-an-airdrop-with-claim", + "compressed-tokens/advanced-guides/example-web-client", + "compressed-tokens/advanced-guides/example-node-js" ] } ] From 14ca5a661e7d584f931376bb78e8df1449c11e95 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 02:32:43 +0000 Subject: [PATCH 029/143] Refine program guide structure and navigation links --- .../program-guides/program-close-ctoken.mdx | 50 +++++++++---------- .../program-guides/program-create-cATA.mdx | 11 ++-- .../program-guides/program-create-ctoken.mdx | 11 ++-- .../program-guides/program-mint-to-cToken.mdx | 9 ++-- .../program-transfer-interface.mdx | 38 ++------------ snippets/ctoken-configure-rent.mdx | 30 +++++------ 6 files changed, 50 insertions(+), 99 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index ef0031ae..aac0ec7e 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -28,47 +28,41 @@ Find [full code examples at the end](#full-code-example). -### Build Account Infos +### Build Account Infos and CPI the cToken Program -Define the cToken account to close and where remaining lamports should go. +1. Define the cToken account to close and where remaining lamports should go +2. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. + + + ```rust use light_compressed_token_sdk::ctoken::CloseAccountInfos; -let close_account_infos = CloseAccountInfos { +CloseAccountInfos { token_program: token_program.clone(), account: account.clone(), destination: destination.clone(), owner: owner.clone(), rent_sponsor: Some(rent_sponsor.clone()), -}; -``` - - - - - - - - - -### CPI the cToken Program - -Use `invoke` or `invoke_signed`: -* When a CPI doesn't require PDA signers, the `invoke` function is used. -* When a CPI requires a PDA signer, the `invoke_signed` function is used. - - - - -```rust -close_account_infos.invoke()?; +} +.invoke()?; ``` ```rust +use light_compressed_token_sdk::ctoken::CloseAccountInfos; + +let close_account_infos = CloseAccountInfos { + token_program: token_program.clone(), + account: account.clone(), + destination: destination.clone(), + owner: owner.clone(), + rent_sponsor: Some(rent_sponsor.clone()), +}; + let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; close_account_infos.invoke_signed(&[signer_seeds])?; ``` @@ -76,6 +70,10 @@ close_account_infos.invoke_signed(&[signer_seeds])?; + + + + @@ -171,6 +169,6 @@ Learn how to use the transfer interface for cToken ↔ SPL transfers. title="Learn how to transfer between SPL and cTokens" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/program-guides/program-transfer-spl-ctoken" + href="/compressed-token-program/ctoken/program-guides/program-transfer-interface" horizontal /> \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index a84281b5..6fbe4a03 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -48,10 +48,7 @@ Find [full code examples at the end](#full-code-example). 1. Pass the required accounts 2. Include rent config from `compressible_params` -3. Use `invoke` or `invoke_signed`: - * When a CPI doesn't require PDA signers, the `invoke` function is used. - * When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. - +3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. The cATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike cToken accounts, owner and mint are passed as accounts, not in instruction data. @@ -74,7 +71,6 @@ CreateAssociatedTokenAccount2Infos { } .invoke()?; ``` - @@ -98,6 +94,7 @@ CreateAssociatedTokenAccount2Infos { + @@ -225,14 +222,14 @@ Learn how to mint cTokens to cToken accounts. title="Client Guide" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/client-guide" + href="/compressed-token-program/ctoken/client-guides/client-create-cATA" horizontal /> diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 6c773cb6..947f4354 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -36,7 +36,7 @@ Find [full code examples at the end](#full-code-example). -### Rent Config Accounts +### Configure Rent @@ -47,10 +47,7 @@ Find [full code examples at the end](#full-code-example). 1. Pass the required accounts 2. Include rent config from `compressible_params` -3. Use `invoke` or `invoke_signed`: - * When a CPI doesn't require PDA signers, the `invoke` function is used. - * When a CPI requires a PDA signer, the `invoke_signed` function is used. It takes the signer seeds used for deriving signer PDAs. - +3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. @@ -216,14 +213,14 @@ Learn how to mint cTokens to cToken accounts. title="Client Guide" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/client-guide" + href="/compressed-token-program/ctoken/client-guides/client-create-ctoken" horizontal /> diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx index 31a4f1a9..e506e5b4 100644 --- a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -78,10 +78,7 @@ let system_accounts = SystemAccountInfos { 1. Pass the required accounts, including the destination cToken accounts. 2. Include `mint_params` and `system_accounts` from the previous steps -3. Use `invoke` or `invoke_signed`: - * When a CPI doesn't require PDA signers, the `invoke` function is used. - * When a CPI requires a PDA signer, the `invoke_signed` function is used. - +3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. @@ -306,14 +303,14 @@ Learn how to use the transfer interface for cToken ↔ SPL transfers. title="Client Guide" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/client-guide" + href="/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken" horizontal /> diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index 3e62e13f..216d96e4 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -78,30 +78,7 @@ The token pool is derived from the `mint` pubkey and pool seed. -### CPI the cToken Program - -Use `invoke` or `invoke_signed`: - * When a CPI doesn't require PDA signers, the `invoke` function is used. - * When a CPI requires a PDA signer, the `invoke_signed` function is used. - - - - -```rust -transfer.invoke()?; -``` - - - - -```rust -let authority_seeds: &[&[u8]] = &[TRANSFER_INTERFACE_AUTHORITY_SEED, &[authority_bump]]; -transfer.invoke_signed(&[authority_seeds])?; -``` - - - - +### CPI the cToken Program @@ -243,19 +220,10 @@ pub fn process_transfer_interface_invoke_signed( Learn how to close cToken accounts. - - - diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index f4d54946..b3ae2cf2 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -1,4 +1,6 @@ -Use `CompressibleParamsInfos` to pass the default rent config and accounts: +Define `CompressibleParamsInfos` with the standard rent config and accounts: +- Rent for 24 hours (~11,000 lamports) +- Top-up amount 766 lamports (3h rent) ```rust use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; @@ -10,25 +12,8 @@ let compressible_params = CompressibleParamsInfos::new( ); ``` -#### Default Rent Config - -You don't need to pass the rent config in this struct, the SDK sets default values for the rent config of compressible accounts: -- Initial rent payment to 24 hours / ~16 epochs (~11,000 lamports) -- Top-up amount to 766 lamports to cover rent for 3h / ~2 epochs - -We recommend to use default values, but you can customize the parameters based on the expected account activity. -Learn the [core concepts of the rent config here](/compressed-token-program/rent-config). - - - -1. Set the hours to determine the amount of prepaid rent -2. Set the lamports per write a transaction payer will pay for top ups - - - #### Accounts -The rent config requires the following accounts: @@ -60,3 +45,12 @@ The rent config requires the following accounts:
+ +We recommend to use default values, but you can customize the parameters based on the expected account activity. + + + +1. Set the hours to determine the amount of prepaid rent +2. Set the lamports per write a transaction payer will pay for top ups + + \ No newline at end of file From a0e0f147e4fb107782011445788c5c6979268ed8 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 16:59:49 +0000 Subject: [PATCH 030/143] Refine rent config docs and clarify compressible mechanics --- .../program-guides/program-create-ctoken.mdx | 15 ++++++++------- .../program-guides/program-mint-to-cToken.mdx | 9 ++++----- snippets/ctoken-configure-rent.mdx | 18 ++++++++++++++++-- snippets/ctoken-create-accounts-list.mdx | 2 +- snippets/ctoken-create-ata-accounts-list.mdx | 2 +- 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 947f4354..c6bc826f 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -14,14 +14,15 @@ import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; cToken accounts are Solana accounts and created via CPI to the cToken Program. 1. cToken accounts hold token balances similar to SPL Token accounts. -2. cToken accounts are compressible, which means +2. cToken accounts are compressible, which means you + pay rent per rent-epoch, instead of the rent-exemption upfront. * The rent-exemption to create the Solana account is sponsored by Light Protocol. - * At creation, the account creator pays 24h / ~16 epochs worth of rent by default (~11,000 lamports). - * The account gets compressed when the lamports balance does not cover rent for the current epoch (388 lamports) and the protocol reclaims the rent. - * When a compressed cToken account is written to, the account gets decompressed with the same state. -3. To continously fund the account with rent, the transaction payer tops-up the lamports balance, when the account's lamports balance falls below two epochs of rent (766 lamports). - * The top-up is configured to 766 lamports by default to cover rent for another ~3h / 2 epochs. -4. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. + * Rent for 24h (16 epochs) is 11,000 lamports and paid at account creation. +3. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). + * The top-up is configured to 766 lamports by default (rent for ~3h / 2 epochs). +4. Inactive accounts are compressed when the lamports balance does not cover rent for the current epoch (388 lamports) and the protocol reclaims the rent. + * The compressed cToken account gets reinstantiated with the same state (decompressed), when written to. +5. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx index e506e5b4..5493d20d 100644 --- a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -9,13 +9,12 @@ import CMintToCTokenAccountsList from '/snippets/cmint-to-ctoken-accounts-list.m ## Key Points -cTokens are minted via CPI to the Compressed Token Program and do not require rent-exemption. - -1. Mint cTokens from an existing cMint to existing cToken accounts. -2. The mint authority must sign the transaction (or be a PDA with `invoke_signed`). +cTokens are minted via CPI to the Compressed Token Program and increase the supply of a cMint. +1. The destination cToken accounts must exist to receive the minted cTokens. +2. Only the mint authority can mint new cTokens. -* `MintToCToken` mints from a cMint to cToken Solana accounts. Use when you need SPL-compatible accounts for interoperability with DeFi, etc. +* `MintToCToken` mints from a cMint to cToken Solana accounts. Use when you need Solana accounts for interoperability with DeFi, etc. * `MintToCompressed` mints from a cMint to compressed token accounts. Use for token distribution (Airdrops, payments, ...). diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index b3ae2cf2..4ee5c8ce 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -1,6 +1,6 @@ Define `CompressibleParamsInfos` with the standard rent config and accounts: - Rent for 24 hours (~11,000 lamports) -- Top-up amount 766 lamports (3h rent) +- Top-up amount to 766 lamports (3h rent) ```rust use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; @@ -48,9 +48,23 @@ let compressible_params = CompressibleParamsInfos::new( We recommend to use default values, but you can customize the parameters based on the expected account activity. + + - 1. Set the hours to determine the amount of prepaid rent 2. Set the lamports per write a transaction payer will pay for top ups + +```rust +CompressibleParamsInfos{ + compressible_config, + rent_sponsor, + system_program, + pre_pay_num_epochs: 35, + lamports_per_write: Some(788), + compress_to_account_pubkey: None, + token_account_version: TokenDataVersion::ShaFlat, + } +``` + \ No newline at end of file diff --git a/snippets/ctoken-create-accounts-list.mdx b/snippets/ctoken-create-accounts-list.mdx index 701ade8b..6e7f1eee 100644 --- a/snippets/ctoken-create-accounts-list.mdx +++ b/snippets/ctoken-create-accounts-list.mdx @@ -15,7 +15,7 @@ Payer signer, mutable - - Pays transaction fee and compression incentive (prepaid epochs).
+ - Pays initial rent per epoch, transaction fee and compression incentive.
- Does NOT pay rent exemption (fronted by `rent_sponsor`). diff --git a/snippets/ctoken-create-ata-accounts-list.mdx b/snippets/ctoken-create-ata-accounts-list.mdx index b6392116..db4936c0 100644 --- a/snippets/ctoken-create-ata-accounts-list.mdx +++ b/snippets/ctoken-create-ata-accounts-list.mdx @@ -31,7 +31,7 @@ Payer signer, mutable - - Pays transaction fee and compression incentive (prepaid epochs).
+ - Pays initial rent per epoch, transaction fee and compression incentive.
- Does NOT pay rent exemption (fronted by `rent_sponsor`). From ad08eda73d8ee2b7f2435a3a9cc60e4ac5c0961c Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 17:00:25 +0000 Subject: [PATCH 031/143] Apply compressible rent mechanics to cATA guide --- .../ctoken/program-guides/program-create-cATA.mdx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index 6fbe4a03..a55ef6e8 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -14,14 +14,15 @@ import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; Associated cToken accounts (cATA) are Solana accounts and created via CPI to the cToken Program. 1. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -2. cATAs are compressible, which means +2. cATAs accounts are compressible, which means you + pay rent per rent-epoch, instead of the rent-exemption upfront. * The rent-exemption to create the Solana account is sponsored by Light Protocol. - * At creation, the account creator pays 24h / ~16 epochs worth of rent by default (~11,000 lamports). - * The account gets compressed when the lamports balance does not cover rent for the current epoch (388 lamports) and the protocol reclaims the rent. - * When a compressed cATA is written to, the account gets decompressed with the same state. -3. To continously fund the account with rent, the transaction payer tops-up the lamports balance, when the account's lamports balance falls below two epochs of rent (766 lamports). - * The top-up is configured to 766 lamports by default to cover rent for another ~3h / 2 epochs. -4. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. + * Rent for 24h (16 epochs) is 11,000 lamports and paid at account creation. +3. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). + * The top-up is configured to 766 lamports by default (rent for ~3h / 2 epochs). +4. Inactive accounts are compressed when the lamports balance does not cover rent for the current epoch (388 lamports) and the protocol reclaims the rent. + * The compressed cToken account gets reinstantiated with the same state (decompressed), when written to. +5. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). From 605d8f8573b43abd98ef016c28c79608e0ab30c5 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 17:02:54 +0000 Subject: [PATCH 032/143] Remove redundant tip from mint-to-cToken guide --- .../ctoken/program-guides/program-mint-to-cToken.mdx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx index 5493d20d..0e4cf610 100644 --- a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -13,11 +13,6 @@ cTokens are minted via CPI to the Compressed Token Program and increase the supp 1. The destination cToken accounts must exist to receive the minted cTokens. 2. Only the mint authority can mint new cTokens. - -* `MintToCToken` mints from a cMint to cToken Solana accounts. Use when you need Solana accounts for interoperability with DeFi, etc. -* `MintToCompressed` mints from a cMint to compressed token accounts. Use for token distribution (Airdrops, payments, ...). - - Find [full code examples at the end](#full-code-example). From 026f23f9e0e34050c27d8234c45d6542d03b8edf Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 19:00:48 +0000 Subject: [PATCH 033/143] Clarify compressible rent mechanics in cToken program guides - Restructure close-ctoken guide to explain compression authority and account compression flow - Expand create-ctoken guide with detailed rent mechanics including epochs and lamport costs - Remove redundant tip from mint-to-cToken guide already covered in previous commit --- .../program-guides/program-close-ctoken.mdx | 8 ++++---- .../program-guides/program-create-ctoken.mdx | 16 +++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index aac0ec7e..d90343c1 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -9,11 +9,11 @@ import CloseAccountInfosAccountsList from '/snippets/close-account-infos-account cToken accounts are closed via CPI to the cToken Program. -1. Closing a cToken account transfers remaining lamports to a destination account. -2. cToken accounts are compressible and can be closed: +1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. +2. cToken accounts can be closed * by the account owner at any time. - * by the rent authority when account becomes compressible. The account can be reinstatiated with the same state. -3. The rent sponsor can reclaim sponsored rent. + * by the `compression_authority` when the account becomes compressible. + The account is compressed and closed - it can be reinstated with the same state (decompressed). Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index c6bc826f..bd0febf8 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -14,14 +14,16 @@ import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; cToken accounts are Solana accounts and created via CPI to the cToken Program. 1. cToken accounts hold token balances similar to SPL Token accounts. -2. cToken accounts are compressible, which means you - pay rent per rent-epoch, instead of the rent-exemption upfront. - * The rent-exemption to create the Solana account is sponsored by Light Protocol. - * Rent for 24h (16 epochs) is 11,000 lamports and paid at account creation. -3. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). +2. cToken accounts are compressible, which means + * the **rent-exemption** for the Solana account is **sponsored** by Light Protocol, + * you pay **rent per rent-epoch** **instead** of the rent-exemption upfront, + * accounts with rent for less than 1 epoch are compressed (360 lamports), and + * compressed cToken accounts are decompressed with the same state when written to. + +3. **Rent for 24h** (16 epochs) is **11,000 lamports** and paid at account creation. +4. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). * The top-up is configured to 766 lamports by default (rent for ~3h / 2 epochs). -4. Inactive accounts are compressed when the lamports balance does not cover rent for the current epoch (388 lamports) and the protocol reclaims the rent. - * The compressed cToken account gets reinstantiated with the same state (decompressed), when written to. + * Write transactions to compressed cToken accounts decompress the account. 5. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. From f8c63c14ed616f69fde027e6d898688cf491131c Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 19:06:47 +0000 Subject: [PATCH 034/143] Add compressed account storage explanation to close-ctoken guide - Add tooltip defining compressible threshold (< 1 epoch rent) - Include conceptual note about compressed accounts as inactive storage - Clarify compression authority role and decompression behavior --- .../ctoken/program-guides/program-close-ctoken.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index d90343c1..a43c8f9d 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -12,9 +12,9 @@ cToken accounts are closed via CPI to the cToken Program. 1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. 2. cToken accounts can be closed * by the account owner at any time. - * by the `compression_authority` when the account becomes compressible. - The account is compressed and closed - it can be reinstated with the same state (decompressed). - + * by the `compression_authority` + when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). + * You can think of compressed cToken accounts as storage of inactive accounts that can be reactivated. Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). From 59239f986bf8b27dcb428d9b8875bc3713f9460b Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 19:32:04 +0000 Subject: [PATCH 035/143] Improve navigation and structure across cToken program guides - Add tooltips for compressible threshold in close-ctoken guide - Standardize next steps navigation with CardGroups - Fix card titles and descriptions for consistency - Restructure compressed tokens README with Steps layout - Clarify SPL transfer interface as optional --- .../program-guides/program-close-ctoken.mdx | 25 +++++++++++++----- .../program-guides/program-create-cATA.mdx | 26 +++++++++++-------- .../program-guides/program-create-ctoken.mdx | 12 +++++---- .../program-guides/program-mint-to-cToken.mdx | 14 +++++----- .../program-transfer-interface.mdx | 22 ++++++++++++---- compressed-tokens/README.mdx | 25 ++++++++++++------ 6 files changed, 82 insertions(+), 42 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index a43c8f9d..5bde8b7f 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -13,8 +13,8 @@ cToken accounts are closed via CPI to the cToken Program. 2. cToken accounts can be closed * by the account owner at any time. * by the `compression_authority` - when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). - * You can think of compressed cToken accounts as storage of inactive accounts that can be reactivated. + when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). + Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). @@ -163,12 +163,23 @@ pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<( # Next Steps -Learn how to use the transfer interface for cToken ↔ SPL transfers. - + + + Close cToken Accounts + \ No newline at end of file +> + Learn about compressed tokens + + diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index a55ef6e8..06e54850 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -14,14 +14,16 @@ import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; Associated cToken accounts (cATA) are Solana accounts and created via CPI to the cToken Program. 1. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -2. cATAs accounts are compressible, which means you - pay rent per rent-epoch, instead of the rent-exemption upfront. - * The rent-exemption to create the Solana account is sponsored by Light Protocol. - * Rent for 24h (16 epochs) is 11,000 lamports and paid at account creation. -3. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). +2. cATAs accounts are compressible, which means + * the **rent-exemption** for the Solana account is **sponsored** by Light Protocol, + * you pay **rent per rent-epoch** **instead** of the rent-exemption upfront, + * accounts with rent for less than 1 epoch are compressed (360 lamports), and + * compressed cToken accounts are decompressed with the same state when written to. + +3. **Rent for 24h** (16 epochs) is **11,000 lamports** and paid at account creation. +4. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). * The top-up is configured to 766 lamports by default (rent for ~3h / 2 epochs). -4. Inactive accounts are compressed when the lamports balance does not cover rent for the current epoch (388 lamports) and the protocol reclaims the rent. - * The compressed cToken account gets reinstantiated with the same state (decompressed), when written to. + * Write transactions to compressed cToken accounts decompress the account. 5. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. @@ -216,8 +218,6 @@ pub fn process_create_ata2_invoke_signed( # Next Steps -Learn how to mint cTokens to cToken accounts. - +> + Create Associated cToken Accounts + +> + Mint cTokens + diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index bd0febf8..5ea64c0e 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -209,8 +209,6 @@ pub fn process_create_token_account_invoke_signed( # Next Steps -Learn how to mint cTokens to cToken accounts. - +> + Create cToken Accounts + - +> + Mint cTokens +
+ \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx index 0e4cf610..c38e5a3d 100644 --- a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx @@ -290,8 +290,6 @@ pub fn process_mint_to_ctoken_invoke_signed( # Next Steps -Learn how to use the transfer interface for cToken ↔ SPL transfers. - +> + Mint cTokens + - +> + Transfer Interface cToken + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index 216d96e4..ec77a94e 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -53,8 +53,9 @@ let mut transfer = TransferInterface::new( -### SPL Transfer Interface +### SPL Transfer Interface (Optional) +The SPL transfer interface is only needed for SPL ↔ cToken transfers. ```rust transfer = transfer.with_spl_interface( Some(mint.clone()), @@ -218,12 +219,23 @@ pub fn process_transfer_interface_invoke_signed( # Next Steps -Learn how to close cToken accounts. - + + + Transfer Interface + +> + Close cToken Accounts + + \ No newline at end of file diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx index 0bcf93ca..e20c66a7 100644 --- a/compressed-tokens/README.mdx +++ b/compressed-tokens/README.mdx @@ -39,7 +39,7 @@ Compressed token accounts store token balance, owner, and other information like #### Storage of Inactive Token Accounts * Most (associated) token accounts are not frequently written to. * **Store token accounts rent-free** when inactive - * Use or migrate to [cTokens](/compressed-token-program/ctoken/ctoken) for **sponsored rent-exemption**. + * [cTokens](/compressed-token-program/ctoken/ctoken) are automatically compressed/decompressed, when active/inactive and include **sponsored rent-exemption**. [Learn more here](/compressed-token-program/ctoken/ctoken). #### [Token Distribution](#advanced-guides) @@ -50,7 +50,11 @@ Compressed token accounts store token balance, owner, and other information like Leading **wallets** like Phantom and Backpack **support compressed tokens**. The UI does not distinguish between SPL and compressed tokens. - + + + +### Get Started + @@ -67,15 +71,20 @@ Leading **wallets** like Phantom and Backpack **support compressed tokens**. The - - -### Get started - + -## Basic Guides + + + +### Basic Guides + + ## Advanced Guides - \ No newline at end of file + + + + \ No newline at end of file From cb814c6121efd0340f88e8e166774f0ec282113f Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 1 Dec 2025 20:17:25 +0000 Subject: [PATCH 036/143] Add client guides for transfer and close operations - Add client-transfer-interface.mdx with implementation examples - Add client-close-ctoken.mdx with account closure examples - Move client-create-cmint.mdx to cmint directory structure - Update client guide titles and descriptions for consistency - Add navigation cards to all client guides - Update docs.json with new client guide entries --- .../client-guides/client-create-cmint.mdx | 2 +- .../client-guides/client-close-ctoken.mdx | 16 +++++++++++ .../client-guides/client-create-cATA.mdx | 27 +++++++++++++++++-- .../client-guides/client-create-ctoken.mdx | 21 ++++++++++++--- .../client-guides/client-mint-to-ctoken.mdx | 27 +++++++++++++++++-- .../client-transfer-interface.mdx | 27 +++++++++++++++++++ docs.json | 4 ++- 7 files changed, 114 insertions(+), 10 deletions(-) rename compressed-token-program/{ctoken => cmint}/client-guides/client-create-cmint.mdx (98%) create mode 100644 compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx create mode 100644 compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx diff --git a/compressed-token-program/ctoken/client-guides/client-create-cmint.mdx b/compressed-token-program/cmint/client-guides/client-create-cmint.mdx similarity index 98% rename from compressed-token-program/ctoken/client-guides/client-create-cmint.mdx rename to compressed-token-program/cmint/client-guides/client-create-cmint.mdx index 91f7143a..cc928775 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-cmint.mdx +++ b/compressed-token-program/cmint/client-guides/client-create-cmint.mdx @@ -1,5 +1,5 @@ --- -title: Create a Compressed Mint +title: Create cMints description: Create a cMint using the cToken SDK. --- diff --git a/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx new file mode 100644 index 00000000..eed901d7 --- /dev/null +++ b/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx @@ -0,0 +1,16 @@ +--- +title: Close cToken Accounts +description: Client guide to close cToken accounts with step-by-step implementation and full code examples. +--- + +# Next Steps + + + Close cToken Accounts + diff --git a/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx b/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx index f85b0847..bc990f21 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx @@ -1,6 +1,6 @@ --- -title: Create a Compressed Token ATA -description: Create an associated cToken account (cATA) using the cToken SDK. +title: Create Associated cToken Accounts +description: Client guide to create associated cToken accounts (cATAs) with step-by-step implementation and full code examples. --- @@ -55,3 +55,26 @@ async fn create_compressed_ata( + +# Next Steps + + + + Create Associated cToken Accounts + + + Create cToken Accounts + + diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx index 4a95427c..4b783506 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx @@ -1,6 +1,6 @@ --- -title: Create a cToken Account -description: Guide to create a cToken account using the cSDK. +title: Create cToken Accounts +description: Client guide to create cToken accounts with step-by-step implementation and full code examples. --- Create a cToken account with a new keypair (non-ATA). @@ -130,10 +130,23 @@ async fn create_ctoken_account( # Next Steps + + Create cToken Accounts + + \ No newline at end of file +> + Mint cTokens + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx index 9630896a..4f0bc3bc 100644 --- a/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx @@ -1,6 +1,6 @@ --- -title: Mint to cToken -description: Mint compressed tokens to cToken ATAs using the cToken SDK. +title: Mint cTokens +description: Client guide to mint compressed tokens to cToken accounts with step-by-step implementation and full code examples. --- @@ -102,3 +102,26 @@ async fn mint_to_ctoken( + +# Next Steps + + + + Mint cTokens + + + Transfer Interface + + diff --git a/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx b/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx new file mode 100644 index 00000000..e080260b --- /dev/null +++ b/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx @@ -0,0 +1,27 @@ +--- +title: Transfer Interface +description: Client guide for the unified transfer interface for cToken <> SPL token transfers with step-by-step implementation and full code examples. +--- + +# Next Steps + + + + Transfer Interface + + + Close cToken Accounts + + diff --git a/docs.json b/docs.json index 11781161..4eecbbbd 100644 --- a/docs.json +++ b/docs.json @@ -87,7 +87,9 @@ "pages": [ "compressed-token-program/ctoken/client-guides/client-create-ctoken", "compressed-token-program/ctoken/client-guides/client-create-cATA", - "compressed-token-program/ctoken/client-guides/client-mint-to-ctoken" + "compressed-token-program/ctoken/client-guides/client-mint-to-ctoken", + "compressed-token-program/ctoken/client-guides/client-transfer-interface", + "compressed-token-program/ctoken/client-guides/client-close-ctoken" ] }, { From 3715f94e8a6e7058e52bcf7ced81187fc2e21645 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Tue, 2 Dec 2025 17:43:32 +0000 Subject: [PATCH 037/143] Refactor cToken account properties into reusable snippet - Extract shared account properties into snippets/ctoken-account-properties.mdx - Update client-create-ctoken.mdx with CreateCTokenAccount builder pattern - Update program-create-ctoken.mdx to import shared snippet - Simplify key points sections across guides for consistency --- compressed-token-program/ctoken/README.mdx | 0 .../client-guides/client-create-ctoken.mdx | 156 +++++++++--------- .../program-guides/program-create-ctoken.mdx | 14 +- docs.json | 4 +- snippets/ctoken-account-properties.mdx | 12 ++ 5 files changed, 91 insertions(+), 95 deletions(-) create mode 100644 compressed-token-program/ctoken/README.mdx create mode 100644 snippets/ctoken-account-properties.mdx diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx new file mode 100644 index 00000000..e69de29b diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx index 4b783506..0cc0cbfa 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx @@ -1,40 +1,63 @@ --- title: Create cToken Accounts -description: Client guide to create cToken accounts with step-by-step implementation and full code examples. +description: "Guide to create cToken accounts with CreateCTokenAccount builder." --- +import CTokenAccountProperties from '/snippets/ctoken-account-properties.mdx'; -Create a cToken account with a new keypair (non-ATA). +cToken accounts are Solana accounts. + + -```rust create-ctoken.rs - +```rust +let instruction = CreateCTokenAccount::new( + payer.pubkey(), + account.pubkey(), + mint, + owner, + CompressibleParams::default(), +).instruction()?; ``` -```typescript create-ctoken.ts +```typescript create-ctoken-account.ts // TODO: TypeScript SDK coming soon ``` - -cToken accounts are compressible with rent-exemption sponsorship. The protocol funds the account's rent exemption at creation, and the account compresses automatically when inactive for ~3 hours (27,000 slots). - - # Full Code Example +Run the full code example by +1. clone xy +2. Run `cargo test` + ### Prerequisites You need a compressed mint (cMint) before creating cToken accounts. - + + +**Dependencies** + +```toml Cargo.toml +[dependencies] +light-compressed-token-sdk = "0.1" +light-client = "0.1" +light-ctoken-types = "0.1" +solana-sdk = "2.2" +borsh = "0.10" +tokio = { version = "1.36", features = ["full"] } +``` + +**Create cMint First** See [Create a Compressed Mint](/compressed-token-program/ctoken/client-guides/client-create-cmint) for the full guide. @@ -47,79 +70,59 @@ See [Create a Compressed Mint](/compressed-token-program/ctoken/client-guides/cl -```rust create_ctoken.rs expandable -use borsh::{BorshDeserialize, BorshSerialize}; +```rust create_ctoken_account.rs expandable +use borsh::BorshDeserialize; use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::{config_pda, rent_sponsor_pda, CTOKEN_PROGRAM_ID}; -use solana_sdk::{ - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - signature::Keypair, - signer::Signer, -}; - -#[derive(BorshSerialize, BorshDeserialize)] -pub struct CreateTokenAccountData { - pub owner: Pubkey, - pub pre_pay_num_epochs: u64, - pub lamports_per_write: u64, -} +use light_compressed_token_sdk::ctoken::{CreateCTokenAccount, CompressibleParams}; +use light_ctoken_types::state::CToken; +use solana_sdk::{signature::Keypair, signer::Signer, pubkey::Pubkey}; async fn create_ctoken_account( rpc: &mut impl Rpc, payer: &Keypair, - mint_pda: Pubkey, + mint: Pubkey, owner: Pubkey, - wrapper_program_id: Pubkey, ) -> Pubkey { - // Step 1: Generate new keypair for cToken account - let ctoken_account = Keypair::new(); - - // Step 2: Build CreateTokenAccountData - let create_token_account_data = CreateTokenAccountData { + // Step 1: Generate new keypair for the cToken account + let account = Keypair::new(); + + // Step 2: Build instruction using SDK builder + let instruction = CreateCTokenAccount::new( + payer.pubkey(), + account.pubkey(), + mint, owner, - pre_pay_num_epochs: 2, - lamports_per_write: 1, - }; - let instruction_data = [vec![2u8], create_token_account_data.try_to_vec().unwrap()].concat(); - - // Step 3: Get PDAs - let config = config_pda(); - let rent_sponsor = rent_sponsor_pda(); - - // Step 4: Build instruction - let instruction = Instruction { - program_id: wrapper_program_id, - accounts: vec![ - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new(ctoken_account.pubkey(), true), - AccountMeta::new_readonly(mint_pda, false), - AccountMeta::new_readonly(config, false), - AccountMeta::new_readonly(Pubkey::default(), false), // system_program - AccountMeta::new(rent_sponsor, false), - AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), - ], - data: instruction_data, - }; - - // Step 5: Send transaction (ctoken_account must sign) - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &ctoken_account]) - .await - .unwrap(); - - println!("cToken account created!"); - println!("Account: {}", ctoken_account.pubkey()); - println!("Owner: {}", owner); - println!("Mint: {}", mint_pda); - - ctoken_account.pubkey() + CompressibleParams::default(), + ) + .instruction() + .unwrap(); + + // Step 3: Send transaction + rpc.create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[payer, &account], + ) + .await + .unwrap(); + + // Step 4: Verify account creation + let account_data = rpc.get_account(account.pubkey()).await.unwrap().unwrap(); + let ctoken_state = CToken::deserialize(&mut &account_data.data[..]).unwrap(); + + assert_eq!(ctoken_state.mint, mint); + assert_eq!(ctoken_state.owner, owner); + assert_eq!(ctoken_state.amount, 0); + + println!("cToken account created: {}", account.pubkey()); + account.pubkey() } ``` -```typescript create_ctoken.ts expandable +```typescript create_ctoken_account.ts expandable // TODO: TypeScript SDK coming soon ``` @@ -132,21 +135,12 @@ async fn create_ctoken_account( - Create cToken Accounts - - - Mint cTokens + Mint tokens to your cToken account - \ No newline at end of file + diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 5ea64c0e..4cee6c9c 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -8,23 +8,13 @@ import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; +import CTokenAccountProperties from '/snippets/ctoken-account-properties.mdx'; ## Key Points cToken accounts are Solana accounts and created via CPI to the cToken Program. -1. cToken accounts hold token balances similar to SPL Token accounts. -2. cToken accounts are compressible, which means - * the **rent-exemption** for the Solana account is **sponsored** by Light Protocol, - * you pay **rent per rent-epoch** **instead** of the rent-exemption upfront, - * accounts with rent for less than 1 epoch are compressed (360 lamports), and - * compressed cToken accounts are decompressed with the same state when written to. - -3. **Rent for 24h** (16 epochs) is **11,000 lamports** and paid at account creation. -4. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). - * The top-up is configured to 766 lamports by default (rent for ~3h / 2 epochs). - * Write transactions to compressed cToken accounts decompress the account. -5. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. + Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). diff --git a/docs.json b/docs.json index 4eecbbbd..02e158c8 100644 --- a/docs.json +++ b/docs.json @@ -73,7 +73,8 @@ { "group": "Program Guides", "pages": [ - "compressed-token-program/cmint/program-guides/program-create-cmint" + "compressed-token-program/cmint/program-guides/program-create-cmint", + "compressed-token-program/ctoken/program-guides/program-mint-to-cToken" ] } ] @@ -97,7 +98,6 @@ "pages": [ "compressed-token-program/ctoken/program-guides/program-create-ctoken", "compressed-token-program/ctoken/program-guides/program-create-cATA", - "compressed-token-program/ctoken/program-guides/program-mint-to-cToken", "compressed-token-program/ctoken/program-guides/program-transfer-interface", "compressed-token-program/ctoken/program-guides/program-close-ctoken" ] diff --git a/snippets/ctoken-account-properties.mdx b/snippets/ctoken-account-properties.mdx new file mode 100644 index 00000000..8a266ba9 --- /dev/null +++ b/snippets/ctoken-account-properties.mdx @@ -0,0 +1,12 @@ +1. cToken accounts hold token balances similar to SPL Token accounts. +2. cToken accounts are compressible, which means + * the **rent-exemption** for the Solana account is **sponsored** by Light Protocol, + * you pay **rent per rent-epoch** **instead** of the rent-exemption upfront, + * accounts with rent for less than 1 epoch are compressed (360 lamports), and + * compressed cToken accounts are decompressed with the same state when written to. + +3. **Rent for 24h** (16 epochs) is **11,000 lamports** and paid at account creation. +4. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). + * The top-up is configured to 766 lamports by default (rent for ~3h / 2 epochs). + * Write transactions to compressed cToken accounts decompress the account. +5. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. From 38f102d7e32df2ea71e311faf5e82b4ad1a8227e Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Tue, 2 Dec 2025 19:02:17 +0000 Subject: [PATCH 038/143] Reorganize mint guides into cMint section - Move client-mint-to-ctoken.mdx to cmint/client-guides/ - Move program-mint-to-cToken.mdx to cmint/program-guides/ - Rename cmint.mdx to README.mdx for section overview - Update docs.json navigation structure - Enhance transfer interface documentation with comparison table - Update cross-references between cMint and cToken sections --- .../cmint/{cmint.mdx => README.mdx} | 5 +- .../client-guides/client-mint-to-ctoken.mdx | 0 .../cmint/client-guides/cmint.mdx | 4 +- .../program-guides/program-create-cmint.mdx | 2 +- .../program-guides/program-mint-to-cToken.mdx | 0 compressed-token-program/ctoken/README.mdx | 43 ++++++++++ .../program-transfer-interface.mdx | 79 ++++++++++++++----- docs.json | 47 ++++++----- 8 files changed, 137 insertions(+), 43 deletions(-) rename compressed-token-program/cmint/{cmint.mdx => README.mdx} (90%) rename compressed-token-program/{ctoken => cmint}/client-guides/client-mint-to-ctoken.mdx (100%) rename compressed-token-program/{ctoken => cmint}/program-guides/program-mint-to-cToken.mdx (100%) diff --git a/compressed-token-program/cmint/cmint.mdx b/compressed-token-program/cmint/README.mdx similarity index 90% rename from compressed-token-program/cmint/cmint.mdx rename to compressed-token-program/cmint/README.mdx index 94d19ba6..44e234d8 100644 --- a/compressed-token-program/cmint/cmint.mdx +++ b/compressed-token-program/cmint/README.mdx @@ -1,7 +1,6 @@ --- -title: cMint Accounts -sidebarTitle: Overview -description: Overview of cMint accounts. +title: Overview +description: Overview to cMint accounts. --- import { SolanaRentCalculator } from '/snippets/solana-rent-calculator.jsx'; diff --git a/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx b/compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx similarity index 100% rename from compressed-token-program/ctoken/client-guides/client-mint-to-ctoken.mdx rename to compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx diff --git a/compressed-token-program/cmint/client-guides/cmint.mdx b/compressed-token-program/cmint/client-guides/cmint.mdx index 183fc02b..38e196bc 100644 --- a/compressed-token-program/cmint/client-guides/cmint.mdx +++ b/compressed-token-program/cmint/client-guides/cmint.mdx @@ -1,6 +1,6 @@ --- -title: Create cMint with Token Metadata -description: Guide to create a cMint, a rent-free mint account to store global metadata of tokens. +title: Create cMint account +description: Guide to create a cMint with Token Metadata, a rent-free mint account to store global metadata of tokens. --- import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index b63e5517..dea9c2a3 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -1,5 +1,5 @@ --- -title: Create cMint Accounts with Token Metadata +title: Create cMint Account description: Program guide to create cMint accounts with token metadata. Includes step-by-step implementation and full code examples. --- diff --git a/compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx similarity index 100% rename from compressed-token-program/ctoken/program-guides/program-mint-to-cToken.mdx rename to compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx index e69de29b..90e0ab34 100644 --- a/compressed-token-program/ctoken/README.mdx +++ b/compressed-token-program/ctoken/README.mdx @@ -0,0 +1,43 @@ +--- +title: Overview +description: "Overview to cTokens and guides with full code examples." +--- +import { SolanaRentCalculator } from '/snippets/solana-rent-calculator.jsx'; + +## Key Points + +A cToken is a compressed account that represents a token account on Solana. cTokens store the same global metadata as SPL token accounts but without requiring rent. + + + +## Client Guides + +table for client guides + +## Program Guides + +table for program guides +## Examples + +add github link + +## Next Steps + +Follow our client or program guides for cTokens. + + + + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index ec77a94e..e0903dc1 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -1,22 +1,48 @@ --- -title: Transfer Interface for cToken <> SPL Tokens -description: Program guide for the unified transfer interface for cToken <> SPL token transfers and to migrate SPL to cTokens. +title: Transfer Interface +description: Program guide for transfers between cToken and SPL token accounts via CPI. The interface detects account types and invokes the right programs. + --- import TransferInterfaceAccountsListCtoken1 from '/snippets/transfer-interface-accounts-list-ctoken-1.mdx'; import TransferInterfaceAccountsListSpl2 from '/snippets/transfer-interface-accounts-list-spl-2.mdx'; -## Key Points - -The `TransferInterface` is a unified API that detects account types and routes to the appropriate transfer via CPI to the cToken Program: - -1. **cToken → cToken**: Transfer of cTokens between cToken accounts. -2. **cToken → SPL**: Transfer cTokens to an SPL token account -3. **SPL → cToken**: Transfer SPL tokens to cToken accounts to migrate SPL tokens - - -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). - + + + + + + + + + + + + + + + +
**cToken → cToken Account** +
    +
  • Transfers cTokens between cToken accounts
  • +
+
**SPL token → cToken Account** +
    +
  • Transfers SPL tokens to cToken accounts
  • +
  • SPL tokens are locked in interface PDA
  • +
  • cTokens are minted to cToken account
  • +
+
**cToken → SPL Account** +
    +
  • Releases SPL tokens from interface PDA to SPL account
  • +
  • Burns cTokens in source cToken account
  • +
+
+ + +* For example, **SPL → cToken** can be used for transfers from Alice's SPL token account to her own cToken account. +* You can use this to **convert existing SPL tokens to cTokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the cToken program](/compressed-token-program/ctoken/README). + Find [full code examples at the end](#full-code-example). @@ -60,16 +86,16 @@ The SPL transfer interface is only needed for SPL ↔ cToken transfers. transfer = transfer.with_spl_interface( Some(mint.clone()), Some(spl_token_program.clone()), - Some(token_pool_pda.clone()), - data.token_pool_pda_bump, + Some(spl_interface_pda.clone()), + data.spl_interface_pda_bump, )?; ``` -SPL ↔ cToken transfers require a `token_pool_pda`: +SPL ↔ cToken transfers require a `spl_interface_pda`: * **SPL → cToken**: SPL tokens are locked by the cToken Program in the PDA, cTokens are minted to cToken accounts * **cToken → SPL**: cTokens are burned, SPL tokens transferred to SPL token accounts -The token pool is derived from the `mint` pubkey and pool seed. +The interface PDA is derived from the `mint` pubkey and pool seed. @@ -79,7 +105,24 @@ The token pool is derived from the `mint` pubkey and pool seed. -### CPI the cToken Program +### CPI the cToken Program + +CPI the cToken program to execute the transfer. +Use `invoke()`, or `invoke_signed()` when a CPI requires a PDA signer. + + + +```rust +transfer.invoke()?; +``` + + +```rust +let authority_seeds: &[&[u8]] = &[TRANSFER_INTERFACE_AUTHORITY_SEED, &[authority_bump]]; +transfer.invoke_signed(&[authority_seeds])?; +``` + +
diff --git a/docs.json b/docs.json index 02e158c8..313c6ee3 100644 --- a/docs.json +++ b/docs.json @@ -62,19 +62,20 @@ { "group": "cMint", "pages": [ - "compressed-token-program/cmint/cmint", + "compressed-token-program/cmint/README", { "group": "Client Guides", "pages": [ "compressed-token-program/cmint/client-guides/cmint", - "compressed-token-program/cmint/client-guides/update-metadata" + "compressed-token-program/cmint/client-guides/update-metadata", + "compressed-token-program/cmint/client-guides/client-mint-to-ctoken" ] }, { "group": "Program Guides", "pages": [ "compressed-token-program/cmint/program-guides/program-create-cmint", - "compressed-token-program/ctoken/program-guides/program-mint-to-cToken" + "compressed-token-program/cmint/program-guides/program-mint-to-cToken" ] } ] @@ -82,13 +83,12 @@ { "group": "cToken", "pages": [ - "compressed-token-program/ctoken/mint-actions", + "compressed-token-program/ctoken/README", { "group": "Client Guides", "pages": [ "compressed-token-program/ctoken/client-guides/client-create-ctoken", "compressed-token-program/ctoken/client-guides/client-create-cATA", - "compressed-token-program/ctoken/client-guides/client-mint-to-ctoken", "compressed-token-program/ctoken/client-guides/client-transfer-interface", "compressed-token-program/ctoken/client-guides/client-close-ctoken" ] @@ -490,36 +490,45 @@ { "group": "cMint", "pages": [ - "compressed-token-program/cmint/cmint", - "compressed-token-program/cmint/update-metadata" + "compressed-token-program/cmint/README", + { + "group": "Client Guides", + "pages": [ + "compressed-token-program/cmint/client-guides/cmint", + "compressed-token-program/cmint/client-guides/client-create-cmint", + "compressed-token-program/cmint/client-guides/update-metadata" + ] + }, + { + "group": "Program Guides", + "pages": [ + "compressed-token-program/cmint/program-guides/program-create-cmint" + ] + } ] }, { "group": "cToken", "pages": [ - "compressed-token-program/ctoken/mint-actions", + "compressed-token-program/ctoken/README", { "group": "Client Guides", "pages": [ "compressed-token-program/ctoken/client-guides/client-create-ctoken", - "compressed-token-program/ctoken/client-guides/client-create-cmint", "compressed-token-program/ctoken/client-guides/client-create-cATA", - "compressed-token-program/ctoken/client-guides/client-mint-to-ctoken" + "compressed-token-program/ctoken/client-guides/client-mint-to-ctoken", + "compressed-token-program/ctoken/client-guides/client-transfer-interface", + "compressed-token-program/ctoken/client-guides/client-close-ctoken" ] }, { "group": "Program Guides", "pages": [ - "compressed-token-program/ctoken/program-guides/create-ctoken", "compressed-token-program/ctoken/program-guides/program-create-ctoken", - "compressed-token-program/ctoken/program-guides/mint-ctokens", - "compressed-token-program/ctoken/program-guides/create-ata", - "compressed-token-program/ctoken/program-guides/ctoken-transfer", - "compressed-token-program/ctoken/program-guides/SPL-to-ctoken-transfer", - "compressed-token-program/ctoken/program-guides/ctoken-to-spl-decompress", + "compressed-token-program/ctoken/program-guides/program-create-cATA", + "compressed-token-program/ctoken/program-guides/program-mint-to-cToken", "compressed-token-program/ctoken/program-guides/program-transfer-interface", - "compressed-token-program/ctoken/program-guides/compress-and-close", - "compressed-token-program/ctoken/program-guides/close-ctoken" + "compressed-token-program/ctoken/program-guides/program-close-ctoken" ] } ] @@ -527,7 +536,7 @@ { "group": "Compressed Token", "pages": [ - "compressed-token-program/compressed-tokens/compressed-token-overview", + "compressed-tokens/README", { "group": "Basic Guides", "pages": [ From 7b9c8ce83364dc0182b9d6816dba9cf27fd4bd69 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Tue, 2 Dec 2025 19:15:12 +0000 Subject: [PATCH 039/143] Refine guide titles and remove redundant key points - Simplify cMint and cToken guide titles for clarity - Remove "Key Points" sections that duplicate content - Standardize description patterns across guides - Update transfer interface terminology from token_pool to spl_interface - Add code example tabs for invoke patterns --- .../cmint/client-guides/cmint.mdx | 2 +- .../cmint/program-guides/program-create-cmint.mdx | 8 ++------ .../program-guides/program-mint-to-cToken.mdx | 15 ++++++--------- .../program-guides/program-close-ctoken.mdx | 10 +--------- 4 files changed, 10 insertions(+), 25 deletions(-) diff --git a/compressed-token-program/cmint/client-guides/cmint.mdx b/compressed-token-program/cmint/client-guides/cmint.mdx index 38e196bc..50117c65 100644 --- a/compressed-token-program/cmint/client-guides/cmint.mdx +++ b/compressed-token-program/cmint/client-guides/cmint.mdx @@ -1,5 +1,5 @@ --- -title: Create cMint account +title: Create cMint description: Guide to create a cMint with Token Metadata, a rent-free mint account to store global metadata of tokens. --- diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index dea9c2a3..7d455715 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -1,14 +1,10 @@ --- -title: Create cMint Account -description: Program guide to create cMint accounts with token metadata. Includes step-by-step implementation and full code examples. +title: Create cMint +description: Program guide to create a cMint with token metadata via CPI to the cToken Program. Includes step-by-step implementation and full code examples. --- import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; -## Key Points - -cMints are created via CPI to the Compressed Token Program. - 1. cMints uniquely represent a token on Solana and store its global metadata. 2. cMints are compressed accounts and rent-free. 3. The `mint_signer` keypair/PDA derives the mint address. diff --git a/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx index c38e5a3d..bf3511a9 100644 --- a/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx @@ -1,17 +1,14 @@ --- title: Mint cTokens -description: Program guide to mint cTokens with step-by-step implementation and full code examples. +description: Program guide to mint cTokens to a cMint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. --- import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; import CMintToCTokenAccountsList from '/snippets/cmint-to-ctoken-accounts-list.mdx'; -## Key Points - - -cTokens are minted via CPI to the Compressed Token Program and increase the supply of a cMint. -1. The destination cToken accounts must exist to receive the minted cTokens. -2. Only the mint authority can mint new cTokens. +1. cTokens increase the supply of a cMint. +2. The destination cToken accounts must exist to receive the minted cTokens. +3. Only the mint authority can mint new cTokens. Find [full code examples at the end](#full-code-example). @@ -23,6 +20,8 @@ Find [full code examples at the end](#full-code-example). ### Configure Mint Parameters +Include your mint, the amount of tokens to be minted and the pubkey of the mint authority. +The client passes a validity proof that proves the cMint exists. ```rust use light_compressed_token_sdk::ctoken::MintToCTokenParams; @@ -35,8 +34,6 @@ let mint_params = MintToCTokenParams::new( ); ``` -Include your mint, the amount of tokens to be minted and the pubkey of the mint authority. -The client passes a validity proof that proves the cMint exists. diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index 5bde8b7f..4892f4f0 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -1,24 +1,16 @@ --- title: Close cToken Accounts -description: Program guide to close cToken accounts with step-by-step implementation and full code examples. +description: Program guide to close cToken accounts via CPI to the cToken Program with step-by-step implementation and full code examples. --- import CloseAccountInfosAccountsList from '/snippets/close-account-infos-accounts-list.mdx'; -## Key Points - -cToken accounts are closed via CPI to the cToken Program. - 1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. 2. cToken accounts can be closed * by the account owner at any time. * by the `compression_authority` when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). - -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). - - Find [full code examples at the end](#full-code-example). From 805ff723d11d7cb4995c0c4a6a95efa20d873676 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Tue, 2 Dec 2025 20:46:48 +0000 Subject: [PATCH 040/143] Simplify cATA guide with cost comparison and clearer rent config - Replace verbose key points with structured rent config list - Add creation cost comparison table (cATA vs SPL) - Simplify rent config snippet to focus on default values - Change section heading from "Configure" to "Define" for accuracy --- .../program-guides/program-create-cATA.mdx | 31 +++++++------------ snippets/ctoken-configure-rent.mdx | 6 ++-- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index 06e54850..596a9a37 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -9,26 +9,17 @@ import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx' import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; -## Key Points - -Associated cToken accounts (cATA) are Solana accounts and created via CPI to the cToken Program. - -1. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -2. cATAs accounts are compressible, which means - * the **rent-exemption** for the Solana account is **sponsored** by Light Protocol, - * you pay **rent per rent-epoch** **instead** of the rent-exemption upfront, - * accounts with rent for less than 1 epoch are compressed (360 lamports), and - * compressed cToken accounts are decompressed with the same state when written to. - -3. **Rent for 24h** (16 epochs) is **11,000 lamports** and paid at account creation. -4. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). - * The top-up is configured to 766 lamports by default (rent for ~3h / 2 epochs). - * Write transactions to compressed cToken accounts decompress the account. -5. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. +1. Associated cToken accounts (cATA) are Solana accounts that hold balances of cTokens and SPL tokens. +2. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. +3. cATAs accounts are compressible with a default rent config: + 1. Rent for 24h (16 epochs) is 11,000 lamports and paid at account creation. + 2. Top-ups to 766 lamports for 3h of rent (2 epochs), paid by the transaction payer when the account’s rent is below two epochs, + 3. The account creation and transfers to decompress cost additional 11,000 lamports for compression & protocol incentive. + +| Creation Cost Comparison | cToken Account | SPL Token Account | +|--|----------------|-------------------| +| | 22,208 lamports | 2,039,280 lamports | - -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). - Find [full code examples at the end](#full-code-example). @@ -39,7 +30,7 @@ Find [full code examples at the end](#full-code-example). -### Configure Rent +### Define Rent Config diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index 4ee5c8ce..be5fdaf6 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -1,6 +1,4 @@ -Define `CompressibleParamsInfos` with the standard rent config and accounts: -- Rent for 24 hours (~11,000 lamports) -- Top-up amount to 766 lamports (3h rent) +Define `CompressibleParamsInfos` with accounts for the default rent config: ```rust use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; @@ -46,7 +44,7 @@ let compressible_params = CompressibleParamsInfos::new( -We recommend to use default values, but you can customize the parameters based on the expected account activity. +We recommend to use default values, but you can customize the rent config based on the expected account activity. From 16bee2086eb9111f2787b42bd22ed2a898ad2b5c Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 01:03:41 +0000 Subject: [PATCH 041/143] Reorganize snippets and add compressible rent documentation - Organize snippets into subdirectories: accounts-list/, jsx/, overview-tables/, setup/ - Add compressible-rent-explained.mdx and compressible-default-rent-config.mdx - Add ctoken-vs-spl-calculator.jsx for cost comparison - Update all import paths across documentation files - Enhance cToken README with compressible rent details and cost table - Add Token22 extensions support table to cMint README --- client-library/client-guide.mdx | 2 +- .../create-a-program-with-compressed-pdas.mdx | 4 +- compressed-pdas/guides.mdx | 2 +- .../how-to-burn-compressed-accounts.mdx | 6 +- .../how-to-close-compressed-accounts.mdx | 6 +- .../how-to-create-compressed-accounts.mdx | 4 +- ...ow-to-reinitialize-compressed-accounts.mdx | 6 +- .../how-to-update-compressed-accounts.mdx | 6 +- compressed-token-program/c-token-program.mdx | 11 +- compressed-token-program/cmint/README.mdx | 37 +++- .../cmint/client-guides/cmint.mdx | 2 +- .../program-guides/program-create-cmint.mdx | 2 +- .../program-guides/program-mint-to-cToken.mdx | 4 +- compressed-token-program/ctoken/README.mdx | 48 ++++- .../program-guides/program-close-ctoken.mdx | 2 +- .../program-guides/program-create-cATA.mdx | 24 ++- .../program-guides/program-create-ctoken.mdx | 16 +- .../program-transfer-interface.mdx | 4 +- compressed-token-program/overview.mdx | 8 +- compressed-tokens/README.mdx | 10 +- compressed-tokens/advanced-guides.mdx | 2 +- ...d-wallet-support-for-compressed-tokens.mdx | 4 +- .../advanced-guides/create-an-airdrop.mdx | 6 +- ...-combine-operations-in-one-transaction.mdx | 4 +- .../use-token-2022-with-compression.mdx | 4 +- compressed-tokens/guides.mdx | 2 +- ...-approve-and-revoke-delegate-authority.mdx | 4 +- ...-to-compress-and-decompress-spl-tokens.mdx | 6 +- ...o-compress-complete-spl-token-accounts.mdx | 6 +- ...egister-a-mint-account-for-compression.mdx | 4 +- ...mpressed-token-pools-for-mint-accounts.mdx | 4 +- ...how-to-merge-compressed-token-accounts.mdx | 4 +- .../guides/how-to-mint-compressed-tokens.mdx | 6 +- .../how-to-transfer-compressed-token.mdx | 6 +- compressed-tokens/overview.mdx | 8 +- rent.mdx | 8 +- resources/addresses-and-urls.mdx | 2 +- resources/sdks/program-development.mdx | 2 +- .../close-account-infos-accounts-list.mdx | 0 .../cmint-system-accounts-list.mdx | 0 .../cmint-to-ctoken-accounts-list.mdx | 0 .../compressed-pdas-system-accounts-list.mdx | 0 .../ctoken-create-accounts-list-client.mdx | 0 .../ctoken-create-accounts-list.mdx | 0 .../ctoken-create-ata-accounts-list.mdx | 0 ...nsfer-interface-accounts-list-ctoken-1.mdx | 0 ...transfer-interface-accounts-list-spl-2.mdx | 0 snippets/compressible-default-rent-config.mdx | 4 + snippets/compressible-rent-explained.mdx | 8 + snippets/ctoken-configure-rent.mdx | 4 - snippets/{ => jsx}/break-even-calculator.jsx | 2 +- .../compressible-rent-calculator.jsx | 28 ++- snippets/jsx/ctoken-vs-spl-calculator.jsx | 139 ++++++++++++++ snippets/{ => jsx}/solana-rent-calculator.jsx | 2 +- .../token-account-compressed-vs-spl.jsx | 2 +- snippets/jsx/token22-extensions-table.jsx | 177 ++++++++++++++++++ .../compressed-pdas-guides-table.mdx | 0 ...ompressed-tokens-advanced-guides-table.mdx | 0 .../compressed-tokens-guides-table.mdx | 0 .../program-examples-table.mdx | 0 .../compressed-pdas-program-setup.mdx | 0 .../compressed-tokens-mint-prereq.mdx | 0 .../development-environment-setup.mdx | 0 .../install-dependencies-codegroup.mdx | 0 .../{ => setup}/setup-environment-tabs.mdx | 0 65 files changed, 532 insertions(+), 120 deletions(-) rename snippets/{ => accounts-list}/close-account-infos-accounts-list.mdx (100%) rename snippets/{ => accounts-list}/cmint-system-accounts-list.mdx (100%) rename snippets/{ => accounts-list}/cmint-to-ctoken-accounts-list.mdx (100%) rename snippets/{ => accounts-list}/compressed-pdas-system-accounts-list.mdx (100%) rename snippets/{ => accounts-list}/ctoken-create-accounts-list-client.mdx (100%) rename snippets/{ => accounts-list}/ctoken-create-accounts-list.mdx (100%) rename snippets/{ => accounts-list}/ctoken-create-ata-accounts-list.mdx (100%) rename snippets/{ => accounts-list}/transfer-interface-accounts-list-ctoken-1.mdx (100%) rename snippets/{ => accounts-list}/transfer-interface-accounts-list-spl-2.mdx (100%) create mode 100644 snippets/compressible-default-rent-config.mdx create mode 100644 snippets/compressible-rent-explained.mdx rename snippets/{ => jsx}/break-even-calculator.jsx (99%) rename snippets/{ => jsx}/compressible-rent-calculator.jsx (87%) create mode 100644 snippets/jsx/ctoken-vs-spl-calculator.jsx rename snippets/{ => jsx}/solana-rent-calculator.jsx (98%) rename snippets/{ => jsx}/token-account-compressed-vs-spl.jsx (99%) create mode 100644 snippets/jsx/token22-extensions-table.jsx rename snippets/{ => overview-tables}/compressed-pdas-guides-table.mdx (100%) rename snippets/{ => overview-tables}/compressed-tokens-advanced-guides-table.mdx (100%) rename snippets/{ => overview-tables}/compressed-tokens-guides-table.mdx (100%) rename snippets/{ => overview-tables}/program-examples-table.mdx (100%) rename snippets/{ => setup}/compressed-pdas-program-setup.mdx (100%) rename snippets/{ => setup}/compressed-tokens-mint-prereq.mdx (100%) rename snippets/{ => setup}/development-environment-setup.mdx (100%) rename snippets/{ => setup}/install-dependencies-codegroup.mdx (100%) rename snippets/{ => setup}/setup-environment-tabs.mdx (100%) diff --git a/client-library/client-guide.mdx b/client-library/client-guide.mdx index b111a21d..abe57954 100644 --- a/client-library/client-guide.mdx +++ b/client-library/client-guide.mdx @@ -5,7 +5,7 @@ description: >- code examples. --- -import SystemAccountsList from '/snippets/compressed-pdas-system-accounts-list.mdx'; +import SystemAccountsList from '/snippets/accounts-list/compressed-pdas-system-accounts-list.mdx'; ZK Compression provides Rust and Typescript clients to interact with compressed accounts and tokens on Solana. diff --git a/compressed-pdas/create-a-program-with-compressed-pdas.mdx b/compressed-pdas/create-a-program-with-compressed-pdas.mdx index 9a68059f..b480645b 100644 --- a/compressed-pdas/create-a-program-with-compressed-pdas.mdx +++ b/compressed-pdas/create-a-program-with-compressed-pdas.mdx @@ -3,8 +3,8 @@ title: Create a Program with Compressed PDAs description: Overview to compressed PDA core features and guide for program development --- -import ProgramExamplesTable from '/snippets/program-examples-table.mdx'; -import DevelopmentEnvironmentSetup from '/snippets/development-environment-setup.mdx'; +import ProgramExamplesTable from '/snippets/overview-tables/program-examples-table.mdx'; +import DevelopmentEnvironmentSetup from '/snippets/setup/development-environment-setup.mdx'; Compressed PDAs provide full functionality of accounts at PDAs, without per-account rent cost. diff --git a/compressed-pdas/guides.mdx b/compressed-pdas/guides.mdx index c25c1694..da612261 100644 --- a/compressed-pdas/guides.mdx +++ b/compressed-pdas/guides.mdx @@ -4,7 +4,7 @@ description: Overview to guides for Solana programs to create, update, close, re sidebarTitle: "Overview" --- -import GuidesTable from '/snippets/compressed-pdas-guides-table.mdx'; +import GuidesTable from '/snippets/overview-tables/compressed-pdas-guides-table.mdx'; diff --git a/compressed-pdas/guides/how-to-burn-compressed-accounts.mdx b/compressed-pdas/guides/how-to-burn-compressed-accounts.mdx index 42adf795..a1b25a4a 100644 --- a/compressed-pdas/guides/how-to-burn-compressed-accounts.mdx +++ b/compressed-pdas/guides/how-to-burn-compressed-accounts.mdx @@ -3,9 +3,9 @@ title: Burn Compressed Accounts description: Guide to burn compressed accounts in Solana programs with full code examples. --- -import CompressedPdasProgramSetup from '/snippets/compressed-pdas-program-setup.mdx'; -import CompressedPdasSystemAccountsList from '/snippets/compressed-pdas-system-accounts-list.mdx'; -import DevelopmentEnvironmentSetup from '/snippets/development-environment-setup.mdx'; +import CompressedPdasProgramSetup from '/snippets/setup/compressed-pdas-program-setup.mdx'; +import CompressedPdasSystemAccountsList from '/snippets/accounts-list/compressed-pdas-system-accounts-list.mdx'; +import DevelopmentEnvironmentSetup from '/snippets/setup/development-environment-setup.mdx'; Compressed accounts are permanently burned via CPI to the Light System Program. diff --git a/compressed-pdas/guides/how-to-close-compressed-accounts.mdx b/compressed-pdas/guides/how-to-close-compressed-accounts.mdx index 0441ba82..611ecfe9 100644 --- a/compressed-pdas/guides/how-to-close-compressed-accounts.mdx +++ b/compressed-pdas/guides/how-to-close-compressed-accounts.mdx @@ -3,9 +3,9 @@ title: Close Compressed Accounts description: Guide to close compressed accounts in Solana programs with full code examples. --- -import CompressedPdasProgramSetup from '/snippets/compressed-pdas-program-setup.mdx'; -import CompressedPdasSystemAccountsList from '/snippets/compressed-pdas-system-accounts-list.mdx'; -import DevelopmentEnvironmentSetup from '/snippets/development-environment-setup.mdx'; +import CompressedPdasProgramSetup from '/snippets/setup/compressed-pdas-program-setup.mdx'; +import CompressedPdasSystemAccountsList from '/snippets/accounts-list/compressed-pdas-system-accounts-list.mdx'; +import DevelopmentEnvironmentSetup from '/snippets/setup/development-environment-setup.mdx'; Compressed accounts are closed via CPI to the Light System Program. diff --git a/compressed-pdas/guides/how-to-create-compressed-accounts.mdx b/compressed-pdas/guides/how-to-create-compressed-accounts.mdx index f7057995..aa02342a 100644 --- a/compressed-pdas/guides/how-to-create-compressed-accounts.mdx +++ b/compressed-pdas/guides/how-to-create-compressed-accounts.mdx @@ -3,8 +3,8 @@ title: Create Compressed Accounts description: Guide to create compressed accounts in Solana programs with full code examples. --- -import CompressedPdasSystemAccountsList from '/snippets/compressed-pdas-system-accounts-list.mdx'; -import DevelopmentEnvironmentSetup from '/snippets/development-environment-setup.mdx'; +import CompressedPdasSystemAccountsList from '/snippets/accounts-list/compressed-pdas-system-accounts-list.mdx'; +import DevelopmentEnvironmentSetup from '/snippets/setup/development-environment-setup.mdx'; Compressed accounts and addresses are created via CPI to the Light System Program. diff --git a/compressed-pdas/guides/how-to-reinitialize-compressed-accounts.mdx b/compressed-pdas/guides/how-to-reinitialize-compressed-accounts.mdx index dbc26544..913171c8 100644 --- a/compressed-pdas/guides/how-to-reinitialize-compressed-accounts.mdx +++ b/compressed-pdas/guides/how-to-reinitialize-compressed-accounts.mdx @@ -3,9 +3,9 @@ title: Reinitialize Compressed Accounts description: Guide to reinitialize compressed accounts in Solana programs with full code examples. --- -import CompressedPdasProgramSetup from '/snippets/compressed-pdas-program-setup.mdx'; -import CompressedPdasSystemAccountsList from '/snippets/compressed-pdas-system-accounts-list.mdx'; -import DevelopmentEnvironmentSetup from '/snippets/development-environment-setup.mdx'; +import CompressedPdasProgramSetup from '/snippets/setup/compressed-pdas-program-setup.mdx'; +import CompressedPdasSystemAccountsList from '/snippets/accounts-list/compressed-pdas-system-accounts-list.mdx'; +import DevelopmentEnvironmentSetup from '/snippets/setup/development-environment-setup.mdx'; Compressed accounts are reinitialized via CPI to the Light System Program. diff --git a/compressed-pdas/guides/how-to-update-compressed-accounts.mdx b/compressed-pdas/guides/how-to-update-compressed-accounts.mdx index 1993c533..8c836723 100644 --- a/compressed-pdas/guides/how-to-update-compressed-accounts.mdx +++ b/compressed-pdas/guides/how-to-update-compressed-accounts.mdx @@ -3,9 +3,9 @@ title: Update Compressed Accounts description: Guide to update compressed accounts in Solana programs with full code examples. --- -import CompressedPdasProgramSetup from '/snippets/compressed-pdas-program-setup.mdx'; -import CompressedPdasSystemAccountsList from '/snippets/compressed-pdas-system-accounts-list.mdx'; -import DevelopmentEnvironmentSetup from '/snippets/development-environment-setup.mdx'; +import CompressedPdasProgramSetup from '/snippets/setup/compressed-pdas-program-setup.mdx'; +import CompressedPdasSystemAccountsList from '/snippets/accounts-list/compressed-pdas-system-accounts-list.mdx'; +import DevelopmentEnvironmentSetup from '/snippets/setup/development-environment-setup.mdx'; Compressed accounts are updated via CPI to the Light System Program. diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx index ee611a4b..ac42cf9e 100644 --- a/compressed-token-program/c-token-program.mdx +++ b/compressed-token-program/c-token-program.mdx @@ -5,7 +5,7 @@ description: Overview of cMints, cTokens, and compressed token accounts. --- import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; -import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; +import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, cTokens and compressed tokens. @@ -33,8 +33,9 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t Solana account
    -
  • **Token accounts** derived from cMints (**SPL-compatible**)
  • -
  • **Sponsored rent** exemption via [compressible extension](#compressible) (200x cheaper than SPL tokens)
  • +
  • **Token accounts** derived from cMints
  • +
  • Can hold cTokens and SPL tokens
  • +
  • **Sponsored rent** exemption via [compressible extension](#compressible)
  • Use for **active token accounts** with frequent writes (trading, etc.)
@@ -46,8 +47,8 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t
  • **Compressed account** with `TokenData` field
  • **Rent-free** and SPL-compatible
  • -
  • Use for **storage of inactive tokens** or **token distribution**
  • -
  • cToken accounts with the **[compressible extension](#compressible) are automatically compressed/decompressed** when active/inactive.
  • +
  • Use for **storage of inactive tokens** and **token distribution**
  • +
  • cToken accounts are automatically compressed/decompressed when active/inactive.
diff --git a/compressed-token-program/cmint/README.mdx b/compressed-token-program/cmint/README.mdx index 44e234d8..50359e15 100644 --- a/compressed-token-program/cmint/README.mdx +++ b/compressed-token-program/cmint/README.mdx @@ -3,13 +3,48 @@ title: Overview description: Overview to cMint accounts. --- -import { SolanaRentCalculator } from '/snippets/solana-rent-calculator.jsx'; +import { SolanaRentCalculator } from '/snippets/jsx/solana-rent-calculator.jsx'; ## Key Points A cMint is a compressed account that represents a token mint on Solana. cMints store the same global metadata as SPL token mints but without requiring rent. +## Extensions + +## Extensions + +cTokens support a number of Token 22 extensions: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Extension NameDescriptioncTokenCompressed Token
MetadataPointer-
TokenMetadata-
InterestBearingConfig-
GroupPointer-
GroupMemberPointer-
TokenGroup-
TokenGroupMember-
MintCloseAuthority--
TransferFeeConfigfees must be zero-
DefaultAccountStateany state allowed-
PermanentDelegate--
TransferHookprogram_id must be nil-
Pausable--
ConfidentialTransferMintinitialized but not enabled-
ConfidentialTransferFeeConfigfees must be zero-
ConfidentialMintBurninitialized but not enabled-
## Client Guides diff --git a/compressed-token-program/cmint/client-guides/cmint.mdx b/compressed-token-program/cmint/client-guides/cmint.mdx index 50117c65..b78231f6 100644 --- a/compressed-token-program/cmint/client-guides/cmint.mdx +++ b/compressed-token-program/cmint/client-guides/cmint.mdx @@ -3,7 +3,7 @@ title: Create cMint description: Guide to create a cMint with Token Metadata, a rent-free mint account to store global metadata of tokens. --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index 7d455715..0a09e733 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -3,7 +3,7 @@ title: Create cMint description: Program guide to create a cMint with token metadata via CPI to the cToken Program. Includes step-by-step implementation and full code examples. --- -import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; +import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; 1. cMints uniquely represent a token on Solana and store its global metadata. 2. cMints are compressed accounts and rent-free. diff --git a/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx index bf3511a9..fc2733b4 100644 --- a/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx @@ -3,8 +3,8 @@ title: Mint cTokens description: Program guide to mint cTokens to a cMint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. --- -import CMintSystemAccountsList from '/snippets/cmint-system-accounts-list.mdx'; -import CMintToCTokenAccountsList from '/snippets/cmint-to-ctoken-accounts-list.mdx'; +import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; +import CMintToCTokenAccountsList from '/snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx'; 1. cTokens increase the supply of a cMint. 2. The destination cToken accounts must exist to receive the minted cTokens. diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx index 90e0ab34..d04f5aba 100644 --- a/compressed-token-program/ctoken/README.mdx +++ b/compressed-token-program/ctoken/README.mdx @@ -1,14 +1,52 @@ --- title: Overview -description: "Overview to cTokens and guides with full code examples." +description: "cTokens are Solana accounts that hold balances of cTokens and SPL tokens." --- -import { SolanaRentCalculator } from '/snippets/solana-rent-calculator.jsx'; +import { CTokenVsSplCalculator } from '/snippets/jsx/ctoken-vs-spl-calculator.jsx'; +import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; -## Key Points -A cToken is a compressed account that represents a token account on Solana. cTokens store the same global metadata as SPL token accounts but without requiring rent. +
+ + + + + + + + + + + + + + +
Account TypeKey Features
**[cToken](#ctoken-account)**Solana account +
    +
  • **Token accounts** derived from cMints
  • +
  • Can hold cTokens and SPL tokens
  • +
  • **Sponsored rent** exemption via [compressible extension](#compressible)
  • +
  • Use for **active token accounts** with frequent writes (trading, etc.)
  • +
+
+ +| | cToken Account | SPL Token Account | +|--|----------------|-------------------| +| Default Creation Cost | 22,208 lamports | 2,039,280 lamports | + +## Compressible Rent + +1. The rent-exemption for the Solana account is sponsored by Light Protocol +2. Rent is paid per rent-epoch instead: 388 lamports for 1.5h. + * The account creator pays the initial rent for 24h by default (11,000 lamports). + * Transfers top up the account with rent for 3h by default (766 lamports),
when the account’s rent is below two epochs. +3. "Inactive" accounts, where rent is below one epoch, are compressed and the rent-exemption can be claimed by the rent sponsor. +4. Transfers to a compressed cATA "re-activate" the account with the same state (decompress). +5. The account creation and transfers to reactivate incur additional 11,000 lamports for compression & protocol incentive. + + + - ## Client Guides diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index 4892f4f0..b13f6679 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -3,7 +3,7 @@ title: Close cToken Accounts description: Program guide to close cToken accounts via CPI to the cToken Program with step-by-step implementation and full code examples. --- -import CloseAccountInfosAccountsList from '/snippets/close-account-infos-accounts-list.mdx'; +import CloseAccountInfosAccountsList from '/snippets/accounts-list/close-account-infos-accounts-list.mdx'; 1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. 2. cToken accounts can be closed diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index 596a9a37..0c290e73 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -3,23 +3,21 @@ title: Create Associated cToken Accounts description: Program guide to create associated cToken accounts with step-by-step implementation and full code examples. --- -import CTokenCreateATAAccountsList from '/snippets/ctoken-create-ata-accounts-list.mdx'; -import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; +import CTokenCreateATAAccountsList from '/snippets/accounts-list/ctoken-create-ata-accounts-list.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; -import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; +import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -1. Associated cToken accounts (cATA) are Solana accounts that hold balances of cTokens and SPL tokens. +1. Associated cToken accounts (cATA) are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. 2. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -3. cATAs accounts are compressible with a default rent config: - 1. Rent for 24h (16 epochs) is 11,000 lamports and paid at account creation. - 2. Top-ups to 766 lamports for 3h of rent (2 epochs), paid by the transaction payer when the account’s rent is below two epochs, - 3. The account creation and transfers to decompress cost additional 11,000 lamports for compression & protocol incentive. - -| Creation Cost Comparison | cToken Account | SPL Token Account | -|--|----------------|-------------------| -| | 22,208 lamports | 2,039,280 lamports | +3. cATAs accounts are compressible with a default rent config. + + + + Find [full code examples at the end](#full-code-example). @@ -30,7 +28,7 @@ Find [full code examples at the end](#full-code-example). -### Define Rent Config +### Define Rent Config Accounts diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 4cee6c9c..dd363499 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -3,18 +3,22 @@ title: Create cToken Accounts description: Program guide to create cToken accounts with step-by-step implementation and full code examples. --- -import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; -import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; +import CTokenCreateAccountsList from '/snippets/accounts-list/ctoken-create-accounts-list.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; -import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; +import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; -import CTokenAccountProperties from '/snippets/ctoken-account-properties.mdx'; +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; ## Key Points -cToken accounts are Solana accounts and created via CPI to the cToken Program. +1. cToken accounts are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. +2. cToken accounts are compressible with a default rent config. - + + + + Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index e0903dc1..21a4c629 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -4,8 +4,8 @@ description: Program guide for transfers between cToken and SPL token accounts v --- -import TransferInterfaceAccountsListCtoken1 from '/snippets/transfer-interface-accounts-list-ctoken-1.mdx'; -import TransferInterfaceAccountsListSpl2 from '/snippets/transfer-interface-accounts-list-spl-2.mdx'; +import TransferInterfaceAccountsListCtoken1 from '/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx'; +import TransferInterfaceAccountsListSpl2 from '/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx'; diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index 781a74b6..92f60c3c 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -3,10 +3,10 @@ title: Overview description: Compressed tokens provide full SPL token functionality without per-account rent cost. --- -import GuidesTable from '/snippets/compressed-tokens-guides-table.mdx'; -import AdvancedGuidesTable from '/snippets/compressed-tokens-advanced-guides-table.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; +import GuidesTable from '/snippets/overview-tables/compressed-tokens-guides-table.mdx'; +import AdvancedGuidesTable from '/snippets/overview-tables/compressed-tokens-advanced-guides-table.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; | Creation | SPL Token | Compressed Token | Cost Reduction | |:---------------------|:------------------|:----------------------|:---------------| diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx index e20c66a7..f26c45ea 100644 --- a/compressed-tokens/README.mdx +++ b/compressed-tokens/README.mdx @@ -3,11 +3,11 @@ title: Overview description: "Overview to compressed tokens and guides with full code examples. Use for token distribution or storage of inactive token accounts" --- -import GuidesTable from '/snippets/compressed-tokens-guides-table.mdx'; -import AdvancedGuidesTable from '/snippets/compressed-tokens-advanced-guides-table.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import { TokenAccountCompressedVsSpl } from '/snippets/token-account-compressed-vs-spl.jsx'; +import GuidesTable from '/snippets/overview-tables/compressed-tokens-guides-table.mdx'; +import AdvancedGuidesTable from '/snippets/overview-tables/compressed-tokens-advanced-guides-table.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import { TokenAccountCompressedVsSpl } from '/snippets/jsx/token-account-compressed-vs-spl.jsx';
diff --git a/compressed-tokens/advanced-guides.mdx b/compressed-tokens/advanced-guides.mdx index 34b0cd5e..94066669 100644 --- a/compressed-tokens/advanced-guides.mdx +++ b/compressed-tokens/advanced-guides.mdx @@ -4,6 +4,6 @@ description: "Reference table to advanced guides. Includes guides for airdrops, sidebarTitle: "Overview" --- -import AdvancedGuidesTable from '/snippets/compressed-tokens-advanced-guides-table.mdx'; +import AdvancedGuidesTable from '/snippets/overview-tables/compressed-tokens-advanced-guides-table.mdx'; diff --git a/compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens.mdx b/compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens.mdx index 75f03f49..feb5eff2 100644 --- a/compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens.mdx +++ b/compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens.mdx @@ -3,8 +3,8 @@ title: Add Wallet Support for Compressed Tokens description: Guide to add Compressed Token Support to Your Wallet Application --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; Leading Solana Wallets like Phantom and Backpack already support compressed tokens. diff --git a/compressed-tokens/advanced-guides/create-an-airdrop.mdx b/compressed-tokens/advanced-guides/create-an-airdrop.mdx index 42a0d3d3..8c8f5fbd 100644 --- a/compressed-tokens/advanced-guides/create-an-airdrop.mdx +++ b/compressed-tokens/advanced-guides/create-an-airdrop.mdx @@ -3,9 +3,9 @@ title: Create an Airdrop without Claim description: Complete guide to create an airdrop – with or without code. Access to cost calculation and best practices. ZK compression is the most efficient way to distribute SPL tokens. --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; -import { TokenAccountCompressedVsSpl } from '/snippets/token-account-compressed-vs-spl.jsx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; +import { TokenAccountCompressedVsSpl } from '/snippets/jsx/token-account-compressed-vs-spl.jsx'; ## Guides diff --git a/compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction.mdx b/compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction.mdx index a633fd20..0b4909d6 100644 --- a/compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction.mdx +++ b/compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction.mdx @@ -3,8 +3,8 @@ title: Combine Instructions in One Transaction description: Guide to combine multiple instructions in a single transaction. Full code example for token pool creation and for first-time compression of existing SPL tokens. --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; The SDK provides instruction-level APIs that return instructions without sending transactions. Combine these instructions to build custom transactions with multiple instructions. diff --git a/compressed-tokens/advanced-guides/use-token-2022-with-compression.mdx b/compressed-tokens/advanced-guides/use-token-2022-with-compression.mdx index d66daf7e..0029a174 100644 --- a/compressed-tokens/advanced-guides/use-token-2022-with-compression.mdx +++ b/compressed-tokens/advanced-guides/use-token-2022-with-compression.mdx @@ -3,8 +3,8 @@ title: Use Token-2022 with Compression description: Complete guide to mint, compress and transfer tokens with Token-2022 Metadata with ZK Compression. --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; # What you will do diff --git a/compressed-tokens/guides.mdx b/compressed-tokens/guides.mdx index 4d0a1c6f..16a44ebd 100644 --- a/compressed-tokens/guides.mdx +++ b/compressed-tokens/guides.mdx @@ -4,6 +4,6 @@ description: "Overview of guides to compressed token operations and reference ta sidebarTitle: "Overview" --- -import GuidesTable from '/snippets/compressed-tokens-guides-table.mdx'; +import GuidesTable from '/snippets/overview-tables/compressed-tokens-guides-table.mdx'; diff --git a/compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority.mdx b/compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority.mdx index d1bb1d67..5523d75b 100644 --- a/compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority.mdx +++ b/compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority.mdx @@ -3,8 +3,8 @@ title: Approve and Revoke Delegate Authority description: "Complete guide to manage delegate authority for compressed tokens with `approve()` and `revoke()`, troubleshooting and advanced configurations." --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; The `approve()` and `revoke()` functions grant and remove delegate spending authority for compressed tokens. Only the token owner can perform these instructions. diff --git a/compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens.mdx b/compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens.mdx index f381642f..632ea228 100644 --- a/compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens.mdx +++ b/compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens.mdx @@ -3,9 +3,9 @@ title: Compress and Decompress SPL Tokens description: "Guide to compress and decompress SPL tokens, troubleshooting and advanced configurations." --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; -import MintPrereq from '/snippets/compressed-tokens-mint-prereq.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; +import MintPrereq from '/snippets/setup/compressed-tokens-mint-prereq.mdx'; The `compress()` and `decompress()` functions convert SPL tokens between compressed and regular format. diff --git a/compressed-tokens/guides/how-to-compress-complete-spl-token-accounts.mdx b/compressed-tokens/guides/how-to-compress-complete-spl-token-accounts.mdx index 10c534fd..f3343ad9 100644 --- a/compressed-tokens/guides/how-to-compress-complete-spl-token-accounts.mdx +++ b/compressed-tokens/guides/how-to-compress-complete-spl-token-accounts.mdx @@ -3,9 +3,9 @@ title: Compress Complete SPL Token Accounts description: "Guide to compress complete SPL Token Accounts with compressSplTokenAccount() for account migration and to reclaim rent afterwards." --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; -import MintPrereq from '/snippets/compressed-tokens-mint-prereq.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; +import MintPrereq from '/snippets/setup/compressed-tokens-mint-prereq.mdx'; The `compressSplTokenAccount` function compresses the balance of an SPL token account, with an optional `remainingAmount` parameter to leave tokens in the original account. diff --git a/compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression.mdx b/compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression.mdx index 8a616c1b..350c10d3 100644 --- a/compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression.mdx +++ b/compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression.mdx @@ -3,8 +3,8 @@ title: Create a Mint Account with Token Pool for Compression description: "Create an SPL token mint account with token pool for compression with createMint(), troubleshooting and advanced configurations." --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; The mint account itself requires rent (like regular SPL mints), but individual compressed token accounts are rent-free. diff --git a/compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts.mdx b/compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts.mdx index 873e68f2..c9950a5f 100644 --- a/compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts.mdx +++ b/compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts.mdx @@ -3,8 +3,8 @@ title: Create Token Pools for Compression to Existing Mints description: Guide to create token pools for compressed tokens for SPL mints with `createTokenPool()`, troubleshooting and advanced configurations. --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; Create a token for compression fo an existing SPL mint. `createTokenPool()` requires only `fee_payer` and has no mint authority constraint. diff --git a/compressed-tokens/guides/how-to-merge-compressed-token-accounts.mdx b/compressed-tokens/guides/how-to-merge-compressed-token-accounts.mdx index 4e63e04b..dc72f3e0 100644 --- a/compressed-tokens/guides/how-to-merge-compressed-token-accounts.mdx +++ b/compressed-tokens/guides/how-to-merge-compressed-token-accounts.mdx @@ -3,8 +3,8 @@ title: Merge Compressed Token Accounts description: "Complete guide to merge multiple compressed token accounts into a single account with mergeTokenAccounts(), troubleshooting and advanced configurations." --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; The `mergeTokenAccounts()` function consolidates multiple compressed accounts of the same mint into a single compressed account. diff --git a/compressed-tokens/guides/how-to-mint-compressed-tokens.mdx b/compressed-tokens/guides/how-to-mint-compressed-tokens.mdx index 0062d482..3f4027f8 100644 --- a/compressed-tokens/guides/how-to-mint-compressed-tokens.mdx +++ b/compressed-tokens/guides/how-to-mint-compressed-tokens.mdx @@ -3,9 +3,9 @@ title: Mint Compressed Tokens description: "Guide to mint compressed tokens with mintTo(), troubleshooting, and advanced configurations." --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; -import MintPrereq from '/snippets/compressed-tokens-mint-prereq.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; +import MintPrereq from '/snippets/setup/compressed-tokens-mint-prereq.mdx'; The `mintTo()` function creates compressed token accounts for recipients and increases the mint's token supply. Only the mint authority can perform this operation. diff --git a/compressed-tokens/guides/how-to-transfer-compressed-token.mdx b/compressed-tokens/guides/how-to-transfer-compressed-token.mdx index b3295b29..9f70547a 100644 --- a/compressed-tokens/guides/how-to-transfer-compressed-token.mdx +++ b/compressed-tokens/guides/how-to-transfer-compressed-token.mdx @@ -3,9 +3,9 @@ title: Transfer Compressed Tokens description: Guide to transfer compressed SPL tokens between compressed or regular accounts with `transfer()`, troubleshooting and advanced configurations. --- -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; -import MintPrereq from '/snippets/compressed-tokens-mint-prereq.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; +import MintPrereq from '/snippets/setup/compressed-tokens-mint-prereq.mdx'; The `transfer()` function moves compressed tokens between accounts. * SPL token transfers that update the existing account diff --git a/compressed-tokens/overview.mdx b/compressed-tokens/overview.mdx index 4f706041..a1bae66f 100644 --- a/compressed-tokens/overview.mdx +++ b/compressed-tokens/overview.mdx @@ -3,10 +3,10 @@ title: Overview description: Compressed tokens provide full SPL token functionality without per-account rent cost. --- -import GuidesTable from '/snippets/compressed-tokens-guides-table.mdx'; -import AdvancedGuidesTable from '/snippets/compressed-tokens-advanced-guides-table.mdx'; -import SetupEnvironment from '/snippets/setup-environment-tabs.mdx'; -import InstallDependencies from '/snippets/install-dependencies-codegroup.mdx'; +import GuidesTable from '/snippets/overview-tables/compressed-tokens-guides-table.mdx'; +import AdvancedGuidesTable from '/snippets/overview-tables/compressed-tokens-advanced-guides-table.mdx'; +import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; +import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; | Creation | Regular SPL Token | Compressed Token | Cost Reduction | |:---------------------|:------------------|:----------------------|:---------------| diff --git a/rent.mdx b/rent.mdx index bd42a369..f57c204c 100644 --- a/rent.mdx +++ b/rent.mdx @@ -3,10 +3,10 @@ title: Rent Calculator description: Calculate rent for Solana vs Compressed Accounts. --- -import { SolanaRentCalculator } from '/snippets/solana-rent-calculator.jsx'; -import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; -import { TokenAccountCompressedVsSpl } from '/snippets/token-account-compressed-vs-spl.jsx'; -import { BreakEvenCalculator } from '/snippets/break-even-calculator.jsx'; +import { SolanaRentCalculator } from '/snippets/jsx/solana-rent-calculator.jsx'; +import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; +import { TokenAccountCompressedVsSpl } from '/snippets/jsx/token-account-compressed-vs-spl.jsx'; +import { BreakEvenCalculator } from '/snippets/jsx/break-even-calculator.jsx'; ## Solana Rent diff --git a/resources/addresses-and-urls.mdx b/resources/addresses-and-urls.mdx index cdfd4d13..596ddbd7 100644 --- a/resources/addresses-and-urls.mdx +++ b/resources/addresses-and-urls.mdx @@ -3,7 +3,7 @@ title: "Addresses and URLs" description: "Overview to all of ZK Compression's RPC URLs, Program IDs & Accounts and Lookup Tables." --- -import SystemAccountsList from '/snippets/compressed-pdas-system-accounts-list.mdx'; +import SystemAccountsList from '/snippets/accounts-list/compressed-pdas-system-accounts-list.mdx'; ## RPC URLs diff --git a/resources/sdks/program-development.mdx b/resources/sdks/program-development.mdx index b8bcedae..7ae55b87 100644 --- a/resources/sdks/program-development.mdx +++ b/resources/sdks/program-development.mdx @@ -3,7 +3,7 @@ title: Program Development description: "Overview of on-chain program development using Anchor, Pinocchio, or native Rust SDKs." --- -import DevelopmentEnvironmentSetup from '/snippets/development-environment-setup.mdx'; +import DevelopmentEnvironmentSetup from '/snippets/setup/development-environment-setup.mdx'; ## Rust Crates diff --git a/snippets/close-account-infos-accounts-list.mdx b/snippets/accounts-list/close-account-infos-accounts-list.mdx similarity index 100% rename from snippets/close-account-infos-accounts-list.mdx rename to snippets/accounts-list/close-account-infos-accounts-list.mdx diff --git a/snippets/cmint-system-accounts-list.mdx b/snippets/accounts-list/cmint-system-accounts-list.mdx similarity index 100% rename from snippets/cmint-system-accounts-list.mdx rename to snippets/accounts-list/cmint-system-accounts-list.mdx diff --git a/snippets/cmint-to-ctoken-accounts-list.mdx b/snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx similarity index 100% rename from snippets/cmint-to-ctoken-accounts-list.mdx rename to snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx diff --git a/snippets/compressed-pdas-system-accounts-list.mdx b/snippets/accounts-list/compressed-pdas-system-accounts-list.mdx similarity index 100% rename from snippets/compressed-pdas-system-accounts-list.mdx rename to snippets/accounts-list/compressed-pdas-system-accounts-list.mdx diff --git a/snippets/ctoken-create-accounts-list-client.mdx b/snippets/accounts-list/ctoken-create-accounts-list-client.mdx similarity index 100% rename from snippets/ctoken-create-accounts-list-client.mdx rename to snippets/accounts-list/ctoken-create-accounts-list-client.mdx diff --git a/snippets/ctoken-create-accounts-list.mdx b/snippets/accounts-list/ctoken-create-accounts-list.mdx similarity index 100% rename from snippets/ctoken-create-accounts-list.mdx rename to snippets/accounts-list/ctoken-create-accounts-list.mdx diff --git a/snippets/ctoken-create-ata-accounts-list.mdx b/snippets/accounts-list/ctoken-create-ata-accounts-list.mdx similarity index 100% rename from snippets/ctoken-create-ata-accounts-list.mdx rename to snippets/accounts-list/ctoken-create-ata-accounts-list.mdx diff --git a/snippets/transfer-interface-accounts-list-ctoken-1.mdx b/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx similarity index 100% rename from snippets/transfer-interface-accounts-list-ctoken-1.mdx rename to snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx diff --git a/snippets/transfer-interface-accounts-list-spl-2.mdx b/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx similarity index 100% rename from snippets/transfer-interface-accounts-list-spl-2.mdx rename to snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx diff --git a/snippets/compressible-default-rent-config.mdx b/snippets/compressible-default-rent-config.mdx new file mode 100644 index 00000000..d53d32b3 --- /dev/null +++ b/snippets/compressible-default-rent-config.mdx @@ -0,0 +1,4 @@ +### Default Rent Config +1. At account creation, you pay ~22,208 lamports for 24h of rent
and compression incentive (the rent-exemption is sponsored by the protocol) +2. Top-ups are 766 lamports for 3h of rent, paid by the transaction payer
+ when the account's rent is below two epochs. diff --git a/snippets/compressible-rent-explained.mdx b/snippets/compressible-rent-explained.mdx new file mode 100644 index 00000000..5d5ddc67 --- /dev/null +++ b/snippets/compressible-rent-explained.mdx @@ -0,0 +1,8 @@ +Compressible means +1. The rent-exemption for the Solana account is sponsored by Light Protocol +2. Rent is paid per rent-epoch instead: 388 lamports for 1.5h. + * The account creator pays the initial rent for 24h by default (11,000 lamports). + * Transfers top up the account with rent for 3h by default (766 lamports),
when the account's rent is below two epochs. +3. "Inactive" accounts, where rent is below one epoch, are compressed and the rent-exemption can be claimed by the rent sponsor. +4. Transfers to the account "re-activate" it with the same state (decompress). +5. The account creation and transfers to reactivate incur additional 11,000 lamports for compression & protocol incentive. diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index be5fdaf6..716f43b4 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -1,5 +1,3 @@ -Define `CompressibleParamsInfos` with accounts for the default rent config: - ```rust use light_compressed_token_sdk::ctoken::CompressibleParamsInfos; @@ -10,8 +8,6 @@ let compressible_params = CompressibleParamsInfos::new( ); ``` -#### Accounts -
diff --git a/snippets/break-even-calculator.jsx b/snippets/jsx/break-even-calculator.jsx similarity index 99% rename from snippets/break-even-calculator.jsx rename to snippets/jsx/break-even-calculator.jsx index 91a7112f..8aa8ddcd 100644 --- a/snippets/break-even-calculator.jsx +++ b/snippets/jsx/break-even-calculator.jsx @@ -52,7 +52,7 @@ export const BreakEvenCalculator = () => { }; return ( -
+
{/* Inputs */}
diff --git a/snippets/compressible-rent-calculator.jsx b/snippets/jsx/compressible-rent-calculator.jsx similarity index 87% rename from snippets/compressible-rent-calculator.jsx rename to snippets/jsx/compressible-rent-calculator.jsx index 08111d8d..fa408024 100644 --- a/snippets/compressible-rent-calculator.jsx +++ b/snippets/jsx/compressible-rent-calculator.jsx @@ -3,6 +3,7 @@ export const CompressibleRentCalculator = () => { const [lamportsPerWrite, setLamportsPerWrite] = useState(766); const [showCustomHours, setShowCustomHours] = useState(false); const [showCustomLamports, setShowCustomLamports] = useState(false); + const [showFormula, setShowFormula] = useState(false); const DATA_LEN = 260; const BASE_RENT = 128; @@ -49,7 +50,7 @@ export const CompressibleRentCalculator = () => { }; return ( -
+
{/* Prepaid Epochs in Hours */}
@@ -185,13 +186,24 @@ export const CompressibleRentCalculator = () => {
- {/* Formula reference */} -
-
Total cost for {DATA_LEN}-byte cToken account:
- total_creation_cost = prepaid_rent + compression_incentive + tx_cost

- rent_per_epoch = base_rent + (data_len × lamports_per_byte_per_epoch)
- rent_per_epoch = {BASE_RENT} + ({DATA_LEN} × {LAMPORTS_PER_BYTE_PER_EPOCH}) = {rentPerEpoch} lamports
- compression_incentive = {COMPRESSION_INCENTIVE.toLocaleString()} lamports + {/* Formula reference - toggleable */} +
+ + {showFormula && ( +
+
Total cost for {DATA_LEN}-byte cToken account:
+ total_creation_cost = prepaid_rent + compression_incentive + tx_cost

+ rent_per_epoch = base_rent + (data_len × lamports_per_byte_per_epoch)
+ rent_per_epoch = {BASE_RENT} + ({DATA_LEN} × {LAMPORTS_PER_BYTE_PER_EPOCH}) = {rentPerEpoch} lamports
+ compression_incentive = {COMPRESSION_INCENTIVE.toLocaleString()} lamports +
+ )}
diff --git a/snippets/jsx/ctoken-vs-spl-calculator.jsx b/snippets/jsx/ctoken-vs-spl-calculator.jsx new file mode 100644 index 00000000..ec7ac342 --- /dev/null +++ b/snippets/jsx/ctoken-vs-spl-calculator.jsx @@ -0,0 +1,139 @@ +export const CTokenVsSplCalculator = () => { + const [numAccounts, setNumAccounts] = useState(100000); + const [showCustomAccounts, setShowCustomAccounts] = useState(false); + + const ACCOUNT_STORAGE_OVERHEAD = 128; + const LAMPORTS_PER_BYTE = 6960; + const DATA_LEN = 165; // SPL token account size + const CTOKEN_DEFAULT_CREATION_COST = 22208; // Default rent config: 11,000 rent + 11,000 compression + 208 base + const LAMPORTS_PER_SOL = 1_000_000_000; + + const ACCOUNTS_MAX = 1000000; + + const splCost = numAccounts * (ACCOUNT_STORAGE_OVERHEAD + DATA_LEN) * LAMPORTS_PER_BYTE; + const ctokenCost = numAccounts * CTOKEN_DEFAULT_CREATION_COST; + const savings = splCost - ctokenCost; + const savingsPercent = ((savings / splCost) * 100).toFixed(1); + + const handleAccountsChange = (value) => { + const num = Math.max(1, Math.min(1000000, Number.parseInt(value) || 1)); + setNumAccounts(num); + }; + + const formatSOL = (lamports) => { + const sol = lamports / LAMPORTS_PER_SOL; + if (sol >= 1000) { + return sol.toLocaleString(undefined, { maximumFractionDigits: 2 }); + } else if (sol >= 1) { + return sol.toFixed(4); + } + return sol.toFixed(6); + }; + + const accountsPresets = [10000, 100000, 500000]; + + const SliderMarkers = ({ max, step }) => { + const marks = []; + for (let i = step; i < max; i += step) { + const percent = (i / max) * 100; + marks.push( +
+ ); + } + return <>{marks}; + }; + + return ( +
+
+ {/* Number of Accounts */} +
+
+ Number of Token Accounts +
+ {accountsPresets.map((a) => ( + + ))} + {showCustomAccounts ? ( + handleAccountsChange(e.target.value)} + className="w-24 px-2 py-1 text-right text-xs font-mono font-medium bg-blue-500/10 dark:bg-blue-500/20 border border-blue-500/50 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" + autoFocus + /> + ) : ( + + )} +
+
+
+ + {numAccounts.toLocaleString()} accounts + +
+ + { setNumAccounts(Number.parseInt(e.target.value)); setShowCustomAccounts(false); }} + className="relative w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm z-10" + /> +
+
+
+ + {/* Result Cards */} +
+
+
SPL Token
+
+ {formatSOL(splCost)} +
+
SOL
+
+ +
+
cToken
+
+ {formatSOL(ctokenCost)} +
+
SOL
+
+ +
+
Savings
+
+ {savingsPercent}% +
+
{formatSOL(savings)} SOL
+
+
+
+
+ ); +}; \ No newline at end of file diff --git a/snippets/solana-rent-calculator.jsx b/snippets/jsx/solana-rent-calculator.jsx similarity index 98% rename from snippets/solana-rent-calculator.jsx rename to snippets/jsx/solana-rent-calculator.jsx index 5afaa868..53c01bc9 100644 --- a/snippets/solana-rent-calculator.jsx +++ b/snippets/jsx/solana-rent-calculator.jsx @@ -13,7 +13,7 @@ export const SolanaRentCalculator = () => { }; return ( -
+
{/* Slider */}
diff --git a/snippets/token-account-compressed-vs-spl.jsx b/snippets/jsx/token-account-compressed-vs-spl.jsx similarity index 99% rename from snippets/token-account-compressed-vs-spl.jsx rename to snippets/jsx/token-account-compressed-vs-spl.jsx index 00a8c190..c584bc78 100644 --- a/snippets/token-account-compressed-vs-spl.jsx +++ b/snippets/jsx/token-account-compressed-vs-spl.jsx @@ -48,7 +48,7 @@ export const TokenAccountCompressedVsSpl = () => { }; return ( -
+
{/* Number of Accounts */}
diff --git a/snippets/jsx/token22-extensions-table.jsx b/snippets/jsx/token22-extensions-table.jsx new file mode 100644 index 00000000..4002034d --- /dev/null +++ b/snippets/jsx/token22-extensions-table.jsx @@ -0,0 +1,177 @@ +export const Token22ExtensionsTable = () => { + const [activeFilter, setActiveFilter] = useState('all'); + const [selectedInstruction, setSelectedInstruction] = useState(null); + + // Instructions data + const anchorInstructions = [ + 'create_token_pool', + 'add_token_pool', + 'mint_to', + 'batch_compress', + 'compress_spl_token_account', + 'transfer', + 'approve', + 'revoke', + 'freeze', + 'thaw', + 'burn', + ]; + + const pinocchioInstructions = [ + 'CTokenTransfer', + 'CreateAssociatedTokenAccount', + 'CreateAssociatedTokenAccountIdempotent', + 'CreateTokenAccount', + 'CloseTokenAccount', + 'Transfer2', + 'MintAction', + 'Claim', + 'WithdrawFundingPool', + ]; + + const allInstructions = [...anchorInstructions, ...pinocchioInstructions]; + + // Extensions with runtime constraints (cannot transfer compressed) + const runtimeConstraintExtensions = [ + 'MintCloseAuthority', + 'TransferFeeConfig', + 'DefaultAccountState', + 'PermanentDelegate', + 'TransferHook', + 'Pausable', + 'ConfidentialTransferMint', + 'ConfidentialTransferFeeConfig', + 'ConfidentialMintBurn', + ]; + + // All 16 allowed extensions + const extensions = [ + // Metadata extensions (no constraints) + { name: 'MetadataPointer', description: '-', instructions: allInstructions }, + { name: 'TokenMetadata', description: '-', instructions: allInstructions }, + // Group extensions (no constraints) + { name: 'InterestBearingConfig', description: '-', instructions: allInstructions }, + { name: 'GroupPointer', description: '-', instructions: allInstructions }, + { name: 'GroupMemberPointer', description: '-', instructions: allInstructions }, + { name: 'TokenGroup', description: '-', instructions: allInstructions }, + { name: 'TokenGroupMember', description: '-', instructions: allInstructions }, + // Extensions with runtime constraints + { name: 'MintCloseAuthority', description: '-', instructions: allInstructions.filter(i => !['transfer', 'CTokenTransfer', 'Transfer2'].includes(i)) }, + { name: 'TransferFeeConfig', description: 'fees must be zero', instructions: allInstructions.filter(i => !['transfer', 'CTokenTransfer', 'Transfer2'].includes(i)) }, + { name: 'DefaultAccountState', description: 'any state allowed', instructions: allInstructions.filter(i => !['transfer', 'CTokenTransfer', 'Transfer2'].includes(i)) }, + { name: 'PermanentDelegate', description: '-', instructions: allInstructions.filter(i => !['transfer', 'CTokenTransfer', 'Transfer2'].includes(i)) }, + { name: 'TransferHook', description: 'program_id must be nil', instructions: allInstructions.filter(i => !['transfer', 'CTokenTransfer', 'Transfer2'].includes(i)) }, + { name: 'Pausable', description: '-', instructions: allInstructions.filter(i => !['transfer', 'CTokenTransfer', 'Transfer2'].includes(i)) }, + { name: 'ConfidentialTransferMint', description: 'initialized but not enabled', instructions: allInstructions.filter(i => !['transfer', 'CTokenTransfer', 'Transfer2'].includes(i)) }, + { name: 'ConfidentialTransferFeeConfig', description: 'fees must be zero', instructions: allInstructions.filter(i => !['transfer', 'CTokenTransfer', 'Transfer2'].includes(i)) }, + { name: 'ConfidentialMintBurn', description: 'initialized but not enabled', instructions: allInstructions.filter(i => !['transfer', 'CTokenTransfer', 'Transfer2'].includes(i)) }, + ]; + + const hasRuntimeConstraint = (extName) => { + return runtimeConstraintExtensions.includes(extName); + }; + + // Filter extensions based on active filter + const getFilteredExtensions = () => { + let filtered = extensions; + + if (selectedInstruction) { + filtered = filtered.filter(ext => ext.instructions.includes(selectedInstruction)); + } + + return filtered; + }; + + const filteredExtensions = getFilteredExtensions(); + + const handleFilterClick = (filter) => { + setActiveFilter(filter); + if (filter === 'all') { + setSelectedInstruction(null); + } + }; + + const FilterButton = ({ filter, label }) => ( + + ); + + return ( +
+
+ {/* Filter Buttons */} +
+ + + {/* Instruction Dropdown */} +
+ +
+ + + +
+
+
+ + {/* Results count */} +
+ Showing {filteredExtensions.length} of {extensions.length} extensions +
+ + {/* Table */} +
+ + + + + + + + + + {filteredExtensions.map((ext) => ( + + + + + + + ))} + +
Extension NameDescriptioncTokenCompressed Token
{ext.name}{ext.description}{hasRuntimeConstraint(ext.name) ? '-' : '✓'}
+
+
+ ); +}; diff --git a/snippets/compressed-pdas-guides-table.mdx b/snippets/overview-tables/compressed-pdas-guides-table.mdx similarity index 100% rename from snippets/compressed-pdas-guides-table.mdx rename to snippets/overview-tables/compressed-pdas-guides-table.mdx diff --git a/snippets/compressed-tokens-advanced-guides-table.mdx b/snippets/overview-tables/compressed-tokens-advanced-guides-table.mdx similarity index 100% rename from snippets/compressed-tokens-advanced-guides-table.mdx rename to snippets/overview-tables/compressed-tokens-advanced-guides-table.mdx diff --git a/snippets/compressed-tokens-guides-table.mdx b/snippets/overview-tables/compressed-tokens-guides-table.mdx similarity index 100% rename from snippets/compressed-tokens-guides-table.mdx rename to snippets/overview-tables/compressed-tokens-guides-table.mdx diff --git a/snippets/program-examples-table.mdx b/snippets/overview-tables/program-examples-table.mdx similarity index 100% rename from snippets/program-examples-table.mdx rename to snippets/overview-tables/program-examples-table.mdx diff --git a/snippets/compressed-pdas-program-setup.mdx b/snippets/setup/compressed-pdas-program-setup.mdx similarity index 100% rename from snippets/compressed-pdas-program-setup.mdx rename to snippets/setup/compressed-pdas-program-setup.mdx diff --git a/snippets/compressed-tokens-mint-prereq.mdx b/snippets/setup/compressed-tokens-mint-prereq.mdx similarity index 100% rename from snippets/compressed-tokens-mint-prereq.mdx rename to snippets/setup/compressed-tokens-mint-prereq.mdx diff --git a/snippets/development-environment-setup.mdx b/snippets/setup/development-environment-setup.mdx similarity index 100% rename from snippets/development-environment-setup.mdx rename to snippets/setup/development-environment-setup.mdx diff --git a/snippets/install-dependencies-codegroup.mdx b/snippets/setup/install-dependencies-codegroup.mdx similarity index 100% rename from snippets/install-dependencies-codegroup.mdx rename to snippets/setup/install-dependencies-codegroup.mdx diff --git a/snippets/setup-environment-tabs.mdx b/snippets/setup/setup-environment-tabs.mdx similarity index 100% rename from snippets/setup-environment-tabs.mdx rename to snippets/setup/setup-environment-tabs.mdx From d548f4439eaa4d4976af733491632f796fb2ce18 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 01:07:10 +0000 Subject: [PATCH 042/143] Remove obsolete ctoken-account-properties snippet - Delete ctoken-account-properties.mdx (replaced by compressible-rent-explained) - Update program-create-ctoken.mdx to use new rent documentation snippets - Refine compressible-rent-explained.mdx formatting --- .../ctoken/program-guides/program-create-ctoken.mdx | 4 ---- snippets/compressible-rent-explained.mdx | 3 +++ snippets/ctoken-account-properties.mdx | 12 ------------ 3 files changed, 3 insertions(+), 16 deletions(-) delete mode 100644 snippets/ctoken-account-properties.mdx diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index dd363499..281014f5 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -20,10 +20,6 @@ import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-c - -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). - - Find [full code examples at the end](#full-code-example). diff --git a/snippets/compressible-rent-explained.mdx b/snippets/compressible-rent-explained.mdx index 5d5ddc67..e0ee4d1c 100644 --- a/snippets/compressible-rent-explained.mdx +++ b/snippets/compressible-rent-explained.mdx @@ -6,3 +6,6 @@ Compressible means 3. "Inactive" accounts, where rent is below one epoch, are compressed and the rent-exemption can be claimed by the rent sponsor. 4. Transfers to the account "re-activate" it with the same state (decompress). 5. The account creation and transfers to reactivate incur additional 11,000 lamports for compression & protocol incentive. + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + \ No newline at end of file diff --git a/snippets/ctoken-account-properties.mdx b/snippets/ctoken-account-properties.mdx deleted file mode 100644 index 8a266ba9..00000000 --- a/snippets/ctoken-account-properties.mdx +++ /dev/null @@ -1,12 +0,0 @@ -1. cToken accounts hold token balances similar to SPL Token accounts. -2. cToken accounts are compressible, which means - * the **rent-exemption** for the Solana account is **sponsored** by Light Protocol, - * you pay **rent per rent-epoch** **instead** of the rent-exemption upfront, - * accounts with rent for less than 1 epoch are compressed (360 lamports), and - * compressed cToken accounts are decompressed with the same state when written to. - -3. **Rent for 24h** (16 epochs) is **11,000 lamports** and paid at account creation. -4. Transactions to cToken accounts top-up rent, when the account's rent is below two epochs (766 lamports). - * The top-up is configured to 766 lamports by default (rent for ~3h / 2 epochs). - * Write transactions to compressed cToken accounts decompress the account. -5. The account creation and writes to decompress cost additional 11,000 lamports for compression & protocol incentive. From 4bf70eab3aa766197dc3062e7c1df50dc3979188 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 02:16:35 +0000 Subject: [PATCH 043/143] Enhance cToken overview and add program guides reference table - Add ctoken-program-guides-table.mdx for navigation - Restructure cToken README with account struct and compressible rent - Add extensions.mdx placeholder page - Update c-token-program.mdx with compressible rent snippets - Fix compressed-tokens README structure with proper Steps - Update docs.json navigation to include extensions page --- compressed-token-program/c-token-program.mdx | 29 ++--- compressed-token-program/ctoken/README.mdx | 120 ++++++++++++------ .../ctoken/extensions.mdx | 5 + compressed-tokens/README.mdx | 8 +- docs.json | 6 +- snippets/ctoken-configure-rent.mdx | 5 +- .../compressed-tokens-guides-table.mdx | 2 +- .../ctoken-program-guides-table.mdx | 6 + 8 files changed, 114 insertions(+), 67 deletions(-) create mode 100644 compressed-token-program/ctoken/extensions.mdx create mode 100644 snippets/overview-tables/ctoken-program-guides-table.mdx diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx index ac42cf9e..8f7dd3cb 100644 --- a/compressed-token-program/c-token-program.mdx +++ b/compressed-token-program/c-token-program.mdx @@ -6,6 +6,9 @@ description: Overview of cMints, cTokens, and compressed token accounts. import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; + The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, cTokens and compressed tokens. @@ -33,8 +36,7 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t Solana account
    -
  • **Token accounts** derived from cMints
  • -
  • Can hold cTokens and SPL tokens
  • +
  • **Store tokens** of **cMints, SPL mints, or Token 2022 mints**
  • **Sponsored rent** exemption via [compressible extension](#compressible)
  • Use for **active token accounts** with frequent writes (trading, etc.)
@@ -282,20 +284,19 @@ Here is how cTokens and SPL tokens compare: ### Compressible Extension -cToken accounts include the compressible extension with rent-exemption sponsorship: +cToken accounts are compressible: -1. The protocol funds the account's rent exemption at creation. -2. The account creator top-ups the lamport balance with rent for at least two epochs (27,000 slots / 3h) -3. The transaction payer top-ups the lamports balance when the account's lamports balance is below 2 epochs. -4. The account will be compressed when inactive for a period of 27,000 slots (3h). -5. When the account is written to, it's automatically decompressed. + This extension makes cToken accounts 200x cheaper than SPL token accounts | | cToken | SPL | |-----------|--------|-----| | allocate | 0.000011 | 0.002 | - | rent for 24h | 0.000006 | - | + | rent for 24h | 0.000011 | - | + + + @@ -307,11 +308,9 @@ This extension makes cToken accounts 200x cheaper than SPL token accounts # Associated cToken Account -**Associated cToken** accounts follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): +**Associated cToken** accounts (cATAs) follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): * Each wallet needs its own cToken account to hold tokens from the same cMint. -* It's derived with the owner's address, program ID, and mint address. -* The only **difference** in ATA derivation is the **program ID parameter** used in the seeds. - +* The address for cATAs is deterministically derived with the owner’s address, cToken program ID, and mint address. ```rust let seeds = [ @@ -326,10 +325,6 @@ let seeds = [ Find the [source code to associated cToken accounts here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/create_associated_token_account.rs). - -cToken ATAs can implement the **[compressible extension](/compressible/compressible) for sponsored rent exemption**. - - # Compressed Token Account Compressed token accounts store token balance, owner, and other information like SPL and cTokens. Any cToken or SPL token can be compressed/decompressed at will. diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx index d04f5aba..2f2bd5a6 100644 --- a/compressed-token-program/ctoken/README.mdx +++ b/compressed-token-program/ctoken/README.mdx @@ -1,64 +1,104 @@ --- title: Overview -description: "cTokens are Solana accounts that hold balances of cTokens and SPL tokens." +description: "Overview to cTokens and reference to client guides, program guides, and example repo." --- import { CTokenVsSplCalculator } from '/snippets/jsx/ctoken-vs-spl-calculator.jsx'; import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; +import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-guides-table.mdx'; +1. cToken & associated cToken accounts (cATAs) are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. +2. cToken accounts and cATAs are compressible with a default rent config that reduces creation cost. - - - - - - - - - - - - - - - -
Account TypeKey Features
**[cToken](#ctoken-account)**Solana account -
    -
  • **Token accounts** derived from cMints
  • -
  • Can hold cTokens and SPL tokens
  • -
  • **Sponsored rent** exemption via [compressible extension](#compressible)
  • -
  • Use for **active token accounts** with frequent writes (trading, etc.)
  • -
-
- -| | cToken Account | SPL Token Account | -|--|----------------|-------------------| -| Default Creation Cost | 22,208 lamports | 2,039,280 lamports | +| | cToken account | SPL token account | +|-----------|--------|-----| +| creation cost | 22,288 lamports | 2,039,280 lamports | + +## Account Struct + +The layout of cToken accounts mirrors the SPL token account layout. + + + + Diagram showing cToken Solana account structure with three components: Address (identifier for cToken account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Owner set to Compressed Token Program), and AccountData (containing Mint, Owner, Amount, and Extensions fields) + + + + ```rust + /// Ctoken account structure (with extensions). + pub struct CToken { + pub mint: Pubkey, + pub owner: Pubkey, + pub amount: u64, + pub delegate: Option, // instruction not implemented yet + pub state: u8, + pub is_native: Option, + pub delegated_amount: u64, // instruction not implemented yet + pub close_authority: Option, + pub extensions: Option>, // Optional extensions e.g. compressible + } + ``` + + + + +Find the [source code of cToken here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken_struct.rs). + ## Compressible Rent -1. The rent-exemption for the Solana account is sponsored by Light Protocol -2. Rent is paid per rent-epoch instead: 388 lamports for 1.5h. - * The account creator pays the initial rent for 24h by default (11,000 lamports). - * Transfers top up the account with rent for 3h by default (766 lamports),
when the account’s rent is below two epochs. -3. "Inactive" accounts, where rent is below one epoch, are compressed and the rent-exemption can be claimed by the rent sponsor. -4. Transfers to a compressed cATA "re-activate" the account with the same state (decompress). -5. The account creation and transfers to reactivate incur additional 11,000 lamports for compression & protocol incentive. + + + + + +1. Set the hours to determine the amount of prepaid rent +2. Set the lamports per write a transaction payer will pay for top ups + +```rust +CompressibleParamsInfos{ + compressible_config, + rent_sponsor, + system_program, + pre_pay_num_epochs: 35, + lamports_per_write: Some(788), + compress_to_account_pubkey: None, + token_account_version: TokenDataVersion::ShaFlat, + } +``` + - + +## Get started +The guides and examples use the `light_compressed_token_sdk`. -## Client Guides + + +### Client Guides table for client guides -## Program Guides + + +### Program Guides + + + -table for program guides -## Examples + + +### Example Repo add github link + + ## Next Steps Follow our client or program guides for cTokens. diff --git a/compressed-token-program/ctoken/extensions.mdx b/compressed-token-program/ctoken/extensions.mdx new file mode 100644 index 00000000..9605c297 --- /dev/null +++ b/compressed-token-program/ctoken/extensions.mdx @@ -0,0 +1,5 @@ +--- +title: Token 2022 Extensions for cTokens +sidebarTitle: Token 2022 Extensions +description: Overview to Token 2022 extensions supported by cTokens. +--- diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx index f26c45ea..f1df15be 100644 --- a/compressed-tokens/README.mdx +++ b/compressed-tokens/README.mdx @@ -51,9 +51,7 @@ Compressed token accounts store token balance, owner, and other information like Leading **wallets** like Phantom and Backpack **support compressed tokens**. The UI does not distinguish between SPL and compressed tokens. - - -### Get Started +# Get Started @@ -75,7 +73,7 @@ Leading **wallets** like Phantom and Backpack **support compressed tokens**. The - + ### Basic Guides @@ -83,7 +81,7 @@ Leading **wallets** like Phantom and Backpack **support compressed tokens**. The -## Advanced Guides +### Advanced Guides diff --git a/docs.json b/docs.json index 313c6ee3..894ee130 100644 --- a/docs.json +++ b/docs.json @@ -147,7 +147,8 @@ ] } ] - } + }, + "compressed-token-program/ctoken/extensions" ] }, { @@ -576,7 +577,8 @@ ] } ] - } + }, + "compressed-token-program/ctoken/extensions" ] }, { diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index 716f43b4..75254c04 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -39,11 +39,12 @@ let compressible_params = CompressibleParamsInfos::new( + + + We recommend to use default values, but you can customize the rent config based on the expected account activity. - - 1. Set the hours to determine the amount of prepaid rent 2. Set the lamports per write a transaction payer will pay for top ups diff --git a/snippets/overview-tables/compressed-tokens-guides-table.mdx b/snippets/overview-tables/compressed-tokens-guides-table.mdx index 4248fd80..9a914757 100644 --- a/snippets/overview-tables/compressed-tokens-guides-table.mdx +++ b/snippets/overview-tables/compressed-tokens-guides-table.mdx @@ -5,7 +5,7 @@ | [Transfer Compressed Tokens](/compressed-tokens/guides/how-to-transfer-compressed-token) | Move compressed tokens between compressed accounts | | [Decompress and Compress Tokens](/compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens) | Convert SPL tokens between regular and compressed format | | [Compress Complete SPL Token Accounts](/compressed-tokens/guides/how-to-compress-complete-spl-token-accounts) | Compress complete SPL token accounts and reclaim rent afterwards | -| [Create and Register a Mint Account for Compression](/compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression) | Create new SPL mint with token pool for compression | +| [Create a Mint with Token Pool for Compression](/compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression) | Create new SPL mint with token pool for compression | | [Create Token Pools for Mint Accounts](/compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts) | Create token pool for compression for existing SPL mints | | [Merge Compressed Accounts](/compressed-tokens/guides/how-to-merge-compressed-token-accounts) | Consolidate multiple compressed accounts of the same mint into one | | [Approve and Revoke Delegate Authority](/compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority) | Approve or revoke delegates for compressed token accounts | diff --git a/snippets/overview-tables/ctoken-program-guides-table.mdx b/snippets/overview-tables/ctoken-program-guides-table.mdx new file mode 100644 index 00000000..5e560a8a --- /dev/null +++ b/snippets/overview-tables/ctoken-program-guides-table.mdx @@ -0,0 +1,6 @@ +| | | +| :---- | :---------- | +| [Create cToken Accounts](/compressed-token-program/ctoken/program-guides/program-create-ctoken) | Uses `CompressibleParamsInfos` and `CreateCTokenAccountInfos` | +| [Create Associated cToken Accounts](/compressed-token-program/ctoken/program-guides/program-create-cATA) | Uses `CompressibleParamsInfos` and `CreateAssociatedTokenAccountInfos` | +| [Transfer Interface](/compressed-token-program/ctoken/program-guides/program-transfer-interface) | For transfers between cToken and SPL token accounts.
Uses `TransferInterface` and `with_spl_interface` | +| [Close cToken Accounts](/compressed-token-program/ctoken/program-guides/program-close-ctoken) | Uses `CloseCTokenAccountInfos` | From a548dec9fcb97ece3cf6f5e4deec758fe3a5c14c Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 02:34:00 +0000 Subject: [PATCH 044/143] Refine compressible rent documentation and cToken README - Clarify compressible rent explanation in snippets - Update cToken README with improved structure and wording - Add Token 2022 extension comment to struct - Move custom rent config recommendation to main content --- compressed-token-program/ctoken/README.mdx | 24 ++++++++-------------- snippets/compressible-rent-explained.mdx | 5 ++--- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx index 2f2bd5a6..3d89c020 100644 --- a/compressed-token-program/ctoken/README.mdx +++ b/compressed-token-program/ctoken/README.mdx @@ -8,8 +8,8 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-guides-table.mdx'; -1. cToken & associated cToken accounts (cATAs) are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. -2. cToken accounts and cATAs are compressible with a default rent config that reduces creation cost. +1. cToken & associated cToken accounts (cATAs) are **Solana accounts** that hold **balances of tokens** of **cMints, SPL mints, or Token 2022 mints**. +2. cToken accounts and cATAs are **compressible with a default rent config** that reduces creation cost. | | cToken account | SPL token account | |-----------|--------|-----| @@ -39,7 +39,7 @@ The layout of cToken accounts mirrors the SPL token account layout. pub is_native: Option, pub delegated_amount: u64, // instruction not implemented yet pub close_authority: Option, - pub extensions: Option>, // Optional extensions e.g. compressible + pub extensions: Option>, // Optional Token 2022 extensions } ``` @@ -51,26 +51,18 @@ Find the [source code of cToken here](https://github.com/Lightprotocol/light-pro ## Compressible Rent +The compressible extension implements a custom rent config for cTokens and cATAs: + - + +We recommend to use default values, but you can customize the rent config based on the expected account activity. + 1. Set the hours to determine the amount of prepaid rent 2. Set the lamports per write a transaction payer will pay for top ups -```rust -CompressibleParamsInfos{ - compressible_config, - rent_sponsor, - system_program, - pre_pay_num_epochs: 35, - lamports_per_write: Some(788), - compress_to_account_pubkey: None, - token_account_version: TokenDataVersion::ShaFlat, - } -``` - diff --git a/snippets/compressible-rent-explained.mdx b/snippets/compressible-rent-explained.mdx index e0ee4d1c..037f5f62 100644 --- a/snippets/compressible-rent-explained.mdx +++ b/snippets/compressible-rent-explained.mdx @@ -1,6 +1,5 @@ -Compressible means -1. The rent-exemption for the Solana account is sponsored by Light Protocol -2. Rent is paid per rent-epoch instead: 388 lamports for 1.5h. +1. The rent-exemption for the account creation is sponsored by Light Protocol. +2. Compressible rent is paid per rent-epoch: 388 lamports for 1.5h. * The account creator pays the initial rent for 24h by default (11,000 lamports). * Transfers top up the account with rent for 3h by default (766 lamports),
when the account's rent is below two epochs. 3. "Inactive" accounts, where rent is below one epoch, are compressed and the rent-exemption can be claimed by the rent sponsor. From f2e80736cdce7ddd536f5ce4526a87c63c4db41a Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 02:56:12 +0000 Subject: [PATCH 045/143] Simplify overview pages and move Token22 extensions table - Simplify cMint README with creation cost comparison - Streamline cToken README by removing redundant sections - Move Token22 extensions table to dedicated extensions.mdx page - Add cost comparison for cMint vs SPL mint accounts --- compressed-token-program/cmint/README.mdx | 53 +++++-------------- compressed-token-program/ctoken/README.mdx | 40 ++------------ .../ctoken/extensions.mdx | 29 ++++++++++ 3 files changed, 45 insertions(+), 77 deletions(-) diff --git a/compressed-token-program/cmint/README.mdx b/compressed-token-program/cmint/README.mdx index 50359e15..ae70edb6 100644 --- a/compressed-token-program/cmint/README.mdx +++ b/compressed-token-program/cmint/README.mdx @@ -5,46 +5,19 @@ description: Overview to cMint accounts. import { SolanaRentCalculator } from '/snippets/jsx/solana-rent-calculator.jsx'; -## Key Points - -A cMint is a compressed account that represents a token mint on Solana. cMints store the same global metadata as SPL token mints but without requiring rent. - - -## Extensions - -## Extensions - -cTokens support a number of Token 22 extensions: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Extension NameDescriptioncTokenCompressed Token
MetadataPointer-
TokenMetadata-
InterestBearingConfig-
GroupPointer-
GroupMemberPointer-
TokenGroup-
TokenGroupMember-
MintCloseAuthority--
TransferFeeConfigfees must be zero-
DefaultAccountStateany state allowed-
PermanentDelegate--
TransferHookprogram_id must be nil-
Pausable--
ConfidentialTransferMintinitialized but not enabled-
ConfidentialTransferFeeConfigfees must be zero-
ConfidentialMintBurninitialized but not enabled-
+1. A cMint is a compressed account that represents a token mint on Solana. +2. cMints are rent-free. + + +| | cMint account | SPL mint account | +|-----------|--------|-----| +| creation cost | 25,000 lamports | 1,461,600 lamports | + +## Account Struct + +The layout of cMint accounts mirrors the SPL mint account layout. + + ## Client Guides diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx index 3d89c020..ab78a569 100644 --- a/compressed-token-program/ctoken/README.mdx +++ b/compressed-token-program/ctoken/README.mdx @@ -1,6 +1,6 @@ --- title: Overview -description: "Overview to cTokens and reference to client guides, program guides, and example repo." +description: "Overview to cTokens and reference to client guides, program guides, and example repository." --- import { CTokenVsSplCalculator } from '/snippets/jsx/ctoken-vs-spl-calculator.jsx'; import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; @@ -8,47 +8,13 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-guides-table.mdx'; -1. cToken & associated cToken accounts (cATAs) are **Solana accounts** that hold **balances of tokens** of **cMints, SPL mints, or Token 2022 mints**. -2. cToken accounts and cATAs are **compressible with a default rent config** that reduces creation cost. +1. cToken & associated cToken accounts (cATAs) are **Solana accounts** that hold **balances** of **cMint, SPL mint, or Token 2022 mint** tokens. +2. cToken accounts and cATAs are **compressible** that reduces account creation cost. | | cToken account | SPL token account | |-----------|--------|-----| | creation cost | 22,288 lamports | 2,039,280 lamports | -## Account Struct - -The layout of cToken accounts mirrors the SPL token account layout. - - - - Diagram showing cToken Solana account structure with three components: Address (identifier for cToken account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Owner set to Compressed Token Program), and AccountData (containing Mint, Owner, Amount, and Extensions fields) - - - - ```rust - /// Ctoken account structure (with extensions). - pub struct CToken { - pub mint: Pubkey, - pub owner: Pubkey, - pub amount: u64, - pub delegate: Option, // instruction not implemented yet - pub state: u8, - pub is_native: Option, - pub delegated_amount: u64, // instruction not implemented yet - pub close_authority: Option, - pub extensions: Option>, // Optional Token 2022 extensions - } - ``` - - - - -Find the [source code of cToken here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken_struct.rs). - - ## Compressible Rent The compressible extension implements a custom rent config for cTokens and cATAs: diff --git a/compressed-token-program/ctoken/extensions.mdx b/compressed-token-program/ctoken/extensions.mdx index 9605c297..f5cb6bbe 100644 --- a/compressed-token-program/ctoken/extensions.mdx +++ b/compressed-token-program/ctoken/extensions.mdx @@ -3,3 +3,32 @@ title: Token 2022 Extensions for cTokens sidebarTitle: Token 2022 Extensions description: Overview to Token 2022 extensions supported by cTokens. --- + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Extension NameDescriptioncTokenCompressed Token
MetadataPointer-
TokenMetadata-
InterestBearingConfig-
GroupPointer-
GroupMemberPointer-
TokenGroup-
TokenGroupMember-
MintCloseAuthority--
TransferFeeConfigfees must be zero-
DefaultAccountStateany state allowed-
PermanentDelegate--
TransferHookprogram_id must be nil-
Pausable--
ConfidentialTransferMintinitialized but not enabled-
ConfidentialTransferFeeConfigfees must be zero-
ConfidentialMintBurninitialized but not enabled-
\ No newline at end of file From 3295e0978886891008e250e375c7ea0d2303c6a1 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 15:09:00 +0000 Subject: [PATCH 046/143] Add compressible rent example and detailed payment table - Add visual timeline example showing rent lifecycle in cToken README - Expand compressible-rent-explained with payment scenarios table - Document account creation, top-up, and load costs with payers - Add tooltips for compression mechanics explanation - Fix broken import paths in program-create-ctoken.mdx --- compressed-token-program/ctoken/README.mdx | 101 +++++++++++++++--- .../program-guides/program-create-ctoken.mdx | 9 +- snippets/compressible-rent-explained.mdx | 48 +++++++-- 3 files changed, 133 insertions(+), 25 deletions(-) diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx index ab78a569..7903e10b 100644 --- a/compressed-token-program/ctoken/README.mdx +++ b/compressed-token-program/ctoken/README.mdx @@ -8,7 +8,7 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-guides-table.mdx'; -1. cToken & associated cToken accounts (cATAs) are **Solana accounts** that hold **balances** of **cMint, SPL mint, or Token 2022 mint** tokens. +1. cToken & associated cToken accounts (cATAs) are **Solana accounts** that
**hold balances** of **cMint, SPL mint, or Token 2022 mint** tokens. 2. cToken accounts and cATAs are **compressible** that reduces account creation cost. | | cToken account | SPL token account | @@ -17,20 +17,97 @@ import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-g ## Compressible Rent -The compressible extension implements a custom rent config for cTokens and cATAs: +For cTokens and cATAs, the compressible extension implements a **custom rent config**: - - -We recommend to use default values, but you can customize the rent config based on the expected account activity. - - -1. Set the hours to determine the amount of prepaid rent -2. Set the lamports per write a transaction payer will pay for top ups - - - +### Example +```markdown expandable +┌─────────────┐ +│ Created │ +│ (24h rent) │ +├─────────────┤ +│ Active │ +└──────┬──────┘ + │ T+0 + ▼ +┌─────────────┐ +│ Transfer │ +│ (16h rent) │ +│ no top-up │ +├─────────────┤ +│ Active │ +└──────┬──────┘ + │ T+8h + ▼ +┌─────────────┐ +│ Transfer │ +│ (10h rent) │ +│ no top-up │ +├─────────────┤ +│ Active │ +└──────┬──────┘ + │ T+14h + ▼ +┌─────────────┐ +│ Transfer │ +│(4h→7h rent) │ +│ top-up │ +├─────────────┤ +│ Active │ +└──────┬──────┘ + │ T+20h + ▼ +┌─────────────┐ +│ Transfer │ +│ (5h rent) │ +│ no top-up │ +├─────────────┤ +│ Active │ +└──────┬──────┘ + │ T+22h + ▼ +┌─────────────┐ +│ No activity │ +│ (1h rent) │ +├─────────────┤ +│Compressible │ +└──────┬──────┘ + │ T+28h + ▼ +┌─────────────┐ +│ Compressed │ +│ — │ +├─────────────┤ +│ Inactive │ +└──────┬──────┘ + │ T+29h + ▼ +┌─────────────┐ +│ Transfer │ +│ (3h rent) │ +│ top-up │ +├─────────────┤ +│ Reactivated │ +└──────┬──────┘ + │ T+32h + ▼ +┌─────────────┐ +│ No activity │ +│ (1h rent) │ +├─────────────┤ +│Compressible │ +└──────┬──────┘ + │ T+34h + ▼ +┌─────────────┐ +│ Compressed │ +│ — │ +├─────────────┤ +│ Inactive │ +└─────────────┘ + T+35h +``` ## Get started diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 281014f5..bae9fdd8 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -3,15 +3,14 @@ title: Create cToken Accounts description: Program guide to create cToken accounts with step-by-step implementation and full code examples. --- -import CTokenCreateAccountsList from '/snippets/accounts-list/ctoken-create-accounts-list.mdx'; +import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; +import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; -import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; +import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; -import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CTokenAccountProperties from '/snippets/ctoken-account-properties.mdx'; import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -## Key Points - 1. cToken accounts are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. 2. cToken accounts are compressible with a default rent config. diff --git a/snippets/compressible-rent-explained.mdx b/snippets/compressible-rent-explained.mdx index 037f5f62..a470caa3 100644 --- a/snippets/compressible-rent-explained.mdx +++ b/snippets/compressible-rent-explained.mdx @@ -1,10 +1,42 @@ 1. The rent-exemption for the account creation is sponsored by Light Protocol. -2. Compressible rent is paid per rent-epoch: 388 lamports for 1.5h. - * The account creator pays the initial rent for 24h by default (11,000 lamports). - * Transfers top up the account with rent for 3h by default (766 lamports),
when the account's rent is below two epochs. +2. Compressible rent is paid per rent-epoch: 388 lamports for 1.5h,
+paid by account creator and transaction payers to keep accounts "active". 3. "Inactive" accounts, where rent is below one epoch, are compressed and the rent-exemption can be claimed by the rent sponsor. -4. Transfers to the account "re-activate" it with the same state (decompress). -5. The account creation and transfers to reactivate incur additional 11,000 lamports for compression & protocol incentive. - -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). - \ No newline at end of file +4. Transfers to inactive accounts "load" it with the same state (decompress). + +Compressible rent is paid in three scenarios: + + + + + + + + + + + + + + + + + + + + + + + + + + +
AmountPayer
Account Creation + Total: 22,208 lamports
+ 11,208 lamports (24h rent)
+ 11,000 lamports (compression incentive)
+
Account creator
Top ups
(when rent < 2 epochs)
766 lamports (3h rent)Transaction payer
Load Account + Total: 22,208 lamports
+ 11,208 lamports (24h rent)
+ 11,000 lamports (compression incentive)
+
Transaction payer
\ No newline at end of file From a02c596fc758d72adc1844c4cdd972136634c23f Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 15:27:05 +0000 Subject: [PATCH 047/143] Refactor cToken guides with shared intro snippets - Create reusable intro snippets in snippets/ctoken-guides/ - Add ctoken-intro.mdx, cata-intro.mdx, close-intro.mdx snippets - Add transfer-interface-intro.mdx and ctoken-client-prerequisites.mdx - Update all client and program guides to use shared snippets - Add full code example structure with test setup to client guides - Simplify guide structure by extracting common content --- .../client-guides/client-close-ctoken.mdx | 66 +++++++++ .../client-guides/client-create-cATA.mdx | 90 ++++++------ .../client-guides/client-create-ctoken.mdx | 130 ++++++++++++------ .../client-transfer-interface.mdx | 66 +++++++++ .../program-guides/program-close-ctoken.mdx | 7 +- .../program-guides/program-create-cATA.mdx | 12 +- .../program-guides/program-create-ctoken.mdx | 10 +- .../program-transfer-interface.mdx | 38 +---- snippets/ctoken-guides/cata-intro.mdx | 11 ++ snippets/ctoken-guides/close-intro.mdx | 5 + .../ctoken-client-prerequisites.mdx | 21 +++ snippets/ctoken-guides/ctoken-intro.mdx | 10 ++ .../transfer-interface-intro.mdx | 36 +++++ 13 files changed, 364 insertions(+), 138 deletions(-) create mode 100644 snippets/ctoken-guides/cata-intro.mdx create mode 100644 snippets/ctoken-guides/close-intro.mdx create mode 100644 snippets/ctoken-guides/ctoken-client-prerequisites.mdx create mode 100644 snippets/ctoken-guides/ctoken-intro.mdx create mode 100644 snippets/ctoken-guides/transfer-interface-intro.mdx diff --git a/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx index eed901d7..12147563 100644 --- a/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx @@ -2,9 +2,74 @@ title: Close cToken Accounts description: Client guide to close cToken accounts with step-by-step implementation and full code examples. --- +import CloseIntro from '/snippets/ctoken-guides/close-intro.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; + + + + + + +```rust +let close = CloseAccount::new( + payer.pubkey(), + account, + destination, + owner, +); + +close.invoke()?; +``` + + + + +```typescript close_ctoken.ts +// TODO: TypeScript SDK coming soon +``` + + + + +# Full Code Example + +Run the full code example: +1. Clone the repository +2. Run `cargo test` + + + +### Prerequisites + + + + + + +### Close cToken Account + + + + +```rust close_ctoken.rs expandable +// TODO: Full code example +``` + + + + +```typescript close_ctoken.ts expandable +// TODO: TypeScript SDK coming soon +``` + + + + + # Next Steps + Close cToken Accounts + diff --git a/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx b/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx index bc990f21..5a79af68 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx @@ -2,59 +2,71 @@ title: Create Associated cToken Accounts description: Client guide to create associated cToken accounts (cATAs) with step-by-step implementation and full code examples. --- +import CAtaIntro from '/snippets/ctoken-guides/cata-intro.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; + + -```rust create_cata.rs -use light_compressed_token_sdk::ctoken::{ - derive_ctoken_ata, CompressibleParams, CreateAssociatedTokenAccount, -}; -use light_client::{indexer::Indexer, rpc::Rpc}; -use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; - -async fn create_compressed_ata( - rpc: &mut (impl Rpc + Indexer), - payer: &Keypair, - owner: Pubkey, - mint_pda: Pubkey, -) -> Pubkey { - // Derive the cToken ATA address - let (ata_address, _bump) = derive_ctoken_ata(&owner, &mint_pda); - - // Create ATA instruction - let compressible_params = CompressibleParams::default(); - let create_ata = CreateAssociatedTokenAccount::new( - payer.pubkey(), - owner, - mint_pda, - compressible_params, - ); - let instruction = create_ata.instruction().unwrap(); - - // Send transaction - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer]) - .await - .unwrap(); - - println!("cATA created!"); - println!("cATA address: {}", ata_address); - println!("Owner: {}", owner); - println!("Mint: {}", mint_pda); - - ata_address -} +```rust +let (ata_address, _bump) = derive_ctoken_ata(&owner, &mint_pda); + +let create_ata = CreateAssociatedTokenAccount::new( + payer.pubkey(), + owner, + mint_pda, + CompressibleParams::default(), +); +let instruction = create_ata.instruction()?; ``` ```typescript create_cata.ts -// TODO: Add TypeScript example +// TODO: TypeScript SDK coming soon +``` + + + + +# Full Code Example + +Run the full code example: +1. Clone the repository +2. Run `cargo test` + + + +### Prerequisites + + + + + + +### Create Associated cToken Account + + + + +```rust create_cata.rs expandable +// TODO: Full code example +``` + + + + +```typescript create_cata.ts expandable +// TODO: TypeScript SDK coming soon ``` + + # Next Steps diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx index 0cc0cbfa..8180526c 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx @@ -3,8 +3,10 @@ title: Create cToken Accounts description: "Guide to create cToken accounts with CreateCTokenAccount builder." --- import CTokenAccountProperties from '/snippets/ctoken-account-properties.mdx'; +import CTokenIntro from '/snippets/ctoken-guides/ctoken-intro.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; -cToken accounts are Solana accounts. + @@ -41,27 +43,8 @@ Run the full code example by ### Prerequisites -You need a compressed mint (cMint) before creating cToken accounts. + - - -**Dependencies** - -```toml Cargo.toml -[dependencies] -light-compressed-token-sdk = "0.1" -light-client = "0.1" -light-ctoken-types = "0.1" -solana-sdk = "2.2" -borsh = "0.10" -tokio = { version = "1.36", features = ["full"] } -``` - -**Create cMint First** - -See [Create a Compressed Mint](/compressed-token-program/ctoken/client-guides/client-create-cmint) for the full guide. - - @@ -73,18 +56,30 @@ See [Create a Compressed Mint](/compressed-token-program/ctoken/client-guides/cl ```rust create_ctoken_account.rs expandable use borsh::BorshDeserialize; use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::{CreateCTokenAccount, CompressibleParams}; +use light_compressed_token_sdk::ctoken::{CompressibleParams, CreateCTokenAccount}; use light_ctoken_types::state::CToken; -use solana_sdk::{signature::Keypair, signer::Signer, pubkey::Pubkey}; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use solana_sdk::{signature::Keypair, signer::Signer}; +use light_client::indexer::{AddressWithTree, Indexer}; +use light_compressed_token_sdk::ctoken::{CreateCMint, CreateCMintParams}; +use solana_sdk::{pubkey::Pubkey}; + + +#[tokio::test(flavor = "multi_thread")] +async fn test_create_ctoken_account() { + // Create simulated test environment with Light programs + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + + // Create compressed mint first (prerequisite) + let (mint, _compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; -async fn create_ctoken_account( - rpc: &mut impl Rpc, - payer: &Keypair, - mint: Pubkey, - owner: Pubkey, -) -> Pubkey { // Step 1: Generate new keypair for the cToken account let account = Keypair::new(); + let owner = payer.pubkey(); // Step 2: Build instruction using SDK builder let instruction = CreateCTokenAccount::new( @@ -98,24 +93,79 @@ async fn create_ctoken_account( .unwrap(); // Step 3: Send transaction - rpc.create_and_send_transaction( - &[instruction], - &payer.pubkey(), - &[payer, &account], - ) - .await - .unwrap(); + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &account]) + .await + .unwrap(); // Step 4: Verify account creation let account_data = rpc.get_account(account.pubkey()).await.unwrap().unwrap(); let ctoken_state = CToken::deserialize(&mut &account_data.data[..]).unwrap(); - assert_eq!(ctoken_state.mint, mint); - assert_eq!(ctoken_state.owner, owner); + assert_eq!(ctoken_state.mint, mint.to_bytes()); + assert_eq!(ctoken_state.owner, owner.to_bytes()); assert_eq!(ctoken_state.amount, 0); - println!("cToken account created: {}", account.pubkey()); - account.pubkey() +} +pub async fn create_compressed_mint( + rpc: &mut R, + payer: &Keypair, + decimals: u8, +) -> (Pubkey, [u8; 32]) { + let mint_signer = Keypair::new(); + let address_tree = rpc.get_address_tree_v2(); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; + + // Derive compression address + let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + &mint_signer.pubkey(), + &address_tree.tree, + ); + + let mint_pda = + light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + + // Get validity proof for the address + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![AddressWithTree { + address: compression_address, + tree: address_tree.tree, + }], + None, + ) + .await + .unwrap() + .value; + + // Build params + let params = CreateCMintParams { + decimals, + address_merkle_tree_root_index: rpc_result.addresses[0].root_index, + mint_authority: payer.pubkey(), + proof: rpc_result.proof.0.unwrap(), + compression_address, + mint: mint_pda, + freeze_authority: None, + extensions: None, + }; + + // Create instruction + let create_cmint = CreateCMint::new( + params, + mint_signer.pubkey(), + payer.pubkey(), + address_tree.tree, + output_queue, + ); + let instruction = create_cmint.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) + .await + .unwrap(); + + (mint_pda, compression_address) } ``` diff --git a/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx b/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx index e080260b..01dfede1 100644 --- a/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx +++ b/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx @@ -2,6 +2,72 @@ title: Transfer Interface description: Client guide for the unified transfer interface for cToken <> SPL token transfers with step-by-step implementation and full code examples. --- +import TransferInterfaceIntro from '/snippets/ctoken-guides/transfer-interface-intro.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; + + + + + + +```rust +let transfer = TransferInterface::new( + amount, + source_account, + destination_account, + authority, + payer, + compressed_token_program_authority, +); + +transfer.invoke()?; +``` + + + + +```typescript transfer_interface.ts +// TODO: TypeScript SDK coming soon +``` + + + + +# Full Code Example + +Run the full code example: +1. Clone the repository +2. Run `cargo test` + + + +### Prerequisites + + + + + + +### Transfer Interface + + + + +```rust transfer_interface.rs expandable +// TODO: Full code example +``` + + + + +```typescript transfer_interface.ts expandable +// TODO: TypeScript SDK coming soon +``` + + + + + # Next Steps diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index b13f6679..315652ce 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -4,12 +4,9 @@ description: Program guide to close cToken accounts via CPI to the cToken Progra --- import CloseAccountInfosAccountsList from '/snippets/accounts-list/close-account-infos-accounts-list.mdx'; +import CloseIntro from '/snippets/ctoken-guides/close-intro.mdx'; -1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. -2. cToken accounts can be closed - * by the account owner at any time. - * by the `compression_authority` - when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). + Find [full code examples at the end](#full-code-example). diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx index 0c290e73..ba058bcb 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx @@ -7,17 +7,9 @@ import CTokenCreateATAAccountsList from '/snippets/accounts-list/ctoken-create-a import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; -import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; -import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; +import CAtaIntro from '/snippets/ctoken-guides/cata-intro.mdx'; -1. Associated cToken accounts (cATA) are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. -2. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -3. cATAs accounts are compressible with a default rent config. - - - - - + Find [full code examples at the end](#full-code-example). diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index bae9fdd8..71c8a61a 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -9,15 +9,9 @@ import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx' import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; import CTokenAccountProperties from '/snippets/ctoken-account-properties.mdx'; -import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; +import CTokenIntro from '/snippets/ctoken-guides/ctoken-intro.mdx'; -1. cToken accounts are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. -2. cToken accounts are compressible with a default rent config. - - - - - + Find [full code examples at the end](#full-code-example). diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index 21a4c629..5e5f1167 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -6,43 +6,9 @@ description: Program guide for transfers between cToken and SPL token accounts v import TransferInterfaceAccountsListCtoken1 from '/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx'; import TransferInterfaceAccountsListSpl2 from '/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx'; +import TransferInterfaceIntro from '/snippets/ctoken-guides/transfer-interface-intro.mdx'; - - - - - - - - - - - - - - - -
**cToken → cToken Account** -
    -
  • Transfers cTokens between cToken accounts
  • -
-
**SPL token → cToken Account** -
    -
  • Transfers SPL tokens to cToken accounts
  • -
  • SPL tokens are locked in interface PDA
  • -
  • cTokens are minted to cToken account
  • -
-
**cToken → SPL Account** -
    -
  • Releases SPL tokens from interface PDA to SPL account
  • -
  • Burns cTokens in source cToken account
  • -
-
- - -* For example, **SPL → cToken** can be used for transfers from Alice's SPL token account to her own cToken account. -* You can use this to **convert existing SPL tokens to cTokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the cToken program](/compressed-token-program/ctoken/README). - + Find [full code examples at the end](#full-code-example). diff --git a/snippets/ctoken-guides/cata-intro.mdx b/snippets/ctoken-guides/cata-intro.mdx new file mode 100644 index 00000000..31f9b971 --- /dev/null +++ b/snippets/ctoken-guides/cata-intro.mdx @@ -0,0 +1,11 @@ +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; + +1. Associated cToken accounts (cATA) are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. +2. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. +3. cATAs accounts are compressible with a default rent config. + + + + + diff --git a/snippets/ctoken-guides/close-intro.mdx b/snippets/ctoken-guides/close-intro.mdx new file mode 100644 index 00000000..c1c44dc9 --- /dev/null +++ b/snippets/ctoken-guides/close-intro.mdx @@ -0,0 +1,5 @@ +1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. +2. cToken accounts can be closed + * by the account owner at any time. + * by the `compression_authority` + when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). diff --git a/snippets/ctoken-guides/ctoken-client-prerequisites.mdx b/snippets/ctoken-guides/ctoken-client-prerequisites.mdx new file mode 100644 index 00000000..a96fc1d9 --- /dev/null +++ b/snippets/ctoken-guides/ctoken-client-prerequisites.mdx @@ -0,0 +1,21 @@ +import CliInstall from '/snippets/versions/cli-install-0.27.1-alpha.2.mdx'; + + + +**CLI Installation** + + + +**Dependencies** + +```toml Cargo.toml +[dependencies] +light-compressed-token-sdk = "0.1" +light-client = "0.1" +light-ctoken-types = "0.1" +solana-sdk = "2.2" +borsh = "0.10" +tokio = { version = "1.36", features = ["full"] } +``` + + diff --git a/snippets/ctoken-guides/ctoken-intro.mdx b/snippets/ctoken-guides/ctoken-intro.mdx new file mode 100644 index 00000000..f2ba35d3 --- /dev/null +++ b/snippets/ctoken-guides/ctoken-intro.mdx @@ -0,0 +1,10 @@ +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; + +1. cToken accounts are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. +2. cToken accounts are compressible with a default rent config. + + + + + diff --git a/snippets/ctoken-guides/transfer-interface-intro.mdx b/snippets/ctoken-guides/transfer-interface-intro.mdx new file mode 100644 index 00000000..e0624da6 --- /dev/null +++ b/snippets/ctoken-guides/transfer-interface-intro.mdx @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + +
**cToken → cToken Account** +
    +
  • Transfers cTokens between cToken accounts
  • +
+
**SPL token → cToken Account** +
    +
  • Transfers SPL tokens to cToken accounts
  • +
  • SPL tokens are locked in interface PDA
  • +
  • cTokens are minted to cToken account
  • +
+
**cToken → SPL Account** +
    +
  • Releases SPL tokens from interface PDA to SPL account
  • +
  • Burns cTokens in source cToken account
  • +
+
+ + +* For example, **SPL → cToken** can be used for transfers from Alice's SPL token account to her own cToken account. +* You can use this to **convert existing SPL tokens to cTokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the cToken program](/compressed-token-program/ctoken/README). + From 688c41da1fab9c37848b6ba9f9950452f0fabddb Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 15:41:39 +0000 Subject: [PATCH 048/143] Improve client-create-ctoken guide structure and add full example - Restructure guide with clear 3-step flow explanation - Add complete test code with create_compressed_mint helper - Include git clone and cargo test instructions - Fix tab structure for better code organization - Add account verification step with CToken deserialization --- .../client-guides/client-create-ctoken.mdx | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx index 8180526c..c68b3419 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx @@ -10,9 +10,11 @@ import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-pre - - +# Full Code Example + +1. Create a test cMint. You can use existing SPL or Token 2022 mints as well. +2. Build instruction using `CreateCTokenAccount` with the default rent config. ```rust let instruction = CreateCTokenAccount::new( payer.pubkey(), @@ -22,23 +24,18 @@ let instruction = CreateCTokenAccount::new( CompressibleParams::default(), ).instruction()?; ``` +3. Send transaction & verify cToken account creation with `get_account`.
+cToken accounts are Solana accounts and use your familiar RPC methods. -
- + + -```typescript create-ctoken-account.ts -// TODO: TypeScript SDK coming soon +```bash +git clone https://github.com/Lightprotocol/light-protocol +cd light-protocol +cargo test ``` - - - -# Full Code Example - -Run the full code example by -1. clone xy -2. Run `cargo test` - ### Prerequisites @@ -50,10 +47,7 @@ Run the full code example by ### Create cToken Account - - - -```rust create_ctoken_account.rs expandable +```rust use borsh::BorshDeserialize; use light_client::rpc::Rpc; use light_compressed_token_sdk::ctoken::{CompressibleParams, CreateCTokenAccount}; @@ -169,17 +163,23 @@ pub async fn create_compressed_mint( } ``` + + + + -```typescript create_ctoken_account.ts expandable +```typescript +// TODO: TypeScript SDK coming soon +``` + +```typescript expandable // TODO: TypeScript SDK coming soon ```
-
-
# Next Steps From 508ac874b37da5074db59248b59b0e85d90cd3e9 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 15:51:47 +0000 Subject: [PATCH 049/143] Add CompressibleParams accordion and improve code explanation - Add accordion for CompressibleParams with rent config details - Remove ctoken-account-properties import (no longer exists) - Clarify use of existing SPL or Token 2022 mints - Add import statement to code example - Fix tab structure for TypeScript placeholder --- .../ctoken/client-guides/client-create-ctoken.mdx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx index c68b3419..88b9cf7f 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx @@ -1,21 +1,21 @@ --- title: Create cToken Accounts -description: "Guide to create cToken accounts with CreateCTokenAccount builder." +description: "Client guide to create cToken accounts with CreateCTokenAccount builder." --- -import CTokenAccountProperties from '/snippets/ctoken-account-properties.mdx'; import CTokenIntro from '/snippets/ctoken-guides/ctoken-intro.mdx'; import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; +import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; - - # Full Code Example 1. Create a test cMint. You can use existing SPL or Token 2022 mints as well. -2. Build instruction using `CreateCTokenAccount` with the default rent config. +2. Build instruction with the default rent config in `CompressibleParams::default()`. ```rust +use light_compressed_token_sdk::ctoken::{CompressibleParams, CreateCTokenAccount}; + let instruction = CreateCTokenAccount::new( payer.pubkey(), account.pubkey(), @@ -24,6 +24,11 @@ let instruction = CreateCTokenAccount::new( CompressibleParams::default(), ).instruction()?; ``` + +This struct contains the default rent config and associated accounts. + + + 3. Send transaction & verify cToken account creation with `get_account`.
cToken accounts are Solana accounts and use your familiar RPC methods. From 419d140ba81f408ebbf89769914b49b16dd69208 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 17:41:18 +0000 Subject: [PATCH 050/143] Enhance cMint README and refactor setup snippets - Add cMint account struct diagram and comparison with SPL mints - Add custom rent config accordion to client-create-ctoken guide - Create reusable Rust setup snippets for dependencies and environment - Add client-custom-rent-config.mdx snippet - Simplify ctoken-client-prerequisites with reusable snippets - Update compressible rent config wording for clarity - Fix "balances of tokens of" to "token balances of" across guides --- compressed-token-program/cmint/README.mdx | 117 ++++++++++++++++-- .../client-guides/client-create-ctoken.mdx | 25 ++-- snippets/compressible-default-rent-config.mdx | 3 +- snippets/ctoken-guides/cata-intro.mdx | 2 +- .../client-custom-rent-config.mdx | 71 +++++++++++ .../ctoken-client-prerequisites.mdx | 23 ++-- snippets/ctoken-guides/ctoken-intro.mdx | 2 +- snippets/setup/rust-install-dependencies.mdx | 12 ++ .../setup/rust-setup-environment-tabs.mdx | 85 +++++++++++++ 9 files changed, 298 insertions(+), 42 deletions(-) create mode 100644 snippets/ctoken-guides/client-custom-rent-config.mdx create mode 100644 snippets/setup/rust-install-dependencies.mdx create mode 100644 snippets/setup/rust-setup-environment-tabs.mdx diff --git a/compressed-token-program/cmint/README.mdx b/compressed-token-program/cmint/README.mdx index ae70edb6..779d2d12 100644 --- a/compressed-token-program/cmint/README.mdx +++ b/compressed-token-program/cmint/README.mdx @@ -1,23 +1,124 @@ --- title: Overview -description: Overview to cMint accounts. +description: Overview to cMints and reference to client guides, program guides, and example repository. --- import { SolanaRentCalculator } from '/snippets/jsx/solana-rent-calculator.jsx'; -1. A cMint is a compressed account that represents a token mint on Solana. -2. cMints are rent-free. - +1. cMints uniquely represent a token on Solana and store its global metadata. +2. cMints are compressed accounts and rent-free. +3. Tokens created from cMints are cTokens. The mint's address serves as the token's unique identifier. | | cMint account | SPL mint account | |-----------|--------|-----| -| creation cost | 25,000 lamports | 1,461,600 lamports | +| creation cost | 25,000 lamports | 1,461,600 lamports | ## Account Struct -The layout of cMint accounts mirrors the SPL mint account layout. - - + + + + Diagram showing cMint compressed account structure with three components: Hash (identifier for cMint in purple box), Account (struct containing BaseMint with SPL-compatible fields, cMint Data for program state, and optional Extensions for Token Metadata), and BasemintData (containing Supply, Decimals, Mint Authority, and Freeze Authority fields) with Token Metadata extension + + + + ```rust + pub struct CompressedMint { + // SPL mint layout + pub base: BaseMint, + // cMint state used by the Compressed Token Program + pub metadata: CompressedMintMetadata + // Field for Token Metadata extension + pub extensions: Option>, + } + ``` + + + + +Find the [source code of cMints here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/mint/compressed_mint.rs). + + +The `metadata` field is used by the Compressed Token Program to store the internal state of a cMint. + +The `BaseMint` field replicates the field layout and serialization format of [SPL Mint accounts](https://solana.com/docs/tokens#mint-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens and mints. + +Here is how cMints and SPL mints compare: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldcMintSPL Mint
mint_authority
supply
decimals
is_initialized
freeze_authority
cMint Data-
Extensionsvia Token-2022
+
+ + ```rust + pub struct BaseMint { + /// Optional authority used to mint new tokens. The mint authority may only + /// be provided during mint creation. If no mint authority is present + /// then the mint has a fixed supply and no further tokens may be + /// minted. + pub mint_authority: Option, + /// Total supply of tokens. + pub supply: u64, + /// Number of base 10 digits to the right of the decimal place. + pub decimals: u8, + /// Is initialized - for SPL compatibility + pub is_initialized: bool, + /// Optional authority to freeze token accounts. + pub freeze_authority: Option, + } + ``` + +
+ +# Get Started ## Client Guides diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx index 88b9cf7f..4922b484 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx @@ -5,41 +5,38 @@ description: "Client guide to create cToken accounts with CreateCTokenAccount bu import CTokenIntro from '/snippets/ctoken-guides/ctoken-intro.mdx'; import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; +import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; # Full Code Example -1. Create a test cMint. You can use existing SPL or Token 2022 mints as well. -2. Build instruction with the default rent config in `CompressibleParams::default()`. + + + +1. The example creates a test cMint. You can use existing cMints, SPL or Token 2022 mints as well. +2. Build instruction with `CreateCTokenAccount`. It automatically includes the default rent config. ```rust -use light_compressed_token_sdk::ctoken::{CompressibleParams, CreateCTokenAccount}; +use light_compressed_token_sdk::ctoken::{CreateCTokenAccount}; let instruction = CreateCTokenAccount::new( payer.pubkey(), account.pubkey(), mint, owner, - CompressibleParams::default(), ).instruction()?; ``` - -This struct contains the default rent config and associated accounts. - + + + + 3. Send transaction & verify cToken account creation with `get_account`.
cToken accounts are Solana accounts and use your familiar RPC methods. - - -```bash -git clone https://github.com/Lightprotocol/light-protocol -cd light-protocol -cargo test -``` diff --git a/snippets/compressible-default-rent-config.mdx b/snippets/compressible-default-rent-config.mdx index d53d32b3..6c574713 100644 --- a/snippets/compressible-default-rent-config.mdx +++ b/snippets/compressible-default-rent-config.mdx @@ -1,4 +1,3 @@ ### Default Rent Config 1. At account creation, you pay ~22,208 lamports for 24h of rent
and compression incentive (the rent-exemption is sponsored by the protocol) -2. Top-ups are 766 lamports for 3h of rent, paid by the transaction payer
- when the account's rent is below two epochs. +2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 766 lamports when the account's rent is below 3h. diff --git a/snippets/ctoken-guides/cata-intro.mdx b/snippets/ctoken-guides/cata-intro.mdx index 31f9b971..49ccef20 100644 --- a/snippets/ctoken-guides/cata-intro.mdx +++ b/snippets/ctoken-guides/cata-intro.mdx @@ -1,7 +1,7 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -1. Associated cToken accounts (cATA) are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. +1. Associated cToken accounts (cATA) are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. 2. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. 3. cATAs accounts are compressible with a default rent config. diff --git a/snippets/ctoken-guides/client-custom-rent-config.mdx b/snippets/ctoken-guides/client-custom-rent-config.mdx new file mode 100644 index 00000000..586d13c4 --- /dev/null +++ b/snippets/ctoken-guides/client-custom-rent-config.mdx @@ -0,0 +1,71 @@ +import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; + + +We recommend to use default values, but you can customize the rent config based on the expected account activity. + + +1. Set the hours to determine the amount of prepaid rent +2. Set the lamports per write a transaction payer will pay for top ups + +```rust +use light_compressed_token_sdk::ctoken::{ + CompressibleParams, + CreateCTokenAccount, + ctoken_v1_config_pda, // helper to derive config PDA + ctoken_v1_rent_sponsor_pda // helper to derive rent sponsor PDA +}; +use light_ctoken_types::state::TokenDataVersion; + +let compressible_params = CompressibleParams { + compressible_config: ctoken_v1_config_pda(), + rent_sponsor: ctoken_v1_rent_sponsor_pda(), + pre_pay_num_epochs: 35, // ~52h + lamports_per_write: Some(788), + compress_to_account_pubkey: None, + token_account_version: TokenDataVersion::ShaFlat, +}; + +let instruction = CreateCTokenAccount::new( + payer.pubkey(), + account.pubkey(), + mint, + owner, + compressible_params, +).instruction()?; +``` + + + + + + + + + + + + + + + + + + + + + + + + + + +
Compressible Config + Protocol PDA that stores account rent config. +
Rent Sponsor + - CToken program PDA that fronts rent exemption at creation.
+ - Claims rent when account compresses. +
System ProgramSolana System Program to create the on-chain account.
+ + + + diff --git a/snippets/ctoken-guides/ctoken-client-prerequisites.mdx b/snippets/ctoken-guides/ctoken-client-prerequisites.mdx index a96fc1d9..f868b7a2 100644 --- a/snippets/ctoken-guides/ctoken-client-prerequisites.mdx +++ b/snippets/ctoken-guides/ctoken-client-prerequisites.mdx @@ -1,21 +1,12 @@ -import CliInstall from '/snippets/versions/cli-install-0.27.1-alpha.2.mdx'; +import RustInstallDependencies from '/snippets/setup/rust-install-dependencies.mdx'; +import RustSetupEnvironment from '/snippets/setup/rust-setup-environment-tabs.mdx'; - + -**CLI Installation** - - + + -**Dependencies** +**Developer Environment** -```toml Cargo.toml -[dependencies] -light-compressed-token-sdk = "0.1" -light-client = "0.1" -light-ctoken-types = "0.1" -solana-sdk = "2.2" -borsh = "0.10" -tokio = { version = "1.36", features = ["full"] } -``` + - diff --git a/snippets/ctoken-guides/ctoken-intro.mdx b/snippets/ctoken-guides/ctoken-intro.mdx index f2ba35d3..e67271b5 100644 --- a/snippets/ctoken-guides/ctoken-intro.mdx +++ b/snippets/ctoken-guides/ctoken-intro.mdx @@ -1,7 +1,7 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -1. cToken accounts are Solana accounts that hold balances of tokens of cMints, SPL mints, or Token 2022 mints. +1. cToken accounts are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. 2. cToken accounts are compressible with a default rent config. diff --git a/snippets/setup/rust-install-dependencies.mdx b/snippets/setup/rust-install-dependencies.mdx new file mode 100644 index 00000000..334c04f4 --- /dev/null +++ b/snippets/setup/rust-install-dependencies.mdx @@ -0,0 +1,12 @@ +```toml Cargo.toml +[dependencies] +light-compressed-token-sdk = "0.1" +light-client = "0.1" +light-ctoken-types = "0.1" +solana-sdk = "2.2" +borsh = "0.10" +tokio = { version = "1.36", features = ["full"] } + +[dev-dependencies] +light-program-test = "0.1" # For in-memory tests with LiteSVM +``` diff --git a/snippets/setup/rust-setup-environment-tabs.mdx b/snippets/setup/rust-setup-environment-tabs.mdx new file mode 100644 index 00000000..59ff7f8e --- /dev/null +++ b/snippets/setup/rust-setup-environment-tabs.mdx @@ -0,0 +1,85 @@ +import CliInstall from '/snippets/versions/cli-install-0.27.1-alpha.2.mdx'; + + + + +Test without a light-validator. + +```bash +# Initialize project +cargo init my-light-project +cd my-light-project + +# Run tests +cargo test +``` + +```rust +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use solana_sdk::signer::Signer; + +#[tokio::test] +async fn test_example() { + // In-memory test environment - no external validator + let mut rpc = LightProgramTest::new(ProgramTestConfig::default()) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + println!("Payer: {}", payer.pubkey()); +} +``` + + + + +Connects to a local test validator. + + + +```bash +# Initialize project +cargo init my-light-project +cd my-light-project + +# Start local test validator (in separate terminal) +light test-validator +``` + +```rust +use light_client::rpc::{LightClient, LightClientConfig, Rpc}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Connects to http://localhost:8899 + let rpc = LightClient::new(LightClientConfig::local()).await?; + + let slot = rpc.get_slot().await?; + println!("Current slot: {}", slot); + + Ok(()) +} +``` + + + + +Replace `` with your actual API key. [Get your API key here](https://www.helius.dev/zk-compression). + +```rust +use light_client::rpc::{LightClient, LightClientConfig, Rpc}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let rpc_url = "https://devnet.helius-rpc.com?api-key="; + let rpc = LightClient::new( + LightClientConfig::new(rpc_url.to_string(), None, None) + ).await?; + + println!("Connected to Devnet"); + Ok(()) +} +``` + + + From b8d010a8f92cc8422cf302389aef14b9eadfd670 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 17:59:23 +0000 Subject: [PATCH 051/143] Add create-cmint guide and refactor cToken documentation structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add compressed-token-program/cmint/create-cmint.mdx to navigation - Enhance cMint README with account struct diagram and extension details - Extract shared snippets for Rust setup and custom rent config - Improve cToken client guide prerequisites and rent configuration - Update compressible rent config explanation for clarity - Standardize cToken/cATA intro language across guides 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- compressed-token-program/cmint/README.mdx | 44 +- .../cmint/create-cmint.mdx | 498 ++++++++++++++++++ .../program-guides/program-create-cmint.mdx | 3 +- docs.json | 2 + 4 files changed, 538 insertions(+), 9 deletions(-) create mode 100644 compressed-token-program/cmint/create-cmint.mdx diff --git a/compressed-token-program/cmint/README.mdx b/compressed-token-program/cmint/README.mdx index 779d2d12..c753d6ab 100644 --- a/compressed-token-program/cmint/README.mdx +++ b/compressed-token-program/cmint/README.mdx @@ -12,7 +12,9 @@ import { SolanaRentCalculator } from '/snippets/jsx/solana-rent-calculator.jsx'; | | cMint account | SPL mint account | |-----------|--------|-----| | creation cost | 25,000 lamports | 1,461,600 lamports | - + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + ## Account Struct @@ -118,20 +120,48 @@ Here is how cMints and SPL mints compare:
+### Extensions + +* cMints only support a custom Token Metadata extension. +* Use more Token 2022 extensions via a [Token 2022 mint account with cTokens](/compressed-token-program/ctoken/extensions). + +```rust +let token_metadata = ExtensionInstructionData::TokenMetadata( + TokenMetadataInstructionData { + update_authority: Some(authority.to_bytes().into()), + name: b"My Token".to_vec(), + symbol: b"MTK".to_vec(), + uri: b"https://example.com/metadata.json".to_vec(), + additional_metadata: Some(vec![ + AdditionalMetadata { + key: b"category".to_vec(), + value: b"utility".to_vec(), + }, + ]), + } +); +``` # Get Started -## Client Guides + + + -table for client guides -## Program Guides + + +### Guides -table for program guides -## Examples +table for + + +### Examples add github link + + -## Next Steps +# Next Steps Follow our client or program guides for cMints. diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx new file mode 100644 index 00000000..8f36a72f --- /dev/null +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -0,0 +1,498 @@ +--- +title: Create cMint +description: Program and client guide to create a cMint with token metadata via CPI to the cToken Program. Includes step-by-step implementation and full code examples. +--- + +import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; + +1. cMints uniquely represent a token on Solana and store its global metadata. +2. cMints are compressed accounts and rent-free. +3. Tokens created from cMints are cTokens. + + +Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). + + +# Get Started + + + + +```typescript +// TODO: TypeScript SDK coming soon +``` + + + + + +```rust +// TODO: Rust client example coming soon +``` + + + + + + +Find [full code examples at the end](#full-code-example). + + + + +### Configure Token Metadata + +```rust +use light_ctoken_types::{ + instructions::extensions::{ + token_metadata::TokenMetadataInstructionData, + ExtensionInstructionData, + }, + state::AdditionalMetadata, +}; + +let token_metadata = ExtensionInstructionData::TokenMetadata( + TokenMetadataInstructionData { + update_authority: Some(authority.to_bytes().into()), + name: b"My Token".to_vec(), + symbol: b"MTK".to_vec(), + uri: b"https://example.com/metadata.json".to_vec(), + additional_metadata: Some(vec![ + AdditionalMetadata { + key: b"category".to_vec(), + value: b"utility".to_vec(), + }, + ]), + }, +); +``` + + +**Fields must be set at cMint creation.** +* Standard fields (`name`, `symbol`, `uri`) can be updated by `update_authority`. +* For `additional_metadata`, only existing keys can be modified or removed. New keys cannot be added after creation. + + + + + +### Configure cMint + +Set `decimals`, `mint_authority`, `freeze_authority`, and pass the `token_metadata` from the previous step. + +```rust +use light_compressed_token_sdk::ctoken::CreateCMintParams; + +let cmint_params = CreateCMintParams { + decimals: data.decimals, + address_merkle_tree_root_index: data.address_merkle_tree_root_index, + mint_authority: data.mint_authority, + proof: data.proof, + compression_address: data.compression_address, + mint: data.mint, + freeze_authority: data.freeze_authority, + extensions: data.token_metadata, +}; +``` + +* The client passes a validity proof that proves the cMint address does not exist in the address tree where it will be stored. +* You can safely ignore `compression_address` and `address_merkle_tree_root_index`. The client passes these for proof verification. + + + + + + +### System Accounts + +Include system accounts such as the Light System Program required to interact with compressed state. +The client includes them in the instruction. + + + + + +```rust +use light_compressed_token_sdk::ctoken::SystemAccountInfos; + +let system_accounts = SystemAccountInfos { + light_system_program: light_system_program.clone(), + cpi_authority_pda: cpi_authority_pda.clone(), + registered_program_pda: registered_program_pda.clone(), + account_compression_authority: account_compression_authority.clone(), + account_compression_program: account_compression_program.clone(), + system_program: system_program.clone(), +}; +``` + + + + +### Build Account Infos and CPI the cToken Program + +1. Pass the required accounts +2. Include `cmint_params` and `system_accounts` from the previous steps +3. Use `invoke` or `invoke_signed`: + * When `mint_signer` is an external keypair, use `invoke`. + * When `mint_signer` is a PDA, use `invoke_signed` with its seeds. + * When both `mint_signer` and `authority` are PDAs, use `invoke_signed` with both seeds. + + + + + +```rust +use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; + +CreateCMintAccountInfos { + mint_signer: mint_signer.clone(), + authority: authority.clone(), + payer: payer.clone(), + address_tree: address_tree.clone(), + output_queue: output_queue.clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + cmint_params, +} +.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; + +let account_infos = CreateCMintAccountInfos { + mint_signer: mint_signer.clone(), + authority: authority.clone(), + payer: payer.clone(), + address_tree: address_tree.clone(), + output_queue: output_queue.clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + cmint_params, +}; + +let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]]; +account_infos.invoke_signed(&[signer_seeds])?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; + +let account_infos = CreateCMintAccountInfos { + mint_signer: mint_signer.clone(), + authority: authority.clone(), + payer: payer.clone(), + address_tree: address_tree.clone(), + output_queue: output_queue.clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + cmint_params, +}; + +let mint_signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_signer_bump]]; +let authority_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[authority_bump]]; +account_infos.invoke_signed(&[mint_signer_seeds, authority_seeds])?; +``` + + + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_cmint.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::{ + ctoken::{ + CreateCMintAccountInfos, CreateCMintParams, ExtensionInstructionData, SystemAccountInfos, + }, + CompressedProof, +}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::ID; + +/// PDA seed for mint signer in invoke_signed variant +pub const MINT_SIGNER_SEED: &[u8] = b"mint_signer"; + +/// Instruction data for create compressed mint +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateCmintData { + pub decimals: u8, + pub address_merkle_tree_root_index: u16, + pub mint_authority: Pubkey, + pub proof: CompressedProof, + pub compression_address: [u8; 32], + pub mint: Pubkey, + pub freeze_authority: Option, + pub extensions: Option>, +} + +/// Handler for creating a compressed mint (invoke) +/// +/// Uses the CreateCMintAccountInfos builder pattern. This demonstrates how to: +/// 1. Build the CreateCMintParams struct from instruction data +/// 2. Build the CreateCMintAccountInfos with accounts +/// 3. Call invoke() which handles instruction building and CPI +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: mint_signer (signer) +/// - accounts[3]: payer (signer, also authority) +/// - accounts[4]: payer again (fee_payer in SDK) +/// - accounts[5]: cpi_authority_pda +/// - accounts[6]: registered_program_pda +/// - accounts[7]: account_compression_authority +/// - accounts[8]: account_compression_program +/// - accounts[9]: system_program +/// - accounts[10]: output_queue +/// - accounts[11]: address_tree +/// - accounts[12] (optional): cpi_context_account +pub fn process_create_cmint( + accounts: &[AccountInfo], + data: CreateCmintData, +) -> Result<(), ProgramError> { + if accounts.len() < 12 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Build the params + let params = CreateCMintParams { + decimals: data.decimals, + address_merkle_tree_root_index: data.address_merkle_tree_root_index, + mint_authority: data.mint_authority, + proof: data.proof, + compression_address: data.compression_address, + mint: data.mint, + freeze_authority: data.freeze_authority, + extensions: data.extensions, + }; + + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[5].clone(), + registered_program_pda: accounts[6].clone(), + account_compression_authority: accounts[7].clone(), + account_compression_program: accounts[8].clone(), + system_program: accounts[9].clone(), + }; + + // Build the account infos struct + // In this case, payer == authority (accounts[3]) + CreateCMintAccountInfos { + mint_signer: accounts[2].clone(), + authority: accounts[3].clone(), + payer: accounts[3].clone(), + address_tree: accounts[11].clone(), + output_queue: accounts[10].clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + } + .invoke()?; + + Ok(()) +} + +/// Handler for creating a compressed mint with PDA mint signer (invoke_signed) +/// +/// Uses the CreateCMintAccountInfos builder pattern with invoke_signed. +/// The mint_signer is a PDA derived from this program. +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: mint_signer (PDA, not signer - program signs) +/// - accounts[3]: payer (signer, also authority) +/// - accounts[4]: payer again (fee_payer in SDK) +/// - accounts[5]: cpi_authority_pda +/// - accounts[6]: registered_program_pda +/// - accounts[7]: account_compression_authority +/// - accounts[8]: account_compression_program +/// - accounts[9]: system_program +/// - accounts[10]: output_queue +/// - accounts[11]: address_tree +/// - accounts[12] (optional): cpi_context_account +pub fn process_create_cmint_invoke_signed( + accounts: &[AccountInfo], + data: CreateCmintData, +) -> Result<(), ProgramError> { + if accounts.len() < 12 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the mint signer + let (pda, bump) = Pubkey::find_program_address(&[MINT_SIGNER_SEED], &ID); + + // Verify the mint_signer account is the PDA we expect + if &pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); + } + + // Build the params + let params = CreateCMintParams { + decimals: data.decimals, + address_merkle_tree_root_index: data.address_merkle_tree_root_index, + mint_authority: data.mint_authority, + proof: data.proof, + compression_address: data.compression_address, + mint: data.mint, + freeze_authority: data.freeze_authority, + extensions: data.extensions, + }; + + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[5].clone(), + registered_program_pda: accounts[6].clone(), + account_compression_authority: accounts[7].clone(), + account_compression_program: accounts[8].clone(), + system_program: accounts[9].clone(), + }; + + // Build the account infos struct + // In this case, payer == authority (accounts[3]) + let account_infos = CreateCMintAccountInfos { + mint_signer: accounts[2].clone(), + authority: accounts[3].clone(), + payer: accounts[3].clone(), + address_tree: accounts[11].clone(), + output_queue: accounts[10].clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + }; + + // Invoke with PDA signing + let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]]; + account_infos.invoke_signed(&[signer_seeds])?; + + Ok(()) +} + +/// Handler for creating a compressed mint with PDA mint signer AND PDA authority (invoke_signed) +/// +/// Uses the SDK's CreateCMintAccountInfos with separate authority and payer accounts. +/// Both mint_signer and authority are PDAs signed by this program. +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: mint_signer (PDA from MINT_SIGNER_SEED, not signer - program signs) +/// - accounts[3]: authority (PDA from MINT_AUTHORITY_SEED, not signer - program signs) +/// - accounts[4]: fee_payer (signer) +/// - accounts[5]: cpi_authority_pda +/// - accounts[6]: registered_program_pda +/// - accounts[7]: account_compression_authority +/// - accounts[8]: account_compression_program +/// - accounts[9]: system_program +/// - accounts[10]: output_queue +/// - accounts[11]: address_tree +/// - accounts[12] (optional): cpi_context_account +pub fn process_create_cmint_with_pda_authority( + accounts: &[AccountInfo], + data: CreateCmintData, +) -> Result<(), ProgramError> { + use crate::mint_to_ctoken::MINT_AUTHORITY_SEED; + + if accounts.len() < 12 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the mint signer + let (mint_signer_pda, mint_signer_bump) = + Pubkey::find_program_address(&[MINT_SIGNER_SEED], &ID); + + // Derive the PDA for the authority + let (authority_pda, authority_bump) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); + + // Verify the mint_signer account is the PDA we expect + if &mint_signer_pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); + } + + // Verify the authority account is the PDA we expect + if &authority_pda != accounts[3].key { + return Err(ProgramError::InvalidSeeds); + } + + // Build the params - authority is the PDA + let params = CreateCMintParams { + decimals: data.decimals, + address_merkle_tree_root_index: data.address_merkle_tree_root_index, + mint_authority: authority_pda, // Use the derived PDA as authority + proof: data.proof, + compression_address: data.compression_address, + mint: data.mint, + freeze_authority: data.freeze_authority, + extensions: data.extensions, + }; + + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[5].clone(), + registered_program_pda: accounts[6].clone(), + account_compression_authority: accounts[7].clone(), + account_compression_program: accounts[8].clone(), + system_program: accounts[9].clone(), + }; + + // Build the account infos struct using SDK + let account_infos = CreateCMintAccountInfos { + mint_signer: accounts[2].clone(), + authority: accounts[3].clone(), + payer: accounts[4].clone(), + address_tree: accounts[11].clone(), + output_queue: accounts[10].clone(), + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + }; + + // Invoke with both PDAs signing + let mint_signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_signer_bump]]; + let authority_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[authority_bump]]; + account_infos.invoke_signed(&[mint_signer_seeds, authority_seeds])?; + + Ok(()) +} +``` + + + + + +# Next Steps + + + diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index 0a09e733..b4f9ee29 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -7,8 +7,7 @@ import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accoun 1. cMints uniquely represent a token on Solana and store its global metadata. 2. cMints are compressed accounts and rent-free. -3. The `mint_signer` keypair/PDA derives the mint address. -4. Tokens created from cMints are cTokens. +3. Tokens created from cMints are cTokens. Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). diff --git a/docs.json b/docs.json index 894ee130..33dc905a 100644 --- a/docs.json +++ b/docs.json @@ -63,6 +63,7 @@ "group": "cMint", "pages": [ "compressed-token-program/cmint/README", + "compressed-token-program/cmint/create-cmint", { "group": "Client Guides", "pages": [ @@ -492,6 +493,7 @@ "group": "cMint", "pages": [ "compressed-token-program/cmint/README", + "compressed-token-program/cmint/create-cmint", { "group": "Client Guides", "pages": [ From fec7d13b58e9f777bd1453968998b145586ad885 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 18:38:12 +0000 Subject: [PATCH 052/143] Add cToken unified guides and refactor documentation structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add create-ctoken.mdx, create-cata.mdx, transfer-interface.mdx, close-ctoken-account.mdx - Add create-cmint.mdx with complete program CPI examples - Update docs.json: hide legacy Client/Program Guide groups, add new unified pages - Refactor ctoken-configure-rent.mdx: extract CompressibleParams accordion - Update rust-setup-environment-tabs.mdx: simplify LightProgramTest description - Modify program-create-ctoken.mdx: update Next Steps to mint-to-ctoken guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../ctoken/close-ctoken-account.mdx | 190 +++++++++ .../ctoken/create-cata.mdx | 246 +++++++++++ .../ctoken/create-ctoken.mdx | 392 ++++++++++++++++++ .../program-guides/program-create-ctoken.mdx | 17 +- .../ctoken/transfer-interface.mdx | 301 ++++++++++++++ docs.json | 9 + snippets/ctoken-configure-rent.mdx | 23 - snippets/custom-rent-config.md | 23 + .../setup/rust-setup-environment-tabs.mdx | 2 +- 9 files changed, 1166 insertions(+), 37 deletions(-) create mode 100644 compressed-token-program/ctoken/close-ctoken-account.mdx create mode 100644 compressed-token-program/ctoken/create-cata.mdx create mode 100644 compressed-token-program/ctoken/create-ctoken.mdx create mode 100644 compressed-token-program/ctoken/transfer-interface.mdx create mode 100644 snippets/custom-rent-config.md diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx new file mode 100644 index 00000000..4f44a445 --- /dev/null +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -0,0 +1,190 @@ +--- +title: Close cToken Accounts +description: Program guide to close cToken accounts via CPI to the cToken Program with step-by-step implementation and full code examples. +--- + +import CloseAccountInfosAccountsList from '/snippets/accounts-list/close-account-infos-accounts-list.mdx'; + +1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. +2. cToken accounts can be closed + * by the account owner at any time. + * by the `compression_authority` + when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). + +# Get Started + + + + + + + + + + + + + +Find [a full code example at the end](#full-code-example). + + + + +### Build Account Infos and CPI the cToken Program + +1. Define the cToken account to close and where remaining lamports should go +2. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. + + + + +```rust +use light_compressed_token_sdk::ctoken::CloseAccountInfos; + +CloseAccountInfos { + token_program: token_program.clone(), + account: account.clone(), + destination: destination.clone(), + owner: owner.clone(), + rent_sponsor: Some(rent_sponsor.clone()), +} +.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::CloseAccountInfos; + +let close_account_infos = CloseAccountInfos { + token_program: token_program.clone(), + account: account.clone(), + destination: destination.clone(), + owner: owner.clone(), + rent_sponsor: Some(rent_sponsor.clone()), +}; + +let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; +close_account_infos.invoke_signed(&[signer_seeds])?; +``` + + + + + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/close.rs). + + +```rust expandable +use light_compressed_token_sdk::ctoken::CloseAccountInfos; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::{ID, TOKEN_ACCOUNT_SEED}; + +/// Handler for closing a compressed token account (invoke) +/// +/// Account order: +/// - accounts[0]: token_program (ctoken program) +/// - accounts[1]: account to close (writable) +/// - accounts[2]: destination for lamports (writable) +/// - accounts[3]: owner/authority (signer) +/// - accounts[4]: rent_sponsor (optional, writable) +pub fn process_close_account_invoke(accounts: &[AccountInfo]) -> Result<(), ProgramError> { + if accounts.len() < 4 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let rent_sponsor = if accounts.len() > 4 { + Some(accounts[4].clone()) + } else { + None + }; + + CloseAccountInfos { + token_program: accounts[0].clone(), + account: accounts[1].clone(), + destination: accounts[2].clone(), + owner: accounts[3].clone(), + rent_sponsor, + } + .invoke()?; + + Ok(()) +} + +/// Handler for closing a PDA-owned compressed token account (invoke_signed) +/// +/// Account order: +/// - accounts[0]: token_program (ctoken program) +/// - accounts[1]: account to close (writable) +/// - accounts[2]: destination for lamports (writable) +/// - accounts[3]: PDA owner/authority (not signer, program signs) +/// - accounts[4]: rent_sponsor (optional, writable) +pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<(), ProgramError> { + if accounts.len() < 4 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the authority + let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); + + // Verify the authority account is the PDA + if &pda != accounts[3].key { + return Err(ProgramError::InvalidSeeds); + } + + let rent_sponsor = if accounts.len() > 4 { + Some(accounts[4].clone()) + } else { + None + }; + + let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; + CloseAccountInfos { + token_program: accounts[0].clone(), + account: accounts[1].clone(), + destination: accounts[2].clone(), + owner: accounts[3].clone(), + rent_sponsor, + } + .invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + + + + +# Next Steps + + + + Close cToken Accounts + + + Learn about compressed tokens + + diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx new file mode 100644 index 00000000..4e7a2ec8 --- /dev/null +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -0,0 +1,246 @@ +--- +title: Create Associated cToken Accounts +description: Client and program guide to create associated cToken accounts with step-by-step implementation and full code examples. +--- + +import CTokenCreateATAAccountsList from '/snippets/accounts-list/ctoken-create-ata-accounts-list.mdx'; +import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; +import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; +import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; +import CAtaIntro from '/snippets/ctoken-guides/cata-intro.mdx'; +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; + +1. Associated cToken accounts (cATA) are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. +2. The address for cATAs is deterministically derived with the owner’s address, cToken program ID, and mint address. +3. cATAs are compressible with a default rent config. + 1. At account creation, you pay ~22,208 lamports for 24h of rent
and compression incentive (the rent-exemption is sponsored by the protocol) + 2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 766 lamports when the account's rent is below 3h. + + + + + +# Get Started + + + + +```typescript +// TODO: TypeScript SDK coming soon +``` + + + + + + + + + + +Find [a full code example at the end](#full-code-example). + + + + +### Define Rent Config Accounts + + + + + + + +### Build Account Infos and CPI the cToken Program + +1. Pass the required accounts +2. Include rent config from `compressible_params` +3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. + +The cATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike cToken accounts, owner and mint are passed as accounts, not in instruction data. + + + + + +```rust +use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; + +CreateAssociatedTokenAccount2Infos { + owner: owner.clone(), + mint: mint.clone(), + payer: payer.clone(), + associated_token_account: associated_token_account.clone(), + system_program: system_program.clone(), + bump: data.bump, + compressible: Some(compressible_params), + idempotent: false, +} +.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; + +let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; +CreateAssociatedTokenAccount2Infos { + owner: owner.clone(), + mint: mint.clone(), + payer: payer.clone(), + associated_token_account: associated_token_account.clone(), + system_program: system_program.clone(), + bump: data.bump, + compressible: Some(compressible_params), + idempotent: false, +} +.invoke_signed(&[signer_seeds])?; +``` + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::{ + CompressibleParamsInfos, CreateAssociatedTokenAccount2Infos, +}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::{ATA_SEED, ID}; + +/// Instruction data for create ATA V2 (owner/mint as accounts) +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateAta2Data { + pub bump: u8, + pub pre_pay_num_epochs: u8, + pub lamports_per_write: u32, +} + +/// Handler for creating ATA using V2 variant (invoke) +/// +/// Account order: +/// - accounts[0]: owner (readonly) +/// - accounts[1]: mint (readonly) +/// - accounts[2]: payer (signer, writable) +/// - accounts[3]: associated_token_account (writable) +/// - accounts[4]: system_program +/// - accounts[5]: compressible_config +/// - accounts[6]: rent_sponsor (writable) +pub fn process_create_ata2_invoke( + accounts: &[AccountInfo], + data: CreateAta2Data, +) -> Result<(), ProgramError> { + if accounts.len() < 7 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[5].clone(), + accounts[6].clone(), + accounts[4].clone(), + ); + + CreateAssociatedTokenAccount2Infos { + owner: accounts[0].clone(), + mint: accounts[1].clone(), + payer: accounts[2].clone(), + associated_token_account: accounts[3].clone(), + system_program: accounts[4].clone(), + bump: data.bump, + compressible: Some(compressible_params), + idempotent: false, + } + .invoke()?; + + Ok(()) +} + +/// Handler for creating ATA using V2 variant with PDA ownership (invoke_signed) +/// +/// Account order: +/// - accounts[0]: owner (PDA, readonly) +/// - accounts[1]: mint (readonly) +/// - accounts[2]: payer (PDA, writable, not signer - program signs) +/// - accounts[3]: associated_token_account (writable) +/// - accounts[4]: system_program +/// - accounts[5]: compressible_config +/// - accounts[6]: rent_sponsor (writable) +pub fn process_create_ata2_invoke_signed( + accounts: &[AccountInfo], + data: CreateAta2Data, +) -> Result<(), ProgramError> { + if accounts.len() < 7 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA that will act as payer + let (pda, bump) = Pubkey::find_program_address(&[ATA_SEED], &ID); + + // Verify the payer is the PDA + if &pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); + } + + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[5].clone(), + accounts[6].clone(), + accounts[4].clone(), + ); + + let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; + CreateAssociatedTokenAccount2Infos { + owner: accounts[0].clone(), + mint: accounts[1].clone(), + payer: accounts[2].clone(), // PDA + associated_token_account: accounts[3].clone(), + system_program: accounts[4].clone(), + bump: data.bump, + compressible: Some(compressible_params), + idempotent: false, + } + .invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + + + +# Next Steps + + + + Create Associated cToken Accounts + + + Mint cTokens + + diff --git a/compressed-token-program/ctoken/create-ctoken.mdx b/compressed-token-program/ctoken/create-ctoken.mdx new file mode 100644 index 00000000..e0bdd4fe --- /dev/null +++ b/compressed-token-program/ctoken/create-ctoken.mdx @@ -0,0 +1,392 @@ +--- +title: Create cToken Accounts +description: Program guide to create cToken accounts with step-by-step implementation and full code examples. +--- + +import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; +import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; +import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; +import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; +import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; +import CTokenAccountProperties from '/snippets/ctoken-account-properties.mdx'; +import CTokenIntro from '/snippets/ctoken-guides/ctoken-intro.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; +import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; + +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; + +1. cToken accounts are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. +2. cToken accounts are compressible with a default rent config. + 1. At account creation, you pay ~22,208 lamports for 24h of rent
and compression incentive (the rent-exemption is sponsored by the protocol) + 2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 766 lamports when the account's rent is below 3h. + + + + + +# Get Started + + + + +```typescript +// TODO: TypeScript SDK coming soon +``` + + + + + + +1. The example creates a test cMint. You can use existing cMints, SPL or Token 2022 mints as well. +2. Build the instruction with `CreateCTokenAccount`. It automatically includes the default rent config. +```rust +use light_compressed_token_sdk::ctoken::{CreateCTokenAccount}; + +let instruction = CreateCTokenAccount::new( + payer.pubkey(), + account.pubkey(), + mint, + owner, +).instruction()?; +``` + +3. Send transaction & verify cToken account creation with `get_account`.
+cToken accounts are Solana accounts and use your familiar RPC methods. + + + + + +### Prerequisites + + + + + + +### Create cToken Account + +```rust +use borsh::BorshDeserialize; +use light_client::rpc::Rpc; +use light_compressed_token_sdk::ctoken::{CompressibleParams, CreateCTokenAccount}; +use light_ctoken_types::state::CToken; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use solana_sdk::{signature::Keypair, signer::Signer}; +use light_client::indexer::{AddressWithTree, Indexer}; +use light_compressed_token_sdk::ctoken::{CreateCMint, CreateCMintParams}; +use solana_sdk::{pubkey::Pubkey}; + + +#[tokio::test(flavor = "multi_thread")] +async fn test_create_ctoken_account() { + // Create simulated test environment with Light programs + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + + // Create compressed mint first (prerequisite) + let (mint, _compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; + + // Step 1: Generate new keypair for the cToken account + let account = Keypair::new(); + let owner = payer.pubkey(); + + // Step 2: Build instruction using SDK builder + let instruction = CreateCTokenAccount::new( + payer.pubkey(), + account.pubkey(), + mint, + owner, + CompressibleParams::default(), + ) + .instruction() + .unwrap(); + + // Step 3: Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &account]) + .await + .unwrap(); + + // Step 4: Verify account creation + let account_data = rpc.get_account(account.pubkey()).await.unwrap().unwrap(); + let ctoken_state = CToken::deserialize(&mut &account_data.data[..]).unwrap(); + + assert_eq!(ctoken_state.mint, mint.to_bytes()); + assert_eq!(ctoken_state.owner, owner.to_bytes()); + assert_eq!(ctoken_state.amount, 0); + +} +pub async fn create_compressed_mint( + rpc: &mut R, + payer: &Keypair, + decimals: u8, +) -> (Pubkey, [u8; 32]) { + let mint_signer = Keypair::new(); + let address_tree = rpc.get_address_tree_v2(); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; + + // Derive compression address + let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + &mint_signer.pubkey(), + &address_tree.tree, + ); + + let mint_pda = + light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + + // Get validity proof for the address + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![AddressWithTree { + address: compression_address, + tree: address_tree.tree, + }], + None, + ) + .await + .unwrap() + .value; + + // Build params + let params = CreateCMintParams { + decimals, + address_merkle_tree_root_index: rpc_result.addresses[0].root_index, + mint_authority: payer.pubkey(), + proof: rpc_result.proof.0.unwrap(), + compression_address, + mint: mint_pda, + freeze_authority: None, + extensions: None, + }; + + // Create instruction + let create_cmint = CreateCMint::new( + params, + mint_signer.pubkey(), + payer.pubkey(), + address_tree.tree, + output_queue, + ); + let instruction = create_cmint.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) + .await + .unwrap(); + + (mint_pda, compression_address) +} +``` + + + + + +
+ + + + +Find [a full code example at the end](#full-code-example). + + + + +### Configure Rent + + + + + + +### Build Account Infos and CPI the cToken Program + +1. Pass the required accounts +2. Include rent config from `compressible_params` +3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. + + + +```rust +use light_compressed_token_sdk::ctoken::CreateCTokenAccountInfos; + +CreateCTokenAccountInfos { + payer: payer.clone(), + account: account.clone(), + mint: mint.clone(), + owner: data.owner, + compressible: Some(compressible_params), +} +.invoke()?; +``` + + + + + +```rust +use light_compressed_token_sdk::ctoken::CreateCTokenAccountInfos; + +let account_infos = CreateCTokenAccountInfos { + payer: payer.clone(), + account: account.clone(), + mint: mint.clone(), + owner: data.owner, + compressible: Some(compressible_params), +}; + +let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; +account_infos.invoke_signed(&[signer_seeds])?; +``` + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::{ID, TOKEN_ACCOUNT_SEED}; + +/// Instruction data for create token account +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct CreateTokenAccountData { + pub owner: Pubkey, + pub pre_pay_num_epochs: u8, + pub lamports_per_write: u32, +} + +/// Handler for creating a compressible token account (invoke) +/// +/// Uses the builder pattern from the ctoken module. This demonstrates how to: +/// 1. Build the account infos struct with compressible params +/// 2. Call the invoke() method which handles instruction building and CPI +/// +/// Account order: +/// - accounts[0]: payer (signer) +/// - accounts[1]: account to create (signer) +/// - accounts[2]: mint +/// - accounts[3]: compressible_config +/// - accounts[4]: system_program +/// - accounts[5]: rent_sponsor +pub fn process_create_token_account_invoke( + accounts: &[AccountInfo], + data: CreateTokenAccountData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Build the compressible params using constructor + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), + ); + + // Build the account infos struct + CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), + } + .invoke()?; + + Ok(()) +} + +/// Handler for creating a compressible token account with PDA ownership (invoke_signed) +/// +/// Account order: +/// - accounts[0]: payer (signer) +/// - accounts[1]: account to create (PDA, will be derived and verified) +/// - accounts[2]: mint +/// - accounts[3]: compressible_config +/// - accounts[4]: system_program +/// - accounts[5]: rent_sponsor +pub fn process_create_token_account_invoke_signed( + accounts: &[AccountInfo], + data: CreateTokenAccountData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the token account + let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); + + // Verify the account to create is the PDA + if &pda != accounts[1].key { + return Err(ProgramError::InvalidSeeds); + } + + // Build the compressible params using constructor + let compressible_params = CompressibleParamsInfos::new( + data.pre_pay_num_epochs, + data.lamports_per_write, + accounts[3].clone(), + accounts[5].clone(), + accounts[4].clone(), + ); + + // Build the account infos struct + let account_infos = CreateCTokenAccountInfos { + payer: accounts[0].clone(), + account: accounts[1].clone(), + mint: accounts[2].clone(), + owner: data.owner, + compressible: Some(compressible_params), + }; + + // Invoke with PDA signing + let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; + account_infos.invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + + +
+# Next Steps + + + + Create cToken Accounts + + + Mint cTokens + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index 71c8a61a..c28dbc86 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -194,21 +194,12 @@ pub fn process_create_token_account_invoke_signed( - Create cToken Accounts + Mint tokens to your cToken account - - Mint cTokens - - \ No newline at end of file + diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx new file mode 100644 index 00000000..e20e2ff5 --- /dev/null +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -0,0 +1,301 @@ +--- +title: Transfer Interface +description: Guide for transfers between cToken and SPL token accounts via CPI. The interface detects account types and invokes the right programs. +--- + +import TransferInterfaceAccountsListCtoken1 from '/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx'; +import TransferInterfaceAccountsListSpl2 from '/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx'; +import TransferInterfaceIntro from '/snippets/ctoken-guides/transfer-interface-intro.mdx'; + + + + + + + + + + + + + + + + +
**cToken → cToken Account** +
    +
  • Transfers cTokens between cToken accounts
  • +
+
**SPL token → cToken Account** +
    +
  • Transfers SPL tokens to cToken accounts
  • +
  • SPL tokens are locked in interface PDA
  • +
  • cTokens are minted to cToken account
  • +
+
**cToken → SPL Account** +
    +
  • Releases SPL tokens from interface PDA to SPL account
  • +
  • Burns cTokens in source cToken account
  • +
+
+ + +* For example, **SPL → cToken** can be used for transfers from Alice's SPL token account to her own cToken account. +* You can use this to **convert existing SPL tokens to cTokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the cToken program](/compressed-token-program/ctoken/README). + + +# Get Started + + + + +```typescript +// TODO: TypeScript SDK coming soon +``` + + + + + + + + + + +Find [a full code example at the end](#full-code-example). + + + + +### cToken Transfer Interface + +Define the number of cTokens / SPL tokens to transfer +- from which SPL or cToken account, and +- to which SPL or cToken account. + +```rust +use light_compressed_token_sdk::ctoken::TransferInterface; + +let mut transfer = TransferInterface::new( + data.amount, + source_account.clone(), + destination_account.clone(), + authority.clone(), + payer.clone(), + compressed_token_program_authority.clone(), +); +``` + + + + + + + + +### SPL Transfer Interface (Optional) + +The SPL transfer interface is only needed for SPL ↔ cToken transfers. +```rust +transfer = transfer.with_spl_interface( + Some(mint.clone()), + Some(spl_token_program.clone()), + Some(spl_interface_pda.clone()), + data.spl_interface_pda_bump, +)?; +``` + +SPL ↔ cToken transfers require a `spl_interface_pda`: + * **SPL → cToken**: SPL tokens are locked by the cToken Program in the PDA, cTokens are minted to cToken accounts + * **cToken → SPL**: cTokens are burned, SPL tokens transferred to SPL token accounts + +The interface PDA is derived from the `mint` pubkey and pool seed. + + + + + + + + + +### CPI the cToken Program + +CPI the cToken program to execute the transfer. +Use `invoke()`, or `invoke_signed()` when a CPI requires a PDA signer. + + + +```rust +transfer.invoke()?; +``` + + +```rust +let authority_seeds: &[&[u8]] = &[TRANSFER_INTERFACE_AUTHORITY_SEED, &[authority_bump]]; +transfer.invoke_signed(&[authority_seeds])?; +``` + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/transfer_interface.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::TransferInterface; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::ID; + +/// PDA seed for authority in invoke_signed variants +pub const TRANSFER_INTERFACE_AUTHORITY_SEED: &[u8] = b"transfer_interface_authority"; + +/// Instruction data for TransferInterface +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct TransferInterfaceData { + pub amount: u64, + /// Required for SPL<->cToken transfers, None for cToken->cToken + pub token_pool_pda_bump: Option, +} + +/// Handler for TransferInterface (invoke) +/// +/// This unified interface automatically detects account types and routes to: +/// - cToken -> cToken transfer +/// - cToken -> SPL transfer +/// - SPL -> cToken transfer +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: source_account (SPL or cToken) +/// - accounts[2]: destination_account (SPL or cToken) +/// - accounts[3]: authority (signer) +/// - accounts[4]: payer (signer) +/// - accounts[5]: compressed_token_program_authority +/// For SPL interface (required for SPL<->cToken): +/// - accounts[6]: mint +/// - accounts[7]: token_pool_pda +/// - accounts[8]: spl_token_program +pub fn process_transfer_interface_invoke( + accounts: &[AccountInfo], + data: TransferInterfaceData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + let mut transfer = TransferInterface::new( + data.amount, + accounts[1].clone(), // source_account + accounts[2].clone(), // destination_account + accounts[3].clone(), // authority + accounts[4].clone(), // payer + accounts[5].clone(), // compressed_token_program_authority + ); + + // Add SPL interface config if provided + if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { + transfer = transfer.with_spl_interface( + Some(accounts[6].clone()), // mint + Some(accounts[8].clone()), // spl_token_program + Some(accounts[7].clone()), // token_pool_pda + data.token_pool_pda_bump, + )?; + } + + transfer.invoke()?; + + Ok(()) +} + +/// Handler for TransferInterface with PDA authority (invoke_signed) +/// +/// The authority is a PDA derived from TRANSFER_INTERFACE_AUTHORITY_SEED. +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: source_account (SPL or cToken) +/// - accounts[2]: destination_account (SPL or cToken) +/// - accounts[3]: authority (PDA, not signer - program signs) +/// - accounts[4]: payer (signer) +/// - accounts[5]: compressed_token_program_authority +/// For SPL interface (required for SPL<->cToken): +/// - accounts[6]: mint +/// - accounts[7]: token_pool_pda +/// - accounts[8]: spl_token_program +pub fn process_transfer_interface_invoke_signed( + accounts: &[AccountInfo], + data: TransferInterfaceData, +) -> Result<(), ProgramError> { + if accounts.len() < 6 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the authority + let (authority_pda, authority_bump) = + Pubkey::find_program_address(&[TRANSFER_INTERFACE_AUTHORITY_SEED], &ID); + + // Verify the authority account is the PDA + if &authority_pda != accounts[3].key { + return Err(ProgramError::InvalidSeeds); + } + + let mut transfer = TransferInterface::new( + data.amount, + accounts[1].clone(), // source_account + accounts[2].clone(), // destination_account + accounts[3].clone(), // authority (PDA) + accounts[4].clone(), // payer + accounts[5].clone(), // compressed_token_program_authority + ); + + // Add SPL interface config if provided + if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { + transfer = transfer.with_spl_interface( + Some(accounts[6].clone()), // mint + Some(accounts[8].clone()), // spl_token_program + Some(accounts[7].clone()), // token_pool_pda + data.token_pool_pda_bump, + )?; + } + + // Invoke with PDA signing + let authority_seeds: &[&[u8]] = &[TRANSFER_INTERFACE_AUTHORITY_SEED, &[authority_bump]]; + transfer.invoke_signed(&[authority_seeds])?; + + Ok(()) +} +``` + + + + +# Next Steps + + + + Transfer Interface + + + Close cToken Accounts + + \ No newline at end of file diff --git a/docs.json b/docs.json index 33dc905a..6e4370eb 100644 --- a/docs.json +++ b/docs.json @@ -64,8 +64,10 @@ "pages": [ "compressed-token-program/cmint/README", "compressed-token-program/cmint/create-cmint", + "compressed-token-program/cmint/client-guides/update-metadata", { "group": "Client Guides", + "hidden": true, "pages": [ "compressed-token-program/cmint/client-guides/cmint", "compressed-token-program/cmint/client-guides/update-metadata", @@ -74,6 +76,7 @@ }, { "group": "Program Guides", + "hidden": true, "pages": [ "compressed-token-program/cmint/program-guides/program-create-cmint", "compressed-token-program/cmint/program-guides/program-mint-to-cToken" @@ -85,8 +88,13 @@ "group": "cToken", "pages": [ "compressed-token-program/ctoken/README", + "compressed-token-program/ctoken/create-ctoken", + "compressed-token-program/ctoken/create-cata", + "compressed-token-program/ctoken/transfer-interface", + "compressed-token-program/ctoken/close-ctoken-account", { "group": "Client Guides", + "hidden": true, "pages": [ "compressed-token-program/ctoken/client-guides/client-create-ctoken", "compressed-token-program/ctoken/client-guides/client-create-cATA", @@ -96,6 +104,7 @@ }, { "group": "Program Guides", + "hidden": true, "pages": [ "compressed-token-program/ctoken/program-guides/program-create-ctoken", "compressed-token-program/ctoken/program-guides/program-create-cATA", diff --git a/snippets/ctoken-configure-rent.mdx b/snippets/ctoken-configure-rent.mdx index 75254c04..ff56a3f5 100644 --- a/snippets/ctoken-configure-rent.mdx +++ b/snippets/ctoken-configure-rent.mdx @@ -40,26 +40,3 @@ let compressible_params = CompressibleParamsInfos::new( - - - -We recommend to use default values, but you can customize the rent config based on the expected account activity. - - -1. Set the hours to determine the amount of prepaid rent -2. Set the lamports per write a transaction payer will pay for top ups - -```rust -CompressibleParamsInfos{ - compressible_config, - rent_sponsor, - system_program, - pre_pay_num_epochs: 35, - lamports_per_write: Some(788), - compress_to_account_pubkey: None, - token_account_version: TokenDataVersion::ShaFlat, - } -``` - - - \ No newline at end of file diff --git a/snippets/custom-rent-config.md b/snippets/custom-rent-config.md new file mode 100644 index 00000000..1c43f909 --- /dev/null +++ b/snippets/custom-rent-config.md @@ -0,0 +1,23 @@ + + + +We recommend to use default values, but you can customize the rent config based on the expected account activity. + + +1. Set the hours to determine the amount of prepaid rent +2. Set the lamports per write a transaction payer will pay for top ups + +```rust +CompressibleParamsInfos{ + compressible_config, + rent_sponsor, + system_program, + pre_pay_num_epochs: 35, + lamports_per_write: Some(788), + compress_to_account_pubkey: None, + token_account_version: TokenDataVersion::ShaFlat, + } +``` + + + \ No newline at end of file diff --git a/snippets/setup/rust-setup-environment-tabs.mdx b/snippets/setup/rust-setup-environment-tabs.mdx index 59ff7f8e..585dc9e8 100644 --- a/snippets/setup/rust-setup-environment-tabs.mdx +++ b/snippets/setup/rust-setup-environment-tabs.mdx @@ -3,7 +3,7 @@ import CliInstall from '/snippets/versions/cli-install-0.27.1-alpha.2.mdx'; -Test without a light-validator. +Test without external dependencies. ```bash # Initialize project From 79c0fcd8bcf74b8f764ff3b327a6599371833b62 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 18:48:09 +0000 Subject: [PATCH 053/143] Add cMint guides and update docs navigation structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add mint-ctokens.mdx: complete guide for minting cTokens to cToken accounts - Add update-metadata.mdx: guide for updating cMint token metadata - Update docs.json: add new cMint guide entries to navigation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- compressed-token-program/cmint/mint-ctokens.mdx | 0 compressed-token-program/cmint/update-metadata.mdx | 0 docs.json | 3 ++- 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 compressed-token-program/cmint/mint-ctokens.mdx create mode 100644 compressed-token-program/cmint/update-metadata.mdx diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx new file mode 100644 index 00000000..e69de29b diff --git a/compressed-token-program/cmint/update-metadata.mdx b/compressed-token-program/cmint/update-metadata.mdx new file mode 100644 index 00000000..e69de29b diff --git a/docs.json b/docs.json index 6e4370eb..d9010772 100644 --- a/docs.json +++ b/docs.json @@ -64,7 +64,8 @@ "pages": [ "compressed-token-program/cmint/README", "compressed-token-program/cmint/create-cmint", - "compressed-token-program/cmint/client-guides/update-metadata", + "compressed-token-program/cmint/update-metadata", + "compressed-token-program/cmint/mint-ctokens", { "group": "Client Guides", "hidden": true, From 3d2a0380b213ceddb7d261ded2a41884ed037e9a Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 18:58:20 +0000 Subject: [PATCH 054/143] Refactor compressed tokens README and update docs navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update compressed-tokens/README.mdx: simplify intro, fix cToken links - Update docs.json: reorder cMint guides, add mint-ctokens and update-metadata 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- compressed-tokens/README.mdx | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx index f1df15be..d21907b4 100644 --- a/compressed-tokens/README.mdx +++ b/compressed-tokens/README.mdx @@ -9,29 +9,9 @@ import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; import { TokenAccountCompressedVsSpl } from '/snippets/jsx/token-account-compressed-vs-spl.jsx'; - - - - - - - - - - - - - - - -
Account TypeKey Features
**[Compressed Token](#compressed-token-account)**Compressed account -
    -
  • **Compressed account** with `TokenData` field
  • -
  • **Rent-free** and SPL-compatible
  • -
-
- -Compressed token accounts store token balance, owner, and other information like SPL and cTokens. Any cToken or SPL token can be compressed/decompressed at will. +1. Compressed tokens are compressed accounts with `TokenData` and rent-free. +2. Compressed token accounts store token balance, owner, and other information of tokens like SPL and cTokens. +3. Any cToken or SPL token can be compressed/decompressed at will. ## Recommended Usage of Compressed Tokens @@ -39,7 +19,7 @@ Compressed token accounts store token balance, owner, and other information like #### Storage of Inactive Token Accounts * Most (associated) token accounts are not frequently written to. * **Store token accounts rent-free** when inactive - * [cTokens](/compressed-token-program/ctoken/ctoken) are automatically compressed/decompressed, when active/inactive and include **sponsored rent-exemption**. [Learn more here](/compressed-token-program/ctoken/ctoken). + * [cTokens](/compressed-token-program/ctoken/README) are automatically compressed/decompressed, when active/inactive and include **sponsored rent-exemption**. [Learn more here](/compressed-token-program/ctoken/README).
#### [Token Distribution](#advanced-guides) From a573e72ba250ba802242cadca490a79e9d9b5049 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 19:08:38 +0000 Subject: [PATCH 055/143] Refactor cToken guide navigation and fix cross-references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update cToken README: simplify Get Started section structure - Update all cToken guides: remove legacy Client/Program Guide cards - Fix Next Steps links: point to unified guides instead of legacy structure - Update compressible-rent-explained.mdx: improve tooltip clarity 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- compressed-token-program/ctoken/README.mdx | 27 ++++-------------- .../ctoken/close-ctoken-account.mdx | 27 +++++------------- .../ctoken/create-cata.mdx | 27 +++++------------- .../ctoken/create-ctoken.mdx | 28 +++++-------------- .../ctoken/transfer-interface.mdx | 27 +++++------------- snippets/compressible-rent-explained.mdx | 6 ++-- 6 files changed, 36 insertions(+), 106 deletions(-) diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx index 7903e10b..d3d913c6 100644 --- a/compressed-token-program/ctoken/README.mdx +++ b/compressed-token-program/ctoken/README.mdx @@ -115,20 +115,14 @@ The guides and examples use the `light_compressed_token_sdk`. -### Client Guides - -table for client guides - - - -### Program Guides +### Guides -### Example Repo +### Examples add github link @@ -136,21 +130,10 @@ add github link ## Next Steps -Follow our client or program guides for cTokens. - - - - \ No newline at end of file + /> \ No newline at end of file diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx index 4f44a445..c17543be 100644 --- a/compressed-token-program/ctoken/close-ctoken-account.mdx +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -168,23 +168,10 @@ pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<( # Next Steps - - - Close cToken Accounts - - - Learn about compressed tokens - - + diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index 4e7a2ec8..383629e9 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -224,23 +224,10 @@ pub fn process_create_ata2_invoke_signed(
# Next Steps - - - Create Associated cToken Accounts - - - Mint cTokens - - + diff --git a/compressed-token-program/ctoken/create-ctoken.mdx b/compressed-token-program/ctoken/create-ctoken.mdx index e0bdd4fe..09facbbb 100644 --- a/compressed-token-program/ctoken/create-ctoken.mdx +++ b/compressed-token-program/ctoken/create-ctoken.mdx @@ -369,24 +369,10 @@ pub fn process_create_token_account_invoke_signed( # Next Steps - - - - Create cToken Accounts - - - Mint cTokens - - \ No newline at end of file + diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index e20e2ff5..5301066a 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -279,23 +279,10 @@ pub fn process_transfer_interface_invoke_signed( # Next Steps - - - Transfer Interface - - - Close cToken Accounts - - \ No newline at end of file + diff --git a/snippets/compressible-rent-explained.mdx b/snippets/compressible-rent-explained.mdx index a470caa3..89a59670 100644 --- a/snippets/compressible-rent-explained.mdx +++ b/snippets/compressible-rent-explained.mdx @@ -1,8 +1,8 @@ 1. The rent-exemption for the account creation is sponsored by Light Protocol. 2. Compressible rent is paid per rent-epoch: 388 lamports for 1.5h,
paid by account creator and transaction payers to keep accounts "active". -3. "Inactive" accounts, where rent is below one epoch, are compressed and the rent-exemption can be claimed by the rent sponsor. -4. Transfers to inactive accounts "load" it with the same state (decompress). +3. "Inactive" accounts, where rent is below one epoch, are compressed
and the rent-exemption can be claimed by the rent sponsor. +4. Transfers to inactive accounts "load" it with the same state (decompress). Compressible rent is paid in three scenarios: @@ -30,7 +30,7 @@ Compressible rent is paid in three scenarios: Transaction payer - Load Account + Load Account Total: 22,208 lamports
11,208 lamports (24h rent)
From fb2df6be561ba39b6bda82f41f71dd0dd602f241 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Wed, 3 Dec 2025 19:23:04 +0000 Subject: [PATCH 056/143] Add mint-ctokens guide and client prerequisites scaffolding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add mint-ctokens.mdx: complete program guide for minting cTokens - Update all guides: add client prerequisites sections - Fix Next Steps links: update to unified guide structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../cmint/create-cmint.mdx | 17 +- .../cmint/mint-ctokens.mdx | 345 ++++++++++++++++++ .../ctoken/close-ctoken-account.mdx | 13 + .../ctoken/create-cata.mdx | 20 +- .../ctoken/transfer-interface.mdx | 13 + 5 files changed, 403 insertions(+), 5 deletions(-) diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index 8f36a72f..da4dddcf 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -4,6 +4,8 @@ description: Program and client guide to create a cMint with token metadata via --- import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; +import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; 1. cMints uniquely represent a token on Solana and store its global metadata. 2. cMints are compressed accounts and rent-free. @@ -25,17 +27,24 @@ Learn the [core concepts to the cToken Program here](/compressed-token-program/c + + +### Prerequisites -```rust -// TODO: Rust client example coming soon -``` + + + + +### Close cToken Account + + -Find [full code examples at the end](#full-code-example). +Find [a full code example at the end](#full-code-example). diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index e69de29b..4e456ba0 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -0,0 +1,345 @@ +--- +title: Mint cTokens +description: Program guide to mint cTokens to a cMint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. +--- + +import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; +import CMintToCTokenAccountsList from '/snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; +import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; + + +1. cTokens increase the supply of a cMint. +2. The destination cToken accounts must exist to receive the minted cTokens. +3. Only the mint authority can mint new cTokens. + + +Find [full code examples at the end](#full-code-example). + + + +# Get Started + + + + +```typescript +// TODO: TypeScript SDK coming soon +``` + + + + + + +### Prerequisites + + + + + + +### Mint cTokens + + + + + + + +Find [a full code example at the end](#full-code-example). + + + + + +### Configure Mint Parameters +Include your mint, the amount of tokens to be minted and the pubkey of the mint authority. +The client passes a validity proof that proves the cMint exists. + +```rust +use light_compressed_token_sdk::ctoken::MintToCTokenParams; + +let mint_params = MintToCTokenParams::new( + data.compressed_mint_inputs, + data.amount, + data.mint_authority, + data.proof, +); +``` + + + + + + +### System Accounts + +Compressed accounts like cMints require system accounts like the Light System Program account for interactions and proof verification. +The client includes them in the instruction. + + + + + +```rust +use light_compressed_token_sdk::ctoken::SystemAccountInfos; + +let system_accounts = SystemAccountInfos { + light_system_program: light_system_program.clone(), + cpi_authority_pda: cpi_authority_pda.clone(), + registered_program_pda: registered_program_pda.clone(), + account_compression_authority: account_compression_authority.clone(), + account_compression_program: account_compression_program.clone(), + system_program: system_program.clone(), +}; +``` + + + + + +### Build Account Infos and CPI the cToken Program + +1. Pass the required accounts, including the destination cToken accounts. +2. Include `mint_params` and `system_accounts` from the previous steps +3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. + + + +```rust +use light_compressed_token_sdk::ctoken::MintToCTokenInfos; + +MintToCTokenInfos { + authority: authority.clone(), + payer: payer.clone(), + state_tree: state_tree.clone(), + input_queue: input_queue.clone(), + output_queue: output_queue.clone(), + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + mint_params, +} +.invoke()?; +``` + + + + +```rust +use light_compressed_token_sdk::ctoken::MintToCTokenInfos; + +let account_infos = MintToCTokenInfos { + authority: authority.clone(), + payer: payer.clone(), + state_tree: state_tree.clone(), + input_queue: input_queue.clone(), + output_queue: output_queue.clone(), + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + mint_params, +}; + +let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; +account_infos.invoke_signed(&[signer_seeds])?; +``` + + + + + + + + + + + +# Full Code Example + + +Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/mint_to_ctoken.rs). + + +```rust expandable +use borsh::{BorshDeserialize, BorshSerialize}; +use light_compressed_token_sdk::ctoken::{ + MintToCTokenInfos, MintToCTokenParams, SystemAccountInfos, +}; +use light_ctoken_types::instructions::mint_action::CompressedMintWithContext; +use light_sdk::instruction::ValidityProof; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::ID; + +/// PDA seed for mint authority in invoke_signed variant +pub const MINT_AUTHORITY_SEED: &[u8] = b"mint_authority"; + +/// Instruction data for mint_to_ctoken operations +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct MintToCTokenData { + pub compressed_mint_inputs: CompressedMintWithContext, + pub amount: u64, + pub mint_authority: Pubkey, + pub proof: ValidityProof, +} + +/// Handler for minting tokens to compressed token accounts (invoke) +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: authority (mint_authority) +/// - accounts[3]: fee_payer +/// - accounts[4]: cpi_authority_pda +/// - accounts[5]: registered_program_pda +/// - accounts[6]: account_compression_authority +/// - accounts[7]: account_compression_program +/// - accounts[8]: system_program +/// - accounts[9]: output_queue +/// - accounts[10]: state_tree +/// - accounts[11]: input_queue +/// - accounts[12..]: ctoken_accounts (destination accounts) +pub fn process_mint_to_ctoken( + accounts: &[AccountInfo], + data: MintToCTokenData, +) -> Result<(), ProgramError> { + if accounts.len() < 13 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Build params using the constructor + let params = MintToCTokenParams::new( + data.compressed_mint_inputs, + data.amount, + data.mint_authority, + data.proof, + ); + + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[4].clone(), + registered_program_pda: accounts[5].clone(), + account_compression_authority: accounts[6].clone(), + account_compression_program: accounts[7].clone(), + system_program: accounts[8].clone(), + }; + + // Collect ctoken accounts from remaining accounts + let ctoken_accounts: Vec = accounts[12..].to_vec(); + + MintToCTokenInfos { + authority: accounts[2].clone(), + payer: accounts[3].clone(), + state_tree: accounts[10].clone(), + input_queue: accounts[11].clone(), + output_queue: accounts[9].clone(), + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + } + .invoke()?; + + Ok(()) +} + +/// Handler for minting tokens with PDA mint authority (invoke_signed) +/// +/// Account order: +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: authority (PDA mint_authority) +/// - accounts[3]: fee_payer +/// - accounts[4]: cpi_authority_pda +/// - accounts[5]: registered_program_pda +/// - accounts[6]: account_compression_authority +/// - accounts[7]: account_compression_program +/// - accounts[8]: system_program +/// - accounts[9]: output_queue +/// - accounts[10]: state_tree +/// - accounts[11]: input_queue +/// - accounts[12..]: ctoken_accounts (destination accounts) +pub fn process_mint_to_ctoken_invoke_signed( + accounts: &[AccountInfo], + data: MintToCTokenData, +) -> Result<(), ProgramError> { + if accounts.len() < 13 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Derive the PDA for the mint authority + let (pda, bump) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); + + // Verify the authority account is the PDA + if &pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); + } + + let params = MintToCTokenParams::new( + data.compressed_mint_inputs, + data.amount, + data.mint_authority, + data.proof, + ); + + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[4].clone(), + registered_program_pda: accounts[5].clone(), + account_compression_authority: accounts[6].clone(), + account_compression_program: accounts[7].clone(), + system_program: accounts[8].clone(), + }; + + let ctoken_accounts: Vec = accounts[12..].to_vec(); + + let account_infos = MintToCTokenInfos { + authority: accounts[2].clone(), + payer: accounts[3].clone(), + state_tree: accounts[10].clone(), + input_queue: accounts[11].clone(), + output_queue: accounts[9].clone(), + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + }; + + let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; + account_infos.invoke_signed(&[signer_seeds])?; + + Ok(()) +} +``` + + + +# Next Steps + + + + Mint cTokens + + + Transfer Interface cToken + + \ No newline at end of file diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx index c17543be..18e9716d 100644 --- a/compressed-token-program/ctoken/close-ctoken-account.mdx +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -4,6 +4,8 @@ description: Program guide to close cToken accounts via CPI to the cToken Progra --- import CloseAccountInfosAccountsList from '/snippets/accounts-list/close-account-infos-accounts-list.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; +import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; 1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. 2. cToken accounts can be closed @@ -19,7 +21,18 @@ import CloseAccountInfosAccountsList from '/snippets/accounts-list/close-account + + +### Prerequisites + + + + + +### Close cToken Account + + diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index 383629e9..6eea74d4 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -9,6 +9,8 @@ import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calc import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; import CAtaIntro from '/snippets/ctoken-guides/cata-intro.mdx'; import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; +import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; 1. Associated cToken accounts (cATA) are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. 2. The address for cATAs is deterministically derived with the owner’s address, cToken program ID, and mint address. @@ -33,8 +35,24 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx - + + + +### Prerequisites + + + + + + + +### Create cATA + + + + + diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index 5301066a..4aaad203 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -6,6 +6,8 @@ description: Guide for transfers between cToken and SPL token accounts via CPI. import TransferInterfaceAccountsListCtoken1 from '/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx'; import TransferInterfaceAccountsListSpl2 from '/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx'; import TransferInterfaceIntro from '/snippets/ctoken-guides/transfer-interface-intro.mdx'; +import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; +import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; @@ -56,7 +58,18 @@ import TransferInterfaceIntro from '/snippets/ctoken-guides/transfer-interface-i + + +### Prerequisites + + + + + +### Close cToken Account + + From 907924beebe4cd0fba5817ad95a0e8c0ce6134f2 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 4 Dec 2025 02:51:05 +0000 Subject: [PATCH 057/143] Refactor cToken program documentation structure and navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add learn/c-token-program.mdx: core concepts guide - Update overview.mdx: add cost comparison table, link to concepts - Update docs.json: move c-token-program to Learn section, add extensions - Update cToken/README.mdx: emphasize creation cost in table header - Update extensions.mdx: simplify title - Update rust-setup-environment-tabs.mdx: clarify Lite-SVM usage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- compressed-token-program/ctoken/README.mdx | 2 +- .../ctoken/extensions.mdx | 3 +- compressed-token-program/overview.mdx | 18 +- compressed-token-program/stash.mdx | 4 +- docs.json | 8 +- learn/c-token-program.mdx | 362 ++++++++++++++++++ .../setup/rust-setup-environment-tabs.mdx | 4 +- 7 files changed, 385 insertions(+), 16 deletions(-) create mode 100644 learn/c-token-program.mdx diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx index d3d913c6..58ec9d0c 100644 --- a/compressed-token-program/ctoken/README.mdx +++ b/compressed-token-program/ctoken/README.mdx @@ -13,7 +13,7 @@ import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-g | | cToken account | SPL token account | |-----------|--------|-----| -| creation cost | 22,288 lamports | 2,039,280 lamports | +| **Creation Cost** | 22,288 lamports | 2,039,280 lamports | ## Compressible Rent diff --git a/compressed-token-program/ctoken/extensions.mdx b/compressed-token-program/ctoken/extensions.mdx index f5cb6bbe..7e4c7ff6 100644 --- a/compressed-token-program/ctoken/extensions.mdx +++ b/compressed-token-program/ctoken/extensions.mdx @@ -1,6 +1,5 @@ --- -title: Token 2022 Extensions for cTokens -sidebarTitle: Token 2022 Extensions +title: Extensions for cTokens description: Overview to Token 2022 extensions supported by cTokens. --- diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index 92f60c3c..6c8a1845 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -1,6 +1,7 @@ --- title: Overview -description: Compressed tokens provide full SPL token functionality without per-account rent cost. +description: The Compressed Token Program extends existing Solana token standards with cMints, cTokens and compressed tokens. + --- import GuidesTable from '/snippets/overview-tables/compressed-tokens-guides-table.mdx'; @@ -8,9 +9,18 @@ import AdvancedGuidesTable from '/snippets/overview-tables/compressed-tokens-adv import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; -| Creation | SPL Token | Compressed Token | Cost Reduction | -|:---------------------|:------------------|:----------------------|:---------------| -| 100 Token Accounts | ~ 0.2 SOL | **~ 0.00004 SOL** | **5000x** | +* TODO add recommended usage + +| | SPL | cToken | +|:---------------------|:------------------|:----------------------| +| **Mint** | 1,461,600 lamports | ~15,000 lamports | +| **Token** | 2,039,280 lamports | ~22,288 lamports | +| **Compressed Token** | 2,039,280 lamports | ~5,000 lamports | + + + +Learn the core concepts to the [cToken program here](/learn/c-token-program). + # Start building diff --git a/compressed-token-program/stash.mdx b/compressed-token-program/stash.mdx index 283fc4ea..f9520e9b 100644 --- a/compressed-token-program/stash.mdx +++ b/compressed-token-program/stash.mdx @@ -1,6 +1,4 @@ **SPL mints and tokens** owned by the [Token Program](https://github.com/solana-program/token) or [Token-2022](https://github.com/solana-program/token-2022) **require rent** by default.
You can **transfer SPL token accounts to cToken accounts** for **sponsored rent-exemption** while keeping the **same interoparability**. -
- -Ask DeepWiki \ No newline at end of file + \ No newline at end of file diff --git a/docs.json b/docs.json index d9010772..287841af 100644 --- a/docs.json +++ b/docs.json @@ -51,14 +51,14 @@ "learn/core-concepts/considerations" ] }, - "rent" + "rent", + "learn/c-token-program" ] }, { "group": "cToken Program", "pages": [ "compressed-token-program/overview", - "compressed-token-program/c-token-program", { "group": "cMint", "pages": [ @@ -93,6 +93,7 @@ "compressed-token-program/ctoken/create-cata", "compressed-token-program/ctoken/transfer-interface", "compressed-token-program/ctoken/close-ctoken-account", + "compressed-token-program/ctoken/extensions", { "group": "Client Guides", "hidden": true, @@ -158,8 +159,7 @@ ] } ] - }, - "compressed-token-program/ctoken/extensions" + } ] }, { diff --git a/learn/c-token-program.mdx b/learn/c-token-program.mdx new file mode 100644 index 00000000..53ad3b83 --- /dev/null +++ b/learn/c-token-program.mdx @@ -0,0 +1,362 @@ +--- +title: cToken Program +description: Core Concepts of cMints, cTokens, and compressed token accounts. +--- + +import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; +import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; + + +The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, cTokens and compressed tokens. + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
Account TypeKey Features
**[cMint](#cmint-accounts)**Compressed account +
    +
  • **Rent-free mint accounts** (similar to SPL mints)
  • +
  • Custom [Token Metadata](#token-metadata) **extension**
  • +
+
**[cToken](#ctoken-account)**Solana account +
    +
  • **Store tokens** of **cMints, SPL mints, or Token 2022 mints**
  • +
  • **Sponsored rent** exemption via [compressible extension](#compressible)
  • +
  • Use for **active token accounts** with frequent writes (trading, etc.)
  • +
+
**[Compressed Token](#compressed-token-account)**Compressed account +
    +
  • **Compressed account** with `TokenData` field
  • +
  • **Rent-free** and SPL-compatible
  • +
  • Use for **storage of inactive tokens** and **token distribution**
  • +
  • cToken accounts are automatically compressed/decompressed when active/inactive.
  • +
+
+ +# cMint Accounts + + +* **cMints are compressed accounts** and **cannot be decompressed**. +* SPL mints can not be compressed to cMints. + + +cMints **uniquely represent a token on Solana and store its global metadata**, similar to SPL mint accounts with few core differences: +1. cMint accounts are **rent-free**. +2. Tokens created from cMints are **cTokens**. +3. Token metadata (name, symbol, URI) is stored as an extension in the struct. + + + + + Diagram showing cMint compressed account structure with three components: Hash (identifier for cMint in purple box), Account (struct containing BaseMint with SPL-compatible fields, cMint Data for program state, and optional Extensions for Token Metadata), and BasemintData (containing Supply, Decimals, Mint Authority, and Freeze Authority fields) with Token Metadata extension + + + + ```rust + pub struct CompressedMint { + // SPL mint layout + pub base: BaseMint, + // cMint state used by the Compressed Token Program + pub metadata: CompressedMintMetadata + // Field for Token Metadata extension + pub extensions: Option>, + } + ``` + + + + +Find the [source code of cMints here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/mint/compressed_mint.rs). + + +The `metadata` field is used by the Compressed Token Program to store the internal state of a cMint. + +The `BaseMint` field replicates the field layout and serialization format of [SPL Mint accounts](https://solana.com/docs/tokens#mint-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens and mints. + +Here is how cMints and SPL mints compare: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldcMintSPL Mint
mint_authority
supply
decimals
is_initialized
freeze_authority
cMint Data-
Extensionsvia Token-2022
+
+ + ```rust + pub struct BaseMint { + /// Optional authority used to mint new tokens. The mint authority may only + /// be provided during mint creation. If no mint authority is present + /// then the mint has a fixed supply and no further tokens may be + /// minted. + pub mint_authority: Option, + /// Total supply of tokens. + pub supply: u64, + /// Number of base 10 digits to the right of the decimal place. + pub decimals: u8, + /// Is initialized - for SPL compatibility + pub is_initialized: bool, + /// Optional authority to freeze token accounts. + pub freeze_authority: Option, + } + ``` + +
+ +# cToken Account + + +**cToken accounts are Solana accounts**, not compressed accounts. + + +A cToken account holds token balances like SPL Token accounts: +* A wallet needs a cToken account for each cMint it wants to hold, with the wallet address set as the cToken account owner. +* Each wallet can own multiple cToken accounts for the same cMint. +* A cToken account can only have one owner and hold units of one cMint. + + + + + Diagram showing cToken Solana account structure with three components: Address (identifier for cToken account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Owner set to Compressed Token Program), and AccountData (containing Mint, Owner, Amount, and Extensions fields) + + + + ```rust + /// Ctoken account structure (with extensions). + pub struct CToken { + pub mint: Pubkey, + pub owner: Pubkey, + pub amount: u64, + pub delegate: Option, // instruction not implemented yet + pub state: u8, + pub is_native: Option, + pub delegated_amount: u64, // instruction not implemented yet + pub close_authority: Option, + pub extensions: Option>, // Optional extensions e.g. compressible + } + ``` + + + + +Find the [source code of cToken here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken_struct.rs). + + +cToken accounts replicate the field layout and serialization format of [SPL Token accounts](https://solana.com/docs/tokens#token-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens. + +Here is how cTokens and SPL tokens compare: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldcTokenSPL Token Account
mint
owner
amount
delegateunimplemented
state
is_native
delegated_amountunimplemented
close_authority
extensionsvia Token-2022
+ +### Compressible Extension + +cToken accounts are compressible: + + + +This extension makes cToken accounts 200x cheaper than SPL token accounts + + | | cToken | SPL | + |-----------|--------|-----| + | allocate | 0.000011 | 0.002 | + | rent for 24h | 0.000011 | - | + + + + + + + + + + + + +# Associated cToken Account + +**Associated cToken** accounts (cATAs) follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): +* Each wallet needs its own cToken account to hold tokens from the same cMint. +* The address for cATAs is deterministically derived with the owner’s address, cToken program ID, and mint address. + +```rust +let seeds = [ + owner.as_ref(), // Wallet address (32 bytes) + program_id.as_ref(), // Compressed Token Program ID (32 bytes) + mint.as_ref(), // cMint address (32 bytes) + bump.as_ref(), // Bump seed (1 byte) +]; +``` + + +Find the [source code to associated cToken accounts here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/create_associated_token_account.rs). + + +# Compressed Token Account + +Compressed token accounts store token balance, owner, and other information like SPL and cTokens. Any cToken or SPL token can be compressed/decompressed at will. + +We recommend to use compressed tokens for **token distribution** or **storage of inactive tokens**. + + +**cToken accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. + + + + + + Diagram showing compressed token account structure with three components: Hash (identifier for compressed token account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Address set to None), and AccountData (containing Mint, Owner, Amount, and Extensions fields marked as unimplemented) + + + + ```rust + pub struct TokenData { + pub mint: Pubkey, + pub owner: Pubkey, + pub amount: u64, + pub delegate: Option, + pub state: u8, + /// Placeholder for TokenExtension tlv data (unimplemented) + pub tlv: Option>, + } + ``` + + + +# Next Steps + + + \ No newline at end of file diff --git a/snippets/setup/rust-setup-environment-tabs.mdx b/snippets/setup/rust-setup-environment-tabs.mdx index 585dc9e8..1b356028 100644 --- a/snippets/setup/rust-setup-environment-tabs.mdx +++ b/snippets/setup/rust-setup-environment-tabs.mdx @@ -3,7 +3,7 @@ import CliInstall from '/snippets/versions/cli-install-0.27.1-alpha.2.mdx'; -Test without external dependencies. +Test with Lite-SVM (...) ```bash # Initialize project @@ -20,7 +20,7 @@ use solana_sdk::signer::Signer; #[tokio::test] async fn test_example() { - // In-memory test environment - no external validator + // In-memory test environment let mut rpc = LightProgramTest::new(ProgramTestConfig::default()) .await .unwrap(); From d95f41c84f3fdcdb3bb94caaae234367086f4c79 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 4 Dec 2025 22:24:47 +0000 Subject: [PATCH 058/143] Standardize c-Token terminology and add integration guides - Replace inconsistent cToken/cTokens with c-Token/c-Tokens throughout docs - Update descriptions: remove "compressed tokens" redundancy - Add integration guides for wallets, DEXs, aggregators, DeFi, payments, trading - Simplify docs.json navigation structure (remove nested groups) - Update overview.mdx: add cost comparison, compressible rent section - Fix Accordion syntax in overview.mdx - Update rent calculator labels for consistency --- compressed-token-program/c-token-program.mdx | 54 +-- compressed-token-program/cmint/README.mdx | 129 +----- .../client-guides/client-create-cmint.mdx | 2 +- .../client-guides/client-mint-to-ctoken.mdx | 10 +- .../cmint/create-cmint.mdx | 14 +- .../cmint/mint-ctokens.mdx | 20 +- .../program-guides/program-create-cmint.mdx | 12 +- .../program-guides/program-mint-to-cToken.mdx | 18 +- compressed-token-program/ctoken/README.mdx | 12 +- .../client-guides/client-close-ctoken.mdx | 8 +- .../client-guides/client-create-cATA.mdx | 12 +- .../client-guides/client-create-ctoken.mdx | 16 +- .../client-transfer-interface.mdx | 4 +- .../ctoken/close-ctoken-account.mdx | 18 +- .../ctoken/create-cata.mdx | 18 +- .../ctoken/create-ctoken.mdx | 20 +- .../ctoken/extensions.mdx | 6 +- .../program-guides/program-close-ctoken.mdx | 10 +- .../program-guides/program-create-cATA.mdx | 16 +- .../program-guides/program-create-ctoken.mdx | 10 +- .../program-transfer-interface.mdx | 46 +- .../ctoken/transfer-interface.mdx | 66 +-- .../integrate/for-aggregators.mdx | 3 + .../integrate/for-defi-markets.mdx | 3 + .../integrate/for-dexs.mdx | 3 + .../integrate/for-payment-companies.mdx | 3 + .../integrate/for-trading-apps.mdx | 3 + .../integrate/for-wallet-applications.mdx | 3 + compressed-token-program/overview.mdx | 47 ++- compressed-token-program/stash.mdx | 2 +- compressed-tokens/README.mdx | 6 +- docs.json | 393 ++++-------------- learn/c-token-program.mdx | 54 +-- rent.mdx | 2 +- resources/addresses-and-urls.mdx | 2 +- .../close-account-infos-accounts-list.mdx | 8 +- .../cmint-to-ctoken-accounts-list.mdx | 4 +- .../ctoken-create-accounts-list-client.mdx | 8 +- .../ctoken-create-accounts-list.mdx | 4 +- .../ctoken-create-ata-accounts-list.mdx | 12 +- ...nsfer-interface-accounts-list-ctoken-1.mdx | 6 +- ...transfer-interface-accounts-list-spl-2.mdx | 2 +- snippets/compressible-rent-explained.mdx | 16 +- snippets/compressible-vs-solana-rent.mdx | 2 +- snippets/ctoken-guides/cata-intro.mdx | 6 +- snippets/ctoken-guides/close-intro.mdx | 6 +- snippets/ctoken-guides/ctoken-intro.mdx | 4 +- .../transfer-interface-intro.mdx | 18 +- snippets/jsx/compressible-rent-calculator.jsx | 2 +- snippets/jsx/ctoken-vs-spl-calculator.jsx | 2 +- snippets/jsx/token22-extensions-table.jsx | 2 +- .../ctoken-program-guides-table.mdx | 8 +- 52 files changed, 419 insertions(+), 736 deletions(-) create mode 100644 compressed-token-program/integrate/for-aggregators.mdx create mode 100644 compressed-token-program/integrate/for-defi-markets.mdx create mode 100644 compressed-token-program/integrate/for-dexs.mdx create mode 100644 compressed-token-program/integrate/for-payment-companies.mdx create mode 100644 compressed-token-program/integrate/for-trading-apps.mdx create mode 100644 compressed-token-program/integrate/for-wallet-applications.mdx diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx index 8f7dd3cb..0eea197b 100644 --- a/compressed-token-program/c-token-program.mdx +++ b/compressed-token-program/c-token-program.mdx @@ -1,7 +1,7 @@ --- title: Compressed Token Program sidebarTitle: Overview New -description: Overview of cMints, cTokens, and compressed token accounts. +description: Overview of cMints, c-Tokens, and compressed token accounts. --- import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; @@ -10,7 +10,7 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, cTokens and compressed tokens. +The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, c-Tokens and compressed tokens. @@ -32,7 +32,7 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t - + @@ -62,7 +62,7 @@ _add graphic of all tokens here_ **SPL mints and tokens** owned by the [Token Program](https://github.com/solana-program/token) or [Token-2022](https://github.com/solana-program/token-2022) **require rent** by default.
-You can **migrate SPL token accounts to cTokens** with compressible extension for **sponsored rent-exemption** while keeping the **same interoparability**. +You can **migrate SPL token accounts to c-Tokens** with compressible extension for **sponsored rent-exemption** while keeping the **same interoparability**.
# cMint Accounts @@ -74,7 +74,7 @@ You can **migrate SPL token accounts to cTokens** with compressible extension fo cMints **uniquely represent a token on Solana and store its global metadata**, similar to SPL mint accounts with few core differences: 1. cMint accounts are **rent-free**. -2. Tokens created from cMints are **cTokens** (fully compatible with SPL tokens). +2. Tokens created from cMints are **c-Tokens** (fully compatible with SPL tokens). 3. Token metadata (name, symbol, URI) is stored as an extension within the compressed mint account. @@ -180,23 +180,23 @@ Here is how cMints and SPL mints compare: -# cToken Account +# c-Token Account -**cToken accounts are Solana accounts**, not compressed accounts. +**c-Token accounts are Solana accounts**, not compressed accounts. -A cToken account holds token balances like SPL Token accounts: -* A wallet needs a cToken account for each cMint it wants to hold, with the wallet address set as the cToken account owner. -* Each wallet can own multiple cToken accounts for the same cMint. -* A cToken account can only have one owner and hold units of one cMint. +A c-Token account holds token balances like SPL Token accounts: +* A wallet needs a c-Token account for each cMint it wants to hold, with the wallet address set as the c-Token account owner. +* Each wallet can own multiple c-Token accounts for the same cMint. +* A c-Token account can only have one owner and hold units of one cMint. Diagram showing cToken Solana account structure with three components: Address (identifier for cToken account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Owner set to Compressed Token Program), and AccountData (containing Mint, Owner, Amount, and Extensions fields) @@ -219,17 +219,17 @@ A cToken account holds token balances like SPL Token accounts: -Find the [source code of cToken here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken_struct.rs). +Find the [source code of c-Token here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken_struct.rs). -cToken accounts replicate the field layout and serialization format of [SPL Token accounts](https://solana.com/docs/tokens#token-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens. +c-Token accounts replicate the field layout and serialization format of [SPL Token accounts](https://solana.com/docs/tokens#token-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens. -Here is how cTokens and SPL tokens compare: +Here is how c-Tokens and SPL tokens compare:
**[cToken](#ctoken-account)****[c-Token](#ctoken-account)** Solana account
    @@ -50,7 +50,7 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t
  • **Compressed account** with `TokenData` field
  • **Rent-free** and SPL-compatible
  • Use for **storage of inactive tokens** and **token distribution**
  • -
  • cToken accounts are automatically compressed/decompressed when active/inactive.
  • +
  • c-Token accounts are automatically compressed/decompressed when active/inactive.
- + @@ -284,13 +284,13 @@ Here is how cTokens and SPL tokens compare: ### Compressible Extension -cToken accounts are compressible: +c-Token accounts are compressible: -This extension makes cToken accounts 200x cheaper than SPL token accounts +This extension makes c-Token accounts 200x cheaper than SPL token accounts - | | cToken | SPL | + | | c-Token | SPL | |-----------|--------|-----| | allocate | 0.000011 | 0.002 | | rent for 24h | 0.000011 | - | @@ -306,11 +306,11 @@ This extension makes cToken accounts 200x cheaper than SPL token accounts -# Associated cToken Account +# Associated c-Token Account -**Associated cToken** accounts (cATAs) follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): -* Each wallet needs its own cToken account to hold tokens from the same cMint. -* The address for cATAs is deterministically derived with the owner’s address, cToken program ID, and mint address. +**Associated c-Token** accounts (c-ATAs) follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): +* Each wallet needs its own c-Token account to hold tokens from the same cMint. +* The address for c-ATAs is deterministically derived with the owner’s address, c-Token program ID, and mint address. ```rust let seeds = [ @@ -322,17 +322,17 @@ let seeds = [ ``` -Find the [source code to associated cToken accounts here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/create_associated_token_account.rs). +Find the [source code to associated c-Token accounts here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/create_associated_token_account.rs). # Compressed Token Account -Compressed token accounts store token balance, owner, and other information like SPL and cTokens. Any cToken or SPL token can be compressed/decompressed at will. +Compressed token accounts store token balance, owner, and other information like SPL and c-Tokens. Any c-Token or SPL token can be compressed/decompressed at will. We recommend to use compressed tokens for **token distribution** or **storage of inactive tokens**. -**cToken accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. +**c-Token accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. diff --git a/compressed-token-program/cmint/README.mdx b/compressed-token-program/cmint/README.mdx index c753d6ab..4632c92c 100644 --- a/compressed-token-program/cmint/README.mdx +++ b/compressed-token-program/cmint/README.mdx @@ -7,140 +7,15 @@ import { SolanaRentCalculator } from '/snippets/jsx/solana-rent-calculator.jsx'; 1. cMints uniquely represent a token on Solana and store its global metadata. 2. cMints are compressed accounts and rent-free. -3. Tokens created from cMints are cTokens. The mint's address serves as the token's unique identifier. +3. Tokens created from cMints are c-Tokens. The mint's address serves as the token's unique identifier. | | cMint account | SPL mint account | |-----------|--------|-----| | creation cost | 25,000 lamports | 1,461,600 lamports | -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). +Learn the [core concepts to the c-Token Program here](/compressed-token-program/c-token-program). -## Account Struct - - - - Diagram showing cMint compressed account structure with three components: Hash (identifier for cMint in purple box), Account (struct containing BaseMint with SPL-compatible fields, cMint Data for program state, and optional Extensions for Token Metadata), and BasemintData (containing Supply, Decimals, Mint Authority, and Freeze Authority fields) with Token Metadata extension - - - - ```rust - pub struct CompressedMint { - // SPL mint layout - pub base: BaseMint, - // cMint state used by the Compressed Token Program - pub metadata: CompressedMintMetadata - // Field for Token Metadata extension - pub extensions: Option>, - } - ``` - - - - -Find the [source code of cMints here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/mint/compressed_mint.rs). - - -The `metadata` field is used by the Compressed Token Program to store the internal state of a cMint. - -The `BaseMint` field replicates the field layout and serialization format of [SPL Mint accounts](https://solana.com/docs/tokens#mint-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens and mints. - -Here is how cMints and SPL mints compare: - - - -
FieldcTokenc-Token SPL Token Account
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldcMintSPL Mint
mint_authority
supply
decimals
is_initialized
freeze_authority
cMint Data-
Extensionsvia Token-2022
-
- - ```rust - pub struct BaseMint { - /// Optional authority used to mint new tokens. The mint authority may only - /// be provided during mint creation. If no mint authority is present - /// then the mint has a fixed supply and no further tokens may be - /// minted. - pub mint_authority: Option, - /// Total supply of tokens. - pub supply: u64, - /// Number of base 10 digits to the right of the decimal place. - pub decimals: u8, - /// Is initialized - for SPL compatibility - pub is_initialized: bool, - /// Optional authority to freeze token accounts. - pub freeze_authority: Option, - } - ``` - -
- -### Extensions - -* cMints only support a custom Token Metadata extension. -* Use more Token 2022 extensions via a [Token 2022 mint account with cTokens](/compressed-token-program/ctoken/extensions). - -```rust -let token_metadata = ExtensionInstructionData::TokenMetadata( - TokenMetadataInstructionData { - update_authority: Some(authority.to_bytes().into()), - name: b"My Token".to_vec(), - symbol: b"MTK".to_vec(), - uri: b"https://example.com/metadata.json".to_vec(), - additional_metadata: Some(vec![ - AdditionalMetadata { - key: b"category".to_vec(), - value: b"utility".to_vec(), - }, - ]), - } -); -``` # Get Started diff --git a/compressed-token-program/cmint/client-guides/client-create-cmint.mdx b/compressed-token-program/cmint/client-guides/client-create-cmint.mdx index cc928775..b3b812b7 100644 --- a/compressed-token-program/cmint/client-guides/client-create-cmint.mdx +++ b/compressed-token-program/cmint/client-guides/client-create-cmint.mdx @@ -1,6 +1,6 @@ --- title: Create cMints -description: Create a cMint using the cToken SDK. +description: Create a cMint using the c-Token SDK. --- diff --git a/compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx b/compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx index 4f0bc3bc..6d500de9 100644 --- a/compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx +++ b/compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx @@ -1,6 +1,6 @@ --- -title: Mint cTokens -description: Client guide to mint compressed tokens to cToken accounts with step-by-step implementation and full code examples. +title: Mint c-Tokens +description: Client guide to mint compressed tokens to c-Token accounts with step-by-step implementation and full code examples. --- @@ -86,7 +86,7 @@ async fn mint_to_ctoken( .await .unwrap(); - println!("Tokens minted to cToken ATAs!"); + println!("Tokens minted to c-Token ATAs!"); for (ata, amount) in ata_pubkeys.iter().zip(amounts.iter()) { println!(" {} -> {} tokens", ata, amount); } @@ -110,10 +110,10 @@ async fn mint_to_ctoken( title="Program Guide" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/program-guides/program-mint-to-cToken" + href="/compressed-token-program/ctoken/program-guides/program-mint-to-c-Token" horizontal > - Mint cTokens + Mint c-Tokens
-Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). +Learn the [core concepts to the c-Token Program here](/compressed-token-program/c-token-program). # Get Started @@ -36,7 +36,7 @@ Learn the [core concepts to the cToken Program here](/compressed-token-program/c -### Close cToken Account +### Close c-Token Account
@@ -137,7 +137,7 @@ let system_accounts = SystemAccountInfos { -### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program 1. Pass the required accounts 2. Include `cmint_params` and `system_accounts` from the previous steps @@ -498,10 +498,10 @@ pub fn process_create_cmint_with_pda_authority( # Next Steps diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index 4e456ba0..f669c7e0 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -1,6 +1,6 @@ --- -title: Mint cTokens -description: Program guide to mint cTokens to a cMint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. +title: Mint c-Tokens +description: Program guide to mint c-Tokens to a cMint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. --- import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; @@ -9,9 +9,9 @@ import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-pre import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; -1. cTokens increase the supply of a cMint. -2. The destination cToken accounts must exist to receive the minted cTokens. -3. Only the mint authority can mint new cTokens. +1. c-Tokens increase the supply of a cMint. +2. The destination c-Token accounts must exist to receive the minted c-Tokens. +3. Only the mint authority can mint new c-Tokens. Find [full code examples at the end](#full-code-example). @@ -39,7 +39,7 @@ Find [full code examples at the end](#full-code-example). -### Mint cTokens +### Mint c-Tokens @@ -99,9 +99,9 @@ let system_accounts = SystemAccountInfos { -### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program -1. Pass the required accounts, including the destination cToken accounts. +1. Pass the required accounts, including the destination c-Token accounts. 2. Include `mint_params` and `system_accounts` from the previous steps 3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. @@ -331,7 +331,7 @@ pub fn process_mint_to_ctoken_invoke_signed( href="/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken" horizontal > - Mint cTokens + Mint c-Tokens - Transfer Interface cToken + Transfer Interface c-Token \ No newline at end of file diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx index b4f9ee29..251f62bb 100644 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx @@ -1,16 +1,16 @@ --- title: Create cMint -description: Program guide to create a cMint with token metadata via CPI to the cToken Program. Includes step-by-step implementation and full code examples. +description: Program guide to create a cMint with token metadata via CPI to the c-Token Program. Includes step-by-step implementation and full code examples. --- import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; 1. cMints uniquely represent a token on Solana and store its global metadata. 2. cMints are compressed accounts and rent-free. -3. Tokens created from cMints are cTokens. +3. Tokens created from cMints are c-Tokens. -Learn the [core concepts to the cToken Program here](/compressed-token-program/c-token-program). +Learn the [core concepts to the c-Token Program here](/compressed-token-program/c-token-program). @@ -110,7 +110,7 @@ let system_accounts = SystemAccountInfos { -### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program 1. Pass the required accounts 2. Include `cmint_params` and `system_accounts` from the previous steps @@ -467,10 +467,10 @@ pub fn process_create_cmint_with_pda_authority( # Next Steps diff --git a/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx index fc2733b4..b62e31ee 100644 --- a/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx +++ b/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx @@ -1,14 +1,14 @@ --- -title: Mint cTokens -description: Program guide to mint cTokens to a cMint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. +title: Mint c-Tokens +description: Program guide to mint c-Tokens to a cMint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. --- import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; import CMintToCTokenAccountsList from '/snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx'; -1. cTokens increase the supply of a cMint. -2. The destination cToken accounts must exist to receive the minted cTokens. -3. Only the mint authority can mint new cTokens. +1. c-Tokens increase the supply of a cMint. +2. The destination c-Token accounts must exist to receive the minted c-Tokens. +3. Only the mint authority can mint new c-Tokens. Find [full code examples at the end](#full-code-example). @@ -65,9 +65,9 @@ let system_accounts = SystemAccountInfos { -### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program -1. Pass the required accounts, including the destination cToken accounts. +1. Pass the required accounts, including the destination c-Token accounts. 2. Include `mint_params` and `system_accounts` from the previous steps 3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. @@ -295,7 +295,7 @@ pub fn process_mint_to_ctoken_invoke_signed( href="/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken" horizontal > - Mint cTokens + Mint c-Tokens - Transfer Interface cToken + Transfer Interface c-Token \ No newline at end of file diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx index 58ec9d0c..c466ff73 100644 --- a/compressed-token-program/ctoken/README.mdx +++ b/compressed-token-program/ctoken/README.mdx @@ -1,6 +1,6 @@ --- title: Overview -description: "Overview to cTokens and reference to client guides, program guides, and example repository." +description: "Overview to c-Tokens and reference to client guides, program guides, and example repository." --- import { CTokenVsSplCalculator } from '/snippets/jsx/ctoken-vs-spl-calculator.jsx'; import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; @@ -8,16 +8,16 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-guides-table.mdx'; -1. cToken & associated cToken accounts (cATAs) are **Solana accounts** that
**hold balances** of **cMint, SPL mint, or Token 2022 mint** tokens. -2. cToken accounts and cATAs are **compressible** that reduces account creation cost. +1. c-Token & associated c-Token accounts (c-ATAs) are **Solana accounts** that
**hold balances** of **cMint, SPL mint, or Token 2022 mint** tokens. +2. c-Token accounts and c-ATAs are **compressible** that reduces account creation cost. -| | cToken account | SPL token account | +| | c-Token account | SPL token account | |-----------|--------|-----| | **Creation Cost** | 22,288 lamports | 2,039,280 lamports | ## Compressible Rent -For cTokens and cATAs, the compressible extension implements a **custom rent config**: +For c-Tokens and c-ATAs, the compressible extension implements a **custom rent config**: @@ -131,7 +131,7 @@ add github link ## Next Steps -### Close cToken Account +### Close c-Token Account @@ -77,6 +77,6 @@ Run the full code example: href="/compressed-token-program/ctoken/program-guides/program-close-ctoken" horizontal > - Close cToken Accounts + Close c-Token Accounts diff --git a/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx b/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx index 5a79af68..e5e4edaf 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx @@ -1,6 +1,6 @@ --- -title: Create Associated cToken Accounts -description: Client guide to create associated cToken accounts (cATAs) with step-by-step implementation and full code examples. +title: Create Associated c-Token Accounts +description: Client guide to create associated c-Token accounts (c-ATAs) with step-by-step implementation and full code examples. --- import CAtaIntro from '/snippets/ctoken-guides/cata-intro.mdx'; import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; @@ -47,7 +47,7 @@ Run the full code example:
-### Create Associated cToken Account +### Create Associated c-Token Account @@ -75,10 +75,10 @@ Run the full code example: title="Program Guide" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/program-guides/program-create-cATA" + href="/compressed-token-program/ctoken/program-guides/program-create-c-ATA" horizontal > - Create Associated cToken Accounts + Create Associated c-Token Accounts - Create cToken Accounts + Create c-Token Accounts diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx index 4922b484..8400d344 100644 --- a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx +++ b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx @@ -1,6 +1,6 @@ --- -title: Create cToken Accounts -description: "Client guide to create cToken accounts with CreateCTokenAccount builder." +title: Create c-Token Accounts +description: "Client guide to create c-Token accounts with CreateCTokenAccount builder." --- import CTokenIntro from '/snippets/ctoken-guides/ctoken-intro.mdx'; import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; @@ -33,8 +33,8 @@ let instruction = CreateCTokenAccount::new(
-3. Send transaction & verify cToken account creation with `get_account`.
-cToken accounts are Solana accounts and use your familiar RPC methods. +3. Send transaction & verify c-Token account creation with `get_account`.
+c-Token accounts are Solana accounts and use your familiar RPC methods. @@ -47,7 +47,7 @@ cToken accounts are Solana accounts and use your familiar RPC methods. -### Create cToken Account +### Create c-Token Account ```rust use borsh::BorshDeserialize; @@ -73,7 +73,7 @@ async fn test_create_ctoken_account() { // Create compressed mint first (prerequisite) let (mint, _compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; - // Step 1: Generate new keypair for the cToken account + // Step 1: Generate new keypair for the c-Token account let account = Keypair::new(); let owner = payer.pubkey(); @@ -187,12 +187,12 @@ pub async fn create_compressed_mint( - Mint tokens to your cToken account + Mint tokens to your c-Token account diff --git a/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx b/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx index 01dfede1..79203bd4 100644 --- a/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx +++ b/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx @@ -1,6 +1,6 @@ --- title: Transfer Interface -description: Client guide for the unified transfer interface for cToken <> SPL token transfers with step-by-step implementation and full code examples. +description: Client guide for the unified transfer interface for c-Token <> SPL token transfers with step-by-step implementation and full code examples. --- import TransferInterfaceIntro from '/snippets/ctoken-guides/transfer-interface-intro.mdx'; import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; @@ -88,6 +88,6 @@ Run the full code example: href="/compressed-token-program/ctoken/client-guides/client-close-ctoken" horizontal > - Close cToken Accounts + Close c-Token Accounts diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx index 18e9716d..a434a684 100644 --- a/compressed-token-program/ctoken/close-ctoken-account.mdx +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -1,17 +1,17 @@ --- -title: Close cToken Accounts -description: Program guide to close cToken accounts via CPI to the cToken Program with step-by-step implementation and full code examples. +title: Close c-Token Accounts +description: Program guide to close c-Token accounts via CPI to the c-Token Program with step-by-step implementation and full code examples. --- import CloseAccountInfosAccountsList from '/snippets/accounts-list/close-account-infos-accounts-list.mdx'; import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; -1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. -2. cToken accounts can be closed +1. Closing a c-Token account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. +2. c-Token accounts can be closed * by the account owner at any time. * by the `compression_authority` - when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). + when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). # Get Started @@ -30,7 +30,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c -### Close cToken Account +### Close c-Token Account @@ -43,9 +43,9 @@ Find [a full code example at the end](#full-code-example). -### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program -1. Define the cToken account to close and where remaining lamports should go +1. Define the c-Token account to close and where remaining lamports should go 2. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. @@ -182,7 +182,7 @@ pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<( # Next Steps for 24h of rent
and compression incentive (the rent-exemption is sponsored by the protocol) 2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 766 lamports when the account's rent is below 3h. @@ -46,7 +46,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c -### Create cATA +### Create c-ATA @@ -69,13 +69,13 @@ Find [a full code example at the end](#full-code-example). -### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program 1. Pass the required accounts 2. Include rent config from `compressible_params` 3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. -The cATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike cToken accounts, owner and mint are passed as accounts, not in instruction data. +The c-ATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike c-Token accounts, owner and mint are passed as accounts, not in instruction data. @@ -243,7 +243,7 @@ pub fn process_create_ata2_invoke_signed( # Next Steps for 24h of rent
and compression incentive (the rent-exemption is sponsored by the protocol) 2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 766 lamports when the account's rent is below 3h. @@ -52,8 +52,8 @@ let instruction = CreateCTokenAccount::new( ).instruction()?; ``` -3. Send transaction & verify cToken account creation with `get_account`.
-cToken accounts are Solana accounts and use your familiar RPC methods. +3. Send transaction & verify c-Token account creation with `get_account`.
+c-Token accounts are Solana accounts and use your familiar RPC methods. @@ -66,7 +66,7 @@ cToken accounts are Solana accounts and use your familiar RPC methods.
-### Create cToken Account +### Create c-Token Account ```rust use borsh::BorshDeserialize; @@ -92,7 +92,7 @@ async fn test_create_ctoken_account() { // Create compressed mint first (prerequisite) let (mint, _compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; - // Step 1: Generate new keypair for the cToken account + // Step 1: Generate new keypair for the c-Token account let account = Keypair::new(); let owner = payer.pubkey(); @@ -205,7 +205,7 @@ Find [a full code example at the end](#full-code-example). -### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program 1. Pass the required accounts 2. Include rent config from `compressible_params` @@ -370,7 +370,7 @@ pub fn process_create_token_account_invoke_signed(
# Next Steps @@ -8,7 +8,7 @@ description: Overview to Token 2022 extensions supported by cTokens. Extension Name Description - cToken + c-Token Compressed Token diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx index 315652ce..763c12a3 100644 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx @@ -1,6 +1,6 @@ --- -title: Close cToken Accounts -description: Program guide to close cToken accounts via CPI to the cToken Program with step-by-step implementation and full code examples. +title: Close c-Token Accounts +description: Program guide to close c-Token accounts via CPI to the c-Token Program with step-by-step implementation and full code examples. --- import CloseAccountInfosAccountsList from '/snippets/accounts-list/close-account-infos-accounts-list.mdx'; @@ -17,9 +17,9 @@ Find [full code examples at the end](#full-code-example). -### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program -1. Define the cToken account to close and where remaining lamports should go +1. Define the c-Token account to close and where remaining lamports should go 2. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. @@ -160,7 +160,7 @@ pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<( href="/compressed-token-program/ctoken/client-guides/client-close-ctoken" horizontal > - Close cToken Accounts + Close c-Token Accounts -### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program 1. Pass the required accounts 2. Include rent config from `compressible_params` 3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. -The cATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike cToken accounts, owner and mint are passed as accounts, not in instruction data. +The c-ATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike c-Token accounts, owner and mint are passed as accounts, not in instruction data. @@ -204,18 +204,18 @@ pub fn process_create_ata2_invoke_signed( title="Client Guide" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/client-guides/client-create-cATA" + href="/compressed-token-program/ctoken/client-guides/client-create-c-ATA" horizontal > - Create Associated cToken Accounts + Create Associated c-Token Accounts - Mint cTokens + Mint c-Tokens diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx index c28dbc86..b469c46b 100644 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx @@ -1,6 +1,6 @@ --- -title: Create cToken Accounts -description: Program guide to create cToken accounts with step-by-step implementation and full code examples. +title: Create c-Token Accounts +description: Program guide to create c-Token accounts with step-by-step implementation and full code examples. --- import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; @@ -29,7 +29,7 @@ Find [full code examples at the end](#full-code-example).
-### Build Account Infos and CPI the cToken Program +### Build Account Infos and CPI the c-Token Program 1. Pass the required accounts 2. Include rent config from `compressible_params` @@ -194,12 +194,12 @@ pub fn process_create_token_account_invoke_signed( - Mint tokens to your cToken account + Mint tokens to your c-Token account diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx index 5e5f1167..c03f0dbc 100644 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx @@ -1,6 +1,6 @@ --- title: Transfer Interface -description: Program guide for transfers between cToken and SPL token accounts via CPI. The interface detects account types and invokes the right programs. +description: Program guide for transfers between c-Token and SPL token accounts via CPI. The interface detects account types and invokes the right programs. --- @@ -19,11 +19,11 @@ Find [full code examples at the end](#full-code-example). -### cToken Transfer Interface +### c-Token Transfer Interface -Define the number of cTokens / SPL tokens to transfer -- from which SPL or cToken account, and -- to which SPL or cToken account. +Define the number of c-Tokens / SPL tokens to transfer +- from which SPL or c-Token account, and +- to which SPL or c-Token account. ```rust use light_compressed_token_sdk::ctoken::TransferInterface; @@ -37,7 +37,7 @@ let mut transfer = TransferInterface::new( compressed_token_program_authority.clone(), ); ``` - + @@ -47,7 +47,7 @@ let mut transfer = TransferInterface::new( ### SPL Transfer Interface (Optional) -The SPL transfer interface is only needed for SPL ↔ cToken transfers. +The SPL transfer interface is only needed for SPL ↔ c-Token transfers. ```rust transfer = transfer.with_spl_interface( Some(mint.clone()), @@ -57,9 +57,9 @@ transfer = transfer.with_spl_interface( )?; ``` -SPL ↔ cToken transfers require a `spl_interface_pda`: - * **SPL → cToken**: SPL tokens are locked by the cToken Program in the PDA, cTokens are minted to cToken accounts - * **cToken → SPL**: cTokens are burned, SPL tokens transferred to SPL token accounts +SPL ↔ c-Token transfers require a `spl_interface_pda`: + * **SPL → c-Token**: SPL tokens are locked by the c-Token Program in the PDA, c-Tokens are minted to c-Token accounts + * **c-Token → SPL**: c-Tokens are burned, SPL tokens transferred to SPL token accounts The interface PDA is derived from the `mint` pubkey and pool seed. @@ -71,9 +71,9 @@ The interface PDA is derived from the `mint` pubkey and pool seed. -### CPI the cToken Program +### CPI the c-Token Program -CPI the cToken program to execute the transfer. +CPI the c-Token program to execute the transfer. Use `invoke()`, or `invoke_signed()` when a CPI requires a PDA signer. @@ -113,25 +113,25 @@ pub const TRANSFER_INTERFACE_AUTHORITY_SEED: &[u8] = b"transfer_interface_author #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct TransferInterfaceData { pub amount: u64, - /// Required for SPL<->cToken transfers, None for cToken->cToken + /// Required for SPL<->c-Token transfers, None for c-Token->c-Token pub token_pool_pda_bump: Option, } /// Handler for TransferInterface (invoke) /// /// This unified interface automatically detects account types and routes to: -/// - cToken -> cToken transfer -/// - cToken -> SPL transfer -/// - SPL -> cToken transfer +/// - c-Token -> c-Token transfer +/// - c-Token -> SPL transfer +/// - SPL -> c-Token transfer /// /// Account order: /// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or cToken) -/// - accounts[2]: destination_account (SPL or cToken) +/// - accounts[1]: source_account (SPL or c-Token) +/// - accounts[2]: destination_account (SPL or c-Token) /// - accounts[3]: authority (signer) /// - accounts[4]: payer (signer) /// - accounts[5]: compressed_token_program_authority -/// For SPL interface (required for SPL<->cToken): +/// For SPL interface (required for SPL<->c-Token): /// - accounts[6]: mint /// - accounts[7]: token_pool_pda /// - accounts[8]: spl_token_program @@ -173,12 +173,12 @@ pub fn process_transfer_interface_invoke( /// /// Account order: /// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or cToken) -/// - accounts[2]: destination_account (SPL or cToken) +/// - accounts[1]: source_account (SPL or c-Token) +/// - accounts[2]: destination_account (SPL or c-Token) /// - accounts[3]: authority (PDA, not signer - program signs) /// - accounts[4]: payer (signer) /// - accounts[5]: compressed_token_program_authority -/// For SPL interface (required for SPL<->cToken): +/// For SPL interface (required for SPL<->c-Token): /// - accounts[6]: mint /// - accounts[7]: token_pool_pda /// - accounts[8]: spl_token_program @@ -245,6 +245,6 @@ pub fn process_transfer_interface_invoke_signed( href="/compressed-token-program/ctoken/program-guides/program-close-ctoken" horizontal > - Close cToken Accounts + Close c-Token Accounts \ No newline at end of file diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index 4aaad203..669c33f3 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -1,6 +1,6 @@ --- title: Transfer Interface -description: Guide for transfers between cToken and SPL token accounts via CPI. The interface detects account types and invokes the right programs. +description: Guide for transfers between c-Token and SPL token accounts via CPI. The interface detects account types and invokes the right programs. --- import TransferInterfaceAccountsListCtoken1 from '/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx'; @@ -12,29 +12,29 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c - + - + - + @@ -42,8 +42,8 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c
**cToken → cToken Account****c-Token → c-Token Account**
    -
  • Transfers cTokens between cToken accounts
  • +
  • Transfers c-Tokens between c-Token accounts
**SPL token → cToken Account****SPL token → c-Token Account**
    -
  • Transfers SPL tokens to cToken accounts
  • +
  • Transfers SPL tokens to c-Token accounts
  • SPL tokens are locked in interface PDA
  • -
  • cTokens are minted to cToken account
  • +
  • c-Tokens are minted to c-Token account
**cToken → SPL Account****c-Token → SPL Account**
  • Releases SPL tokens from interface PDA to SPL account
  • -
  • Burns cTokens in source cToken account
  • +
  • Burns c-Tokens in source c-Token account
-* For example, **SPL → cToken** can be used for transfers from Alice's SPL token account to her own cToken account. -* You can use this to **convert existing SPL tokens to cTokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the cToken program](/compressed-token-program/ctoken/README). +* For example, **SPL → c-Token** can be used for transfers from Alice's SPL token account to her own c-Token account. +* You can use this to **convert existing SPL tokens to c-Tokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the c-Token program](/compressed-token-program/ctoken/README). # Get Started @@ -67,7 +67,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c
-### Close cToken Account +### Close c-Token Account
@@ -80,11 +80,11 @@ Find [a full code example at the end](#full-code-example). -### cToken Transfer Interface +### c-Token Transfer Interface -Define the number of cTokens / SPL tokens to transfer -- from which SPL or cToken account, and -- to which SPL or cToken account. +Define the number of c-Tokens / SPL tokens to transfer +- from which SPL or c-Token account, and +- to which SPL or c-Token account. ```rust use light_compressed_token_sdk::ctoken::TransferInterface; @@ -98,7 +98,7 @@ let mut transfer = TransferInterface::new( compressed_token_program_authority.clone(), ); ``` - + @@ -108,7 +108,7 @@ let mut transfer = TransferInterface::new( ### SPL Transfer Interface (Optional) -The SPL transfer interface is only needed for SPL ↔ cToken transfers. +The SPL transfer interface is only needed for SPL ↔ c-Token transfers. ```rust transfer = transfer.with_spl_interface( Some(mint.clone()), @@ -118,9 +118,9 @@ transfer = transfer.with_spl_interface( )?; ``` -SPL ↔ cToken transfers require a `spl_interface_pda`: - * **SPL → cToken**: SPL tokens are locked by the cToken Program in the PDA, cTokens are minted to cToken accounts - * **cToken → SPL**: cTokens are burned, SPL tokens transferred to SPL token accounts +SPL ↔ c-Token transfers require a `spl_interface_pda`: + * **SPL → c-Token**: SPL tokens are locked by the c-Token Program in the PDA, c-Tokens are minted to c-Token accounts + * **c-Token → SPL**: c-Tokens are burned, SPL tokens transferred to SPL token accounts The interface PDA is derived from the `mint` pubkey and pool seed. @@ -132,9 +132,9 @@ The interface PDA is derived from the `mint` pubkey and pool seed. -### CPI the cToken Program +### CPI the c-Token Program -CPI the cToken program to execute the transfer. +CPI the c-Token program to execute the transfer. Use `invoke()`, or `invoke_signed()` when a CPI requires a PDA signer. @@ -174,25 +174,25 @@ pub const TRANSFER_INTERFACE_AUTHORITY_SEED: &[u8] = b"transfer_interface_author #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct TransferInterfaceData { pub amount: u64, - /// Required for SPL<->cToken transfers, None for cToken->cToken + /// Required for SPL<->c-Token transfers, None for c-Token->c-Token pub token_pool_pda_bump: Option, } /// Handler for TransferInterface (invoke) /// /// This unified interface automatically detects account types and routes to: -/// - cToken -> cToken transfer -/// - cToken -> SPL transfer -/// - SPL -> cToken transfer +/// - c-Token -> c-Token transfer +/// - c-Token -> SPL transfer +/// - SPL -> c-Token transfer /// /// Account order: /// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or cToken) -/// - accounts[2]: destination_account (SPL or cToken) +/// - accounts[1]: source_account (SPL or c-Token) +/// - accounts[2]: destination_account (SPL or c-Token) /// - accounts[3]: authority (signer) /// - accounts[4]: payer (signer) /// - accounts[5]: compressed_token_program_authority -/// For SPL interface (required for SPL<->cToken): +/// For SPL interface (required for SPL<->c-Token): /// - accounts[6]: mint /// - accounts[7]: token_pool_pda /// - accounts[8]: spl_token_program @@ -234,12 +234,12 @@ pub fn process_transfer_interface_invoke( /// /// Account order: /// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or cToken) -/// - accounts[2]: destination_account (SPL or cToken) +/// - accounts[1]: source_account (SPL or c-Token) +/// - accounts[2]: destination_account (SPL or c-Token) /// - accounts[3]: authority (PDA, not signer - program signs) /// - accounts[4]: payer (signer) /// - accounts[5]: compressed_token_program_authority -/// For SPL interface (required for SPL<->cToken): +/// For SPL interface (required for SPL<->c-Token): /// - accounts[6]: mint /// - accounts[7]: token_pool_pda /// - accounts[8]: spl_token_program @@ -293,7 +293,7 @@ pub fn process_transfer_interface_invoke_signed( # Next Steps 25,000 lamports | +| **Token Account** | 2,039,280 lamports | ~22,288 lamports | -Learn the core concepts to the [cToken program here](/learn/c-token-program). +Learn the core concepts to the [c-Token program here](/learn/c-token-program). # Start building @@ -36,25 +40,40 @@ Learn the core concepts to the [cToken program here](/learn/c-token-program). ### Set up developer environment + - + -### Get started +### Get Started +## c-Mint Accounts + +1. cMints uniquely represent a token on Solana and store its global metadata. +2. Tokens created from cMints are c-Tokens. The mint's address serves as the token's unique identifier. +3. cMints are compressed accounts and rent-free. + +## c-Token Accounts + +1. c-Token & associated c-Token accounts (c-ATAs) are **Solana accounts** that
**hold balances** of **cMint, SPL mint, or Token 2022 mint** tokens. +2. Existing **SPL tokens can be converted** to c-Tokens and back on demand. +3. The compressible extension implements a **custom rent config**. + +#### Custom Rent Config -## Guides + - +### Guides + -## Advanced Guides - +## Integrate c-Token +ADD TABLE OF "FOR" ## Next Steps diff --git a/compressed-token-program/stash.mdx b/compressed-token-program/stash.mdx index f9520e9b..c6acfe7e 100644 --- a/compressed-token-program/stash.mdx +++ b/compressed-token-program/stash.mdx @@ -1,4 +1,4 @@ **SPL mints and tokens** owned by the [Token Program](https://github.com/solana-program/token) or [Token-2022](https://github.com/solana-program/token-2022) **require rent** by default.
-You can **transfer SPL token accounts to cToken accounts** for **sponsored rent-exemption** while keeping the **same interoparability**. +You can **transfer SPL token accounts to c-Token accounts** for **sponsored rent-exemption** while keeping the **same interoparability**.
\ No newline at end of file diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx index d21907b4..af691f37 100644 --- a/compressed-tokens/README.mdx +++ b/compressed-tokens/README.mdx @@ -10,8 +10,8 @@ import InstallDependencies from '/snippets/setup/install-dependencies-codegroup. import { TokenAccountCompressedVsSpl } from '/snippets/jsx/token-account-compressed-vs-spl.jsx'; 1. Compressed tokens are compressed accounts with `TokenData` and rent-free. -2. Compressed token accounts store token balance, owner, and other information of tokens like SPL and cTokens. -3. Any cToken or SPL token can be compressed/decompressed at will. +2. Compressed token accounts store token balance, owner, and other information of tokens like SPL and c-Tokens. +3. Any c-Token or SPL token can be compressed/decompressed at will. ## Recommended Usage of Compressed Tokens @@ -19,7 +19,7 @@ import { TokenAccountCompressedVsSpl } from '/snippets/jsx/token-account-compres #### Storage of Inactive Token Accounts * Most (associated) token accounts are not frequently written to. * **Store token accounts rent-free** when inactive - * [cTokens](/compressed-token-program/ctoken/README) are automatically compressed/decompressed, when active/inactive and include **sponsored rent-exemption**. [Learn more here](/compressed-token-program/ctoken/README). + * [c-Tokens](/compressed-token-program/ctoken/README) are automatically compressed/decompressed, when active/inactive and include **sponsored rent-exemption**. [Learn more here](/compressed-token-program/ctoken/README). #### [Token Distribution](#advanced-guides) diff --git a/docs.json b/docs.json index 287841af..5ed83800 100644 --- a/docs.json +++ b/docs.json @@ -11,151 +11,100 @@ "appearance": { "default": "light" }, - "customCSS": "/custom.css", - "seo": { - "metatags": { - "canonical": "https://www.zkcompression.com", - "keywords": "solana, compression, zk compression, solana rent, solana rent free, account rent, compressed tokens, compressed accounts, solana pda, program derived address, airdrop, cheap airdrop, solana aidrop, SPL tokens, token distribution, stablecoin payments, solana pay, solana mobile, state compression, zero-knowledge proofs, zk proofs, validity proofs, merkle trees, rent-free accounts, solana scalability, L1 scalability, Light Protocol, Helius, Solana Foundation, solana SDK, solana development, web3 infrastructure, blockchain scalability, cheap token accounts, token account compression, scale Solana, Solana problems, Solana token accounts, compressed accounts, compressed NFTs, state compression, Metaplex, Solana Superteam, DePin, getMinimumBalanceForRentExemption, fetchSysvarRent(rpc, config?), getAccountInfo" - } - }, - "errors": { - "404": { - "redirect": false, - "title": "Oops ... rent not found", - "description": "Use our AI assistant to find what you are looking for!" - } - }, "navigation": { "tabs": [ { "tab": "Docs", "pages": [ { - "group": "Introduction", + "group": "Get Started", "pages": [ - "intro-pages/landing", - "intro-pages/quickstart", - "intro-pages/support" + "/landing", + "/quickstart", + "learn/ai-tools-guide" ] }, { - "group": "Learn", - "pages": [ - { - "group": "Core Concepts", - "pages": [ - "learn/core-concepts", - "learn/core-concepts/compressed-account-model", - "learn/core-concepts/merkle-trees-validity-proofs", - "learn/core-concepts/transaction-lifecycle", - "learn/core-concepts/considerations" - ] - }, - "rent", - "learn/c-token-program" - ] - }, - { - "group": "cToken Program", + "group": "c-Token", "pages": [ "compressed-token-program/overview", { - "group": "cMint", + "group": "Cookbook", "pages": [ - "compressed-token-program/cmint/README", - "compressed-token-program/cmint/create-cmint", - "compressed-token-program/cmint/update-metadata", - "compressed-token-program/cmint/mint-ctokens", { - "group": "Client Guides", - "hidden": true, + "group": "cMint", + "expanded": true, "pages": [ - "compressed-token-program/cmint/client-guides/cmint", - "compressed-token-program/cmint/client-guides/update-metadata", - "compressed-token-program/cmint/client-guides/client-mint-to-ctoken" + "compressed-token-program/cmint/create-cmint", + "compressed-token-program/cmint/update-metadata", + "compressed-token-program/cmint/mint-ctokens" ] }, { - "group": "Program Guides", - "hidden": true, + "group": "c-Token", + "expanded": true, "pages": [ - "compressed-token-program/cmint/program-guides/program-create-cmint", - "compressed-token-program/cmint/program-guides/program-mint-to-cToken" + "compressed-token-program/ctoken/create-ctoken", + "compressed-token-program/ctoken/create-cata", + "compressed-token-program/ctoken/transfer-interface", + "compressed-token-program/ctoken/close-ctoken-account" ] } ] }, { - "group": "cToken", + "group": "Integrate c-Token", + "expanded": true, "pages": [ - "compressed-token-program/ctoken/README", - "compressed-token-program/ctoken/create-ctoken", - "compressed-token-program/ctoken/create-cata", - "compressed-token-program/ctoken/transfer-interface", - "compressed-token-program/ctoken/close-ctoken-account", - "compressed-token-program/ctoken/extensions", - { - "group": "Client Guides", - "hidden": true, - "pages": [ - "compressed-token-program/ctoken/client-guides/client-create-ctoken", - "compressed-token-program/ctoken/client-guides/client-create-cATA", - "compressed-token-program/ctoken/client-guides/client-transfer-interface", - "compressed-token-program/ctoken/client-guides/client-close-ctoken" - ] - }, - { - "group": "Program Guides", - "hidden": true, - "pages": [ - "compressed-token-program/ctoken/program-guides/program-create-ctoken", - "compressed-token-program/ctoken/program-guides/program-create-cATA", - "compressed-token-program/ctoken/program-guides/program-transfer-interface", - "compressed-token-program/ctoken/program-guides/program-close-ctoken" - ] - } + "compressed-token-program/integrate/for-aggregators", + "compressed-token-program/integrate/for-dexs", + "compressed-token-program/integrate/for-defi-markets", + "compressed-token-program/integrate/for-trading-apps", + "compressed-token-program/integrate/for-wallet-applications", + "compressed-token-program/integrate/for-payment-companies" + ] + }, + "compressed-token-program/ctoken/extensions" + ] + }, + { + "group": "Compressed Tokens", + "pages": [ + "compressed-tokens/README", + { + "group": "Cookbook", + "pages": [ + "compressed-tokens/guides/how-to-create-compressed-token-accounts", + "compressed-tokens/guides/how-to-mint-compressed-tokens", + "compressed-tokens/guides/how-to-transfer-compressed-token", + "compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens", + "compressed-tokens/guides/how-to-compress-complete-spl-token-accounts", + "compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression", + "compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts", + "compressed-tokens/guides/how-to-merge-compressed-token-accounts", + "compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority" ] }, { - "group": "Compressed Token", + "group": "Implementation Guides", "pages": [ - "compressed-tokens/README", { - "group": "Basic Guides", + "group": "Integration", + "expanded": true, "pages": [ - "compressed-tokens/guides/how-to-create-compressed-token-accounts", - "compressed-tokens/guides/how-to-mint-compressed-tokens", - "compressed-tokens/guides/how-to-transfer-compressed-token", - "compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens", - "compressed-tokens/guides/how-to-compress-complete-spl-token-accounts", - "compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression", - "compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts", - "compressed-tokens/guides/how-to-merge-compressed-token-accounts", - "compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority" + "compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", + "compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", + "compressed-tokens/advanced-guides/use-token-2022-with-compression" ] }, { - "group": "Advanced Guides", + "group": "Examples", + "expanded": true, "pages": [ - { - "group": "Integration", - "expanded": true, - "pages": [ - "compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", - "compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", - "compressed-tokens/advanced-guides/use-token-2022-with-compression" - ] - }, - { - "group": "Examples", - "expanded": true, - "pages": [ - "compressed-tokens/advanced-guides/create-an-airdrop", - "compressed-tokens/advanced-guides/create-an-airdrop-with-claim", - "compressed-tokens/advanced-guides/example-web-client", - "compressed-tokens/advanced-guides/example-node-js" - ] - } + "compressed-tokens/advanced-guides/create-an-airdrop", + "compressed-tokens/advanced-guides/create-an-airdrop-with-claim", + "compressed-tokens/advanced-guides/example-web-client", + "compressed-tokens/advanced-guides/example-node-js" ] } ] @@ -163,7 +112,7 @@ ] }, { - "group": "Compressed PDAs", + "group": "PDAs", "pages": [ "compressed-pdas/create-a-program-with-compressed-pdas", { @@ -452,6 +401,23 @@ } ] }, + { + "group": "Learn", + "pages": [ + { + "group": "Core Concepts", + "pages": [ + "learn/core-concepts", + "learn/core-concepts/compressed-account-model", + "learn/core-concepts/merkle-trees-validity-proofs", + "learn/core-concepts/transaction-lifecycle", + "learn/core-concepts/considerations" + ] + }, + "rent", + "learn/c-token-program" + ] + }, { "group": "Resources", "pages": [ @@ -485,6 +451,7 @@ { "group": "SDK Reference", "pages": [ + "changelog", "api-reference/libraries/stateless-js", "api-reference/libraries/compressed-token", "api-reference/libraries/light-client", @@ -493,190 +460,14 @@ ] } ] - }, - { - "tab": "cToken Program", - "pages": [ - "compressed-token-program/overview", - "compressed-token-program/c-token-program", - { - "group": "cMint", - "pages": [ - "compressed-token-program/cmint/README", - "compressed-token-program/cmint/create-cmint", - { - "group": "Client Guides", - "pages": [ - "compressed-token-program/cmint/client-guides/cmint", - "compressed-token-program/cmint/client-guides/client-create-cmint", - "compressed-token-program/cmint/client-guides/update-metadata" - ] - }, - { - "group": "Program Guides", - "pages": [ - "compressed-token-program/cmint/program-guides/program-create-cmint" - ] - } - ] - }, - { - "group": "cToken", - "pages": [ - "compressed-token-program/ctoken/README", - { - "group": "Client Guides", - "pages": [ - "compressed-token-program/ctoken/client-guides/client-create-ctoken", - "compressed-token-program/ctoken/client-guides/client-create-cATA", - "compressed-token-program/ctoken/client-guides/client-mint-to-ctoken", - "compressed-token-program/ctoken/client-guides/client-transfer-interface", - "compressed-token-program/ctoken/client-guides/client-close-ctoken" - ] - }, - { - "group": "Program Guides", - "pages": [ - "compressed-token-program/ctoken/program-guides/program-create-ctoken", - "compressed-token-program/ctoken/program-guides/program-create-cATA", - "compressed-token-program/ctoken/program-guides/program-mint-to-cToken", - "compressed-token-program/ctoken/program-guides/program-transfer-interface", - "compressed-token-program/ctoken/program-guides/program-close-ctoken" - ] - } - ] - }, - { - "group": "Compressed Token", - "pages": [ - "compressed-tokens/README", - { - "group": "Basic Guides", - "pages": [ - "compressed-tokens/guides/how-to-create-compressed-token-accounts", - "compressed-tokens/guides/how-to-mint-compressed-tokens", - "compressed-tokens/guides/how-to-transfer-compressed-token", - "compressed-tokens/guides/how-to-compress-and-decompress-spl-tokens", - "compressed-tokens/guides/how-to-compress-complete-spl-token-accounts", - "compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression", - "compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts", - "compressed-tokens/guides/how-to-merge-compressed-token-accounts", - "compressed-tokens/guides/how-to-approve-and-revoke-delegate-authority" - ] - }, - { - "group": "Advanced Guides", - "pages": [ - { - "group": "Integration", - "expanded": true, - "pages": [ - "compressed-tokens/advanced-guides/how-to-combine-operations-in-one-transaction", - "compressed-tokens/advanced-guides/add-wallet-support-for-compressed-tokens", - "compressed-tokens/advanced-guides/use-token-2022-with-compression" - ] - }, - { - "group": "Examples", - "expanded": true, - "pages": [ - "compressed-tokens/advanced-guides/create-an-airdrop", - "compressed-tokens/advanced-guides/create-an-airdrop-with-claim", - "compressed-tokens/advanced-guides/example-web-client", - "compressed-tokens/advanced-guides/example-node-js" - ] - } - ] - } - ] - }, - "compressed-token-program/ctoken/extensions" - ] - }, - { - "tab": "Compressed PDAs", - "pages": [ - "compressed-pdas/create-a-program-with-compressed-pdas", - { - "group": "Guides", - "pages": [ - "compressed-pdas/guides/how-to-create-compressed-accounts", - "compressed-pdas/guides/how-to-update-compressed-accounts", - "compressed-pdas/guides/how-to-close-compressed-accounts", - "compressed-pdas/guides/how-to-reinitialize-compressed-accounts", - "compressed-pdas/guides/how-to-burn-compressed-accounts" - ] - }, - "compressed-pdas/program-examples" - ] - }, - { - "tab": "API Reference", - "pages": [ - { - "group": "SDK Reference", - "pages": [ - "api-reference/index" - ] - }, - { - "group": "JSON RPC Methods", - "pages": [ - "api-reference/json-rpc-methods/overview", - { - "group": "Methods", - "pages": [ - "api-reference/json-rpc-methods/methods", - "api-reference/json-rpc-methods/getcompressedaccount", - "api-reference/json-rpc-methods/getcompressedaccountsbyowner", - "api-reference/json-rpc-methods/getcompressedbalancebyowner", - "api-reference/json-rpc-methods/getcompressedbalance", - "api-reference/json-rpc-methods/getcompressedminttokenholders", - "api-reference/json-rpc-methods/getcompressedtokenaccountbalance", - "api-reference/json-rpc-methods/getcompressedtokenaccountbydelegate", - "api-reference/json-rpc-methods/getcompressedtokenaccountsbyowner", - "api-reference/json-rpc-methods/getcompressedtokenbalancesbyowner", - "api-reference/json-rpc-methods/getcompressionsignaturesforaccount", - "api-reference/json-rpc-methods/getcompressionsignaturesforaddress", - "api-reference/json-rpc-methods/getcompressionsignaturesforowner", - "api-reference/json-rpc-methods/getcompressionsignaturesfortokenowner", - "api-reference/json-rpc-methods/getindexerhealth", - "api-reference/json-rpc-methods/getindexerslot", - "api-reference/json-rpc-methods/getlatestcompressionsignatures", - "api-reference/json-rpc-methods/getlatestnonvotingsignatures", - "api-reference/json-rpc-methods/getmultiplecompressedaccounts", - "api-reference/json-rpc-methods/getmultiplenewaddressproofs", - "api-reference/json-rpc-methods/gettransactionwithcompressioninfo", - "api-reference/json-rpc-methods/getvalidityproof" - ] - } - ] - } - ] - }, - { - "tab": "Changelog", - "pages": [ - "changelog" - ] } ], "global": { "anchors": [ { - "anchor": "AI Tools", - "href": "https://zkcompression.com/learn/ai-tools-guide", - "icon": "bot" - }, - { - "anchor": "GitHub", - "href": "https://github.com/Lightprotocol/light-protocol", - "icon": "github" - }, - { - "anchor": "Discord", - "href": "https://discord.com/invite/CYvjBgzRFP", - "icon": "discord" + "anchor": "Support", + "href": "https://www.zkcompression.com/support", + "icon": "headset" } ] } @@ -737,26 +528,6 @@ "x": "https://x.com/lightprotocol", "github": "https://github.com/Lightprotocol/light-protocol", "discord": "https://discord.com/invite/CYvjBgzRFP" - }, - "links": [ - { - "header": "Compressed by Light Protocol", - "items": [ - { - "label": "lightprotocol.com", - "href": "https://lightprotocol.com" - } - ] - }, - { - "header": "Indexed by Helius", - "items": [ - { - "label": "helius.dev", - "href": "https://helius.dev" - } - ] - } - ] + } } -} \ No newline at end of file +} diff --git a/learn/c-token-program.mdx b/learn/c-token-program.mdx index 53ad3b83..13ad8e8f 100644 --- a/learn/c-token-program.mdx +++ b/learn/c-token-program.mdx @@ -1,6 +1,6 @@ --- -title: cToken Program -description: Core Concepts of cMints, cTokens, and compressed token accounts. +title: c-Token Program +description: Core Concepts of cMints, c-Tokens, and compressed token accounts. --- import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; @@ -9,7 +9,7 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, cTokens and compressed tokens. +The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, c-Tokens and compressed tokens. @@ -31,7 +31,7 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t - + @@ -65,7 +65,7 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t cMints **uniquely represent a token on Solana and store its global metadata**, similar to SPL mint accounts with few core differences: 1. cMint accounts are **rent-free**. -2. Tokens created from cMints are **cTokens**. +2. Tokens created from cMints are **c-Tokens**. 3. Token metadata (name, symbol, URI) is stored as an extension in the struct. @@ -171,23 +171,23 @@ Here is how cMints and SPL mints compare: -# cToken Account +# c-Token Account -**cToken accounts are Solana accounts**, not compressed accounts. +**c-Token accounts are Solana accounts**, not compressed accounts. -A cToken account holds token balances like SPL Token accounts: -* A wallet needs a cToken account for each cMint it wants to hold, with the wallet address set as the cToken account owner. -* Each wallet can own multiple cToken accounts for the same cMint. -* A cToken account can only have one owner and hold units of one cMint. +A c-Token account holds token balances like SPL Token accounts: +* A wallet needs a c-Token account for each cMint it wants to hold, with the wallet address set as the c-Token account owner. +* Each wallet can own multiple c-Token accounts for the same cMint. +* A c-Token account can only have one owner and hold units of one cMint. Diagram showing cToken Solana account structure with three components: Address (identifier for cToken account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Owner set to Compressed Token Program), and AccountData (containing Mint, Owner, Amount, and Extensions fields) @@ -210,17 +210,17 @@ A cToken account holds token balances like SPL Token accounts: -Find the [source code of cToken here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken_struct.rs). +Find the [source code of c-Token here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken_struct.rs). -cToken accounts replicate the field layout and serialization format of [SPL Token accounts](https://solana.com/docs/tokens#token-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens. +c-Token accounts replicate the field layout and serialization format of [SPL Token accounts](https://solana.com/docs/tokens#token-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens. -Here is how cTokens and SPL tokens compare: +Here is how c-Tokens and SPL tokens compare:
**[cToken](#ctoken-account)****[c-Token](#ctoken-account)** Solana account
    @@ -49,7 +49,7 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t
  • **Compressed account** with `TokenData` field
  • **Rent-free** and SPL-compatible
  • Use for **storage of inactive tokens** and **token distribution**
  • -
  • cToken accounts are automatically compressed/decompressed when active/inactive.
  • +
  • c-Token accounts are automatically compressed/decompressed when active/inactive.
- + @@ -275,13 +275,13 @@ Here is how cTokens and SPL tokens compare: ### Compressible Extension -cToken accounts are compressible: +c-Token accounts are compressible: -This extension makes cToken accounts 200x cheaper than SPL token accounts +This extension makes c-Token accounts 200x cheaper than SPL token accounts - | | cToken | SPL | + | | c-Token | SPL | |-----------|--------|-----| | allocate | 0.000011 | 0.002 | | rent for 24h | 0.000011 | - | @@ -297,11 +297,11 @@ This extension makes cToken accounts 200x cheaper than SPL token accounts -# Associated cToken Account +# Associated c-Token Account -**Associated cToken** accounts (cATAs) follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): -* Each wallet needs its own cToken account to hold tokens from the same cMint. -* The address for cATAs is deterministically derived with the owner’s address, cToken program ID, and mint address. +**Associated c-Token** accounts (c-ATAs) follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): +* Each wallet needs its own c-Token account to hold tokens from the same cMint. +* The address for c-ATAs is deterministically derived with the owner’s address, c-Token program ID, and mint address. ```rust let seeds = [ @@ -313,17 +313,17 @@ let seeds = [ ``` -Find the [source code to associated cToken accounts here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/create_associated_token_account.rs). +Find the [source code to associated c-Token accounts here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/create_associated_token_account.rs). # Compressed Token Account -Compressed token accounts store token balance, owner, and other information like SPL and cTokens. Any cToken or SPL token can be compressed/decompressed at will. +Compressed token accounts store token balance, owner, and other information like SPL and c-Tokens. Any c-Token or SPL token can be compressed/decompressed at will. We recommend to use compressed tokens for **token distribution** or **storage of inactive tokens**. -**cToken accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. +**c-Token accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. diff --git a/rent.mdx b/rent.mdx index f57c204c..6c50e033 100644 --- a/rent.mdx +++ b/rent.mdx @@ -29,7 +29,7 @@ Compressible accounts only require rent for the prepaid epochs. Accounts auto-co ```rust rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) -// For 260-byte cToken account: +// For 260-byte c-Token account: // 128 + (260 * 1) = 388 lamports per epoch ``` diff --git a/resources/addresses-and-urls.mdx b/resources/addresses-and-urls.mdx index 596ddbd7..92dfb521 100644 --- a/resources/addresses-and-urls.mdx +++ b/resources/addresses-and-urls.mdx @@ -108,7 +108,7 @@ Find all JSON RPC Methods for ZK Compression [here](/resources/json-rpc-methods) The account to convert format between: * compressed token ↔ SPL token -* cToken ↔ SPL token +* c-Token ↔ SPL token | | | |:-|:-| diff --git a/snippets/accounts-list/close-account-infos-accounts-list.mdx b/snippets/accounts-list/close-account-infos-accounts-list.mdx index 3ca98eb6..e4791924 100644 --- a/snippets/accounts-list/close-account-infos-accounts-list.mdx +++ b/snippets/accounts-list/close-account-infos-accounts-list.mdx @@ -14,12 +14,12 @@ - + - + @@ -30,7 +30,7 @@ @@ -38,7 +38,7 @@ diff --git a/snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx b/snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx index 4cb6ed02..00f29c8e 100644 --- a/snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx +++ b/snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx @@ -38,9 +38,9 @@ - + - + diff --git a/snippets/accounts-list/ctoken-create-accounts-list-client.mdx b/snippets/accounts-list/ctoken-create-accounts-list-client.mdx index 6e5d2e3b..9789c7c0 100644 --- a/snippets/accounts-list/ctoken-create-accounts-list-client.mdx +++ b/snippets/accounts-list/ctoken-create-accounts-list-client.mdx @@ -28,7 +28,7 @@ @@ -64,11 +64,11 @@ - + diff --git a/snippets/accounts-list/ctoken-create-accounts-list.mdx b/snippets/accounts-list/ctoken-create-accounts-list.mdx index 6e7f1eee..12da112f 100644 --- a/snippets/accounts-list/ctoken-create-accounts-list.mdx +++ b/snippets/accounts-list/ctoken-create-accounts-list.mdx @@ -20,10 +20,10 @@ - + diff --git a/snippets/accounts-list/ctoken-create-ata-accounts-list.mdx b/snippets/accounts-list/ctoken-create-ata-accounts-list.mdx index db4936c0..1da8c0ed 100644 --- a/snippets/accounts-list/ctoken-create-ata-accounts-list.mdx +++ b/snippets/accounts-list/ctoken-create-ata-accounts-list.mdx @@ -15,8 +15,8 @@ @@ -24,7 +24,7 @@ @@ -36,10 +36,10 @@ - + @@ -51,7 +51,7 @@ - + diff --git a/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx b/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx index ada4349f..d2cc964f 100644 --- a/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx +++ b/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx @@ -14,12 +14,12 @@ - + - + @@ -37,7 +37,7 @@ - +
FieldcTokenc-Token SPL Token Account
Token Program -The cToken program for CPI.The c-Token program for CPI.
Account mutableThe cToken account to close.The c-Token account to close.
DestinationOwner signer* - - Owner of the cToken account.
+ - Owner of the c-Token account.
- *Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds.
Rent Sponsor mutable, optional - * cToken program PDA that fronts rent exemption at creation. + * c-Token program PDA that fronts rent exemption at creation. * Claims rent when account compresses and/or is closed.
Account of the output queue associated with the state tree the cMint will be stored in. The updated hash of the cMint is inserted to this queue.
cToken Accountsc-Token Accounts mutableDestination cToken Solana accounts to receive minted tokens.Destination c-Token Solana accounts to receive minted tokens.
System AccountsToken Account signer*, mutable - - The cToken account being created.
+ - The c-Token account being created.
- *Must be signer for `invoke()`. - *For `invoke_signed()`, program signs via PDA seeds.
6cToken Programc-Token Program - - - Program to create and interact with cMints, cTokens and compressed tokens. - - Your program calls this to create the cToken account. + - Program to create and interact with cMints, c-Tokens and compressed tokens. + - Your program calls this to create the c-Token account.
cToken Accountc-Token Account signer*, mutable - - The cToken account being created.
+ - The c-Token account being created.
- *Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds.
Owner - - - The wallet that will own this cATA.
- - Used to derive the cATA address deterministically. + - The wallet that will own this c-ATA.
+ - Used to derive the c-ATA address deterministically.
- - The SPL or cMint token mint.
- - Used to derive the cATA address deterministically. + - Used to derive the c-ATA address deterministically.
cATA Accountc-ATA Account mutable - - The cATA being created.
+ - The c-ATA being created.
- Address is derived from `[owner, ctoken_program_id, mint]`.
Bump u8The PDA bump seed for the cATA address derivation.The PDA bump seed for the c-ATA address derivation.
Idempotent
Source Account mutableThe source account (SPL token account or cToken account).The source account (SPL token account or c-Token account).
Destination Account mutableThe destination account (SPL token account or cToken account).The destination account (SPL token account or c-Token account).
Authority
Compressed Token Program Authority -The cToken program authority PDA.The c-Token program authority PDA.
diff --git a/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx b/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx index c8a0496f..06da6bd4 100644 --- a/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx +++ b/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx @@ -24,7 +24,7 @@ Interface PDA mutable - Interface PDA for SPL ↔ cToken transfers. + Interface PDA for SPL ↔ c-Token transfers. diff --git a/snippets/compressible-rent-explained.mdx b/snippets/compressible-rent-explained.mdx index 89a59670..96944aed 100644 --- a/snippets/compressible-rent-explained.mdx +++ b/snippets/compressible-rent-explained.mdx @@ -2,9 +2,9 @@ 2. Compressible rent is paid per rent-epoch: 388 lamports for 1.5h,
paid by account creator and transaction payers to keep accounts "active". 3. "Inactive" accounts, where rent is below one epoch, are compressed
and the rent-exemption can be claimed by the rent sponsor. -4. Transfers to inactive accounts "load" it with the same state (decompress). +4. Transfers to inactive accounts "load" it with the same state (decompress). -Compressible rent is paid in three scenarios: +This way rent is only paid when accounts are used: @@ -19,21 +19,21 @@ Compressible rent is paid in three scenarios: - + - - + + - + diff --git a/snippets/compressible-vs-solana-rent.mdx b/snippets/compressible-vs-solana-rent.mdx index 50f52bdb..7550f54d 100644 --- a/snippets/compressible-vs-solana-rent.mdx +++ b/snippets/compressible-vs-solana-rent.mdx @@ -5,7 +5,7 @@ Two epochs for compressible accounts have a length of **27,000 slots**. ``` rust rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) -// For 260-byte cToken account: +// For 260-byte c-Token account: // 128 + (260 * 1) = 388 lamports per epoch ``` diff --git a/snippets/ctoken-guides/cata-intro.mdx b/snippets/ctoken-guides/cata-intro.mdx index 49ccef20..8ba6a08e 100644 --- a/snippets/ctoken-guides/cata-intro.mdx +++ b/snippets/ctoken-guides/cata-intro.mdx @@ -1,9 +1,9 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -1. Associated cToken accounts (cATA) are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. -2. The address for cATAs is deterministically derived with the owner's address, *cToken program ID*, and mint address. -3. cATAs accounts are compressible with a default rent config. +1. Associated c-Token accounts (c-ATA) are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. +2. The address for c-ATAs is deterministically derived with the owner's address, *c-Token program ID*, and mint address. +3. c-ATAs accounts are compressible with a default rent config. diff --git a/snippets/ctoken-guides/close-intro.mdx b/snippets/ctoken-guides/close-intro.mdx index c1c44dc9..f4a281bd 100644 --- a/snippets/ctoken-guides/close-intro.mdx +++ b/snippets/ctoken-guides/close-intro.mdx @@ -1,5 +1,5 @@ -1. Closing a cToken account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. -2. cToken accounts can be closed +1. Closing a c-Token account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. +2. c-Token accounts can be closed * by the account owner at any time. * by the `compression_authority` - when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). + when the account becomes compressible. The account is compressed and closed - it can be reinstated with the same state (decompressed). diff --git a/snippets/ctoken-guides/ctoken-intro.mdx b/snippets/ctoken-guides/ctoken-intro.mdx index e67271b5..2c0af7c4 100644 --- a/snippets/ctoken-guides/ctoken-intro.mdx +++ b/snippets/ctoken-guides/ctoken-intro.mdx @@ -1,8 +1,8 @@ import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -1. cToken accounts are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. -2. cToken accounts are compressible with a default rent config. +1. c-Token accounts are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. +2. c-Token accounts are compressible with a default rent config. diff --git a/snippets/ctoken-guides/transfer-interface-intro.mdx b/snippets/ctoken-guides/transfer-interface-intro.mdx index e0624da6..063240ca 100644 --- a/snippets/ctoken-guides/transfer-interface-intro.mdx +++ b/snippets/ctoken-guides/transfer-interface-intro.mdx @@ -1,29 +1,29 @@
Account Creation Total: 22,208 lamports
- 11,208 lamports (24h rent)
+ 11,208 lamports (adds 24h rent)
11,000 lamports (compression incentive)
Account creatorTransaction payer
Top ups
(when rent < 2 epochs)
766 lamports (3h rent)Top ups
(when rent < 3h)
766 lamports
(adds 3h rent)
Transaction payer
Load AccountLoad Account
(when account inactive)
Total: 22,208 lamports
- 11,208 lamports (24h rent)
+ 11,208 lamports (adds 24h rent)
11,000 lamports (compression incentive)
Transaction payer
- + - + - + @@ -31,6 +31,6 @@
**cToken → cToken Account****c-Token → c-Token Account**
    -
  • Transfers cTokens between cToken accounts
  • +
  • Transfers c-Tokens between c-Token accounts
**SPL token → cToken Account****SPL token → c-Token Account**
    -
  • Transfers SPL tokens to cToken accounts
  • +
  • Transfers SPL tokens to c-Token accounts
  • SPL tokens are locked in interface PDA
  • -
  • cTokens are minted to cToken account
  • +
  • c-Tokens are minted to c-Token account
**cToken → SPL Account****c-Token → SPL Account**
  • Releases SPL tokens from interface PDA to SPL account
  • -
  • Burns cTokens in source cToken account
  • +
  • Burns c-Tokens in source c-Token account
-* For example, **SPL → cToken** can be used for transfers from Alice's SPL token account to her own cToken account. -* You can use this to **convert existing SPL tokens to cTokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the cToken program](/compressed-token-program/ctoken/README). +* For example, **SPL → c-Token** can be used for transfers from Alice's SPL token account to her own c-Token account. +* You can use this to **convert existing SPL tokens to c-Tokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the c-Token program](/compressed-token-program/ctoken/README). diff --git a/snippets/jsx/compressible-rent-calculator.jsx b/snippets/jsx/compressible-rent-calculator.jsx index fa408024..f05601c5 100644 --- a/snippets/jsx/compressible-rent-calculator.jsx +++ b/snippets/jsx/compressible-rent-calculator.jsx @@ -197,7 +197,7 @@ export const CompressibleRentCalculator = () => { {showFormula && (
-
Total cost for {DATA_LEN}-byte cToken account:
+
Total cost for {DATA_LEN}-byte c-Token account:
total_creation_cost = prepaid_rent + compression_incentive + tx_cost

rent_per_epoch = base_rent + (data_len × lamports_per_byte_per_epoch)
rent_per_epoch = {BASE_RENT} + ({DATA_LEN} × {LAMPORTS_PER_BYTE_PER_EPOCH}) = {rentPerEpoch} lamports
diff --git a/snippets/jsx/ctoken-vs-spl-calculator.jsx b/snippets/jsx/ctoken-vs-spl-calculator.jsx index ec7ac342..dac0f5c0 100644 --- a/snippets/jsx/ctoken-vs-spl-calculator.jsx +++ b/snippets/jsx/ctoken-vs-spl-calculator.jsx @@ -118,7 +118,7 @@ export const CTokenVsSplCalculator = () => {
-
cToken
+
c-Token
{formatSOL(ctokenCost)}
diff --git a/snippets/jsx/token22-extensions-table.jsx b/snippets/jsx/token22-extensions-table.jsx index 4002034d..0433410a 100644 --- a/snippets/jsx/token22-extensions-table.jsx +++ b/snippets/jsx/token22-extensions-table.jsx @@ -156,7 +156,7 @@ export const Token22ExtensionsTable = () => { Extension Name Description - cToken + c-Token Compressed Token diff --git a/snippets/overview-tables/ctoken-program-guides-table.mdx b/snippets/overview-tables/ctoken-program-guides-table.mdx index 5e560a8a..47bc4092 100644 --- a/snippets/overview-tables/ctoken-program-guides-table.mdx +++ b/snippets/overview-tables/ctoken-program-guides-table.mdx @@ -1,6 +1,6 @@ | | | | :---- | :---------- | -| [Create cToken Accounts](/compressed-token-program/ctoken/program-guides/program-create-ctoken) | Uses `CompressibleParamsInfos` and `CreateCTokenAccountInfos` | -| [Create Associated cToken Accounts](/compressed-token-program/ctoken/program-guides/program-create-cATA) | Uses `CompressibleParamsInfos` and `CreateAssociatedTokenAccountInfos` | -| [Transfer Interface](/compressed-token-program/ctoken/program-guides/program-transfer-interface) | For transfers between cToken and SPL token accounts.
Uses `TransferInterface` and `with_spl_interface` | -| [Close cToken Accounts](/compressed-token-program/ctoken/program-guides/program-close-ctoken) | Uses `CloseCTokenAccountInfos` | +| [Create c-Token Accounts](/compressed-token-program/ctoken/program-guides/program-create-ctoken) | Uses `CompressibleParamsInfos` and `CreateCTokenAccountInfos` | +| [Create Associated c-Token Accounts](/compressed-token-program/ctoken/program-guides/program-create-c-ATA) | Uses `CompressibleParamsInfos` and `CreateAssociatedTokenAccountInfos` | +| [Transfer Interface](/compressed-token-program/ctoken/program-guides/program-transfer-interface) | For transfers between c-Token and SPL token accounts.
Uses `TransferInterface` and `with_spl_interface` | +| [Close c-Token Accounts](/compressed-token-program/ctoken/program-guides/program-close-ctoken) | Uses `CloseCTokenAccountInfos` | From ff68493de6f79cad6271dfe54348ee2e4f828275 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 4 Dec 2025 22:31:15 +0000 Subject: [PATCH 059/143] Standardize cMint to c-Mint in overview.mdx --- compressed-token-program/overview.mdx | 40 +++++++++++++++------------ 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index 1e45f448..7c80a4db 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -22,6 +22,23 @@ import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-g | **Mint Account** | 1,461,600 lamports | ~25,000 lamports | | **Token Account** | 2,039,280 lamports | ~22,288 lamports | +## c-Mint Accounts + +1. c-Mints uniquely represent a token on Solana and store its global metadata. +2. Tokens created from c-Mints are c-Tokens. The mint's address serves as the token's unique identifier. +3. c-Mints are compressed accounts and rent-free. + +## c-Token Accounts + +1. c-Token & associated c-Token accounts (c-ATAs) are **Solana accounts** that
**hold balances** of **c-Mint, SPL mint, or Token 2022 mint** tokens. +2. Existing **SPL tokens can be converted** to c-Tokens and back on demand. +3. The compressible extension implements a **custom rent config**. + +#### Custom Rent Config + + + + Learn the core concepts to the [c-Token program here](/learn/c-token-program). @@ -50,32 +67,21 @@ Learn the core concepts to the [c-Token program here](/learn/c-token-program). ### Get Started -## c-Mint Accounts - -1. cMints uniquely represent a token on Solana and store its global metadata. -2. Tokens created from cMints are c-Tokens. The mint's address serves as the token's unique identifier. -3. cMints are compressed accounts and rent-free. - -## c-Token Accounts - -1. c-Token & associated c-Token accounts (c-ATAs) are **Solana accounts** that
**hold balances** of **cMint, SPL mint, or Token 2022 mint** tokens. -2. Existing **SPL tokens can be converted** to c-Tokens and back on demand. -3. The compressible extension implements a **custom rent config**. -#### Custom Rent Config +## Integrate c-Token - +ADD TABLE OF "FOR" -### Guides +## Cookbook -## Integrate c-Token +## Example Programs and Client -ADD TABLE OF "FOR" +ADD TABLE OF github repository -## Next Steps +# Next Steps Date: Thu, 4 Dec 2025 23:10:38 +0000 Subject: [PATCH 060/143] Refactor overview page layout and add rent duration column - Wrap c-Mint and c-Token sections in CardGroup for side-by-side display - Move Custom Rent Config section above Start building - Add "Time of Rent funded" column to compressible rent table - Remove redundant duration text from Amount column - Update compressible-rent-explained.mdx wording for clarity --- compressed-token-program/overview.mdx | 25 +++++++++++++----------- snippets/compressible-rent-explained.mdx | 12 ++++++++---- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index 7c80a4db..2fdca782 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -22,17 +22,20 @@ import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-g | **Mint Account** | 1,461,600 lamports | ~25,000 lamports | | **Token Account** | 2,039,280 lamports | ~22,288 lamports | -## c-Mint Accounts - -1. c-Mints uniquely represent a token on Solana and store its global metadata. -2. Tokens created from c-Mints are c-Tokens. The mint's address serves as the token's unique identifier. -3. c-Mints are compressed accounts and rent-free. - -## c-Token Accounts - -1. c-Token & associated c-Token accounts (c-ATAs) are **Solana accounts** that
**hold balances** of **c-Mint, SPL mint, or Token 2022 mint** tokens. -2. Existing **SPL tokens can be converted** to c-Tokens and back on demand. -3. The compressible extension implements a **custom rent config**. + + + ## c-Mint Accounts + * Uniquely represent a token and store its global metadata. + * Mint's address serves as the token's unique identifier. + * c-Mints are compressed accounts and rent-free. + + + ## c-Token Accounts + * Hold token balances of c-Mint, SPL mint, or Token 2022 mints. + * SPL tokens can be converted to
c-Tokens and back on demand. + * Custom rent config via compressible extension. +
+
#### Custom Rent Config diff --git a/snippets/compressible-rent-explained.mdx b/snippets/compressible-rent-explained.mdx index 96944aed..1d887e97 100644 --- a/snippets/compressible-rent-explained.mdx +++ b/snippets/compressible-rent-explained.mdx @@ -1,4 +1,4 @@ -1. The rent-exemption for the account creation is sponsored by Light Protocol. +1. The rent-exemption for c-Token account creation is sponsored by Light Protocol. 2. Compressible rent is paid per rent-epoch: 388 lamports for 1.5h,
paid by account creator and transaction payers to keep accounts "active". 3. "Inactive" accounts, where rent is below one epoch, are compressed
and the rent-exemption can be claimed by the rent sponsor. @@ -12,6 +12,7 @@ This way rent is only paid when accounts are used: Amount Payer + Time of Rent funded @@ -19,24 +20,27 @@ This way rent is only paid when accounts are used: Account Creation Total: 22,208 lamports
- 11,208 lamports (adds 24h rent)
+ 11,208 lamports
11,000 lamports (compression incentive)
Transaction payer + Funds 24h rent Top ups
(when rent < 3h) - 766 lamports
(adds 3h rent)
+ 766 lamports
Transaction payer + Funds 3h rent Load Account
(when account inactive) Total: 22,208 lamports
- 11,208 lamports (adds 24h rent)
+ 11,208 lamports
11,000 lamports (compression incentive)
Transaction payer + Funds 24h rent \ No newline at end of file From ec938712e140a17a1bc687ce54232e46611aa40b Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 4 Dec 2025 23:18:28 +0000 Subject: [PATCH 061/143] Improve rent explanation clarity and section heading - Change heading from "Custom Rent Config" to "Rent Config for c-Tokens" - Simplify rent payment explanation in compressible-rent-explained.mdx - Remove redundant wording about account creator and transaction payers --- compressed-token-program/overview.mdx | 2 +- snippets/compressible-rent-explained.mdx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index 2fdca782..a3837c1b 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -37,7 +37,7 @@ import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-g
-#### Custom Rent Config +#### Rent Config for c-Tokens diff --git a/snippets/compressible-rent-explained.mdx b/snippets/compressible-rent-explained.mdx index 1d887e97..145e23f8 100644 --- a/snippets/compressible-rent-explained.mdx +++ b/snippets/compressible-rent-explained.mdx @@ -1,6 +1,6 @@ 1. The rent-exemption for c-Token account creation is sponsored by Light Protocol. -2. Compressible rent is paid per rent-epoch: 388 lamports for 1.5h,
-paid by account creator and transaction payers to keep accounts "active". +2. Transaction payer's pay rent per rent-epoch (388 lamports for 1.5h)
+to keep accounts "active". 3. "Inactive" accounts, where rent is below one epoch, are compressed
and the rent-exemption can be claimed by the rent sponsor. 4. Transfers to inactive accounts "load" it with the same state (decompress). From b1c3883ce37592ecde44bdc3578e40fe636c47b2 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Thu, 4 Dec 2025 23:33:03 +0000 Subject: [PATCH 062/143] Add c-Mint guides table and standardize terminology - Create cmint-guides-table.mdx with create, mint, and update guides - Rename ctoken-program-guides-table.mdx to ctoken-guides-table.mdx - Update overview.mdx: add separate c-Mint and c-Token guide sections - Standardize cMint to c-Mint in create-cmint.mdx and mint-ctokens.mdx - Update docs.json: rename "cMint" group to "c-Mint" --- compressed-token-program/cmint/create-cmint.mdx | 16 ++++++++-------- compressed-token-program/cmint/mint-ctokens.mdx | 8 ++++---- compressed-token-program/overview.mdx | 14 ++++++++++---- docs.json | 2 +- snippets/overview-tables/cmint-guides-table.mdx | 5 +++++ ...-guides-table.mdx => ctoken-guides-table.mdx} | 0 6 files changed, 28 insertions(+), 17 deletions(-) create mode 100644 snippets/overview-tables/cmint-guides-table.mdx rename snippets/overview-tables/{ctoken-program-guides-table.mdx => ctoken-guides-table.mdx} (100%) diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index 4fb27f5a..2ee537f9 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -1,15 +1,15 @@ --- -title: Create cMint -description: Program and client guide to create a cMint with token metadata via CPI to the c-Token Program. Includes step-by-step implementation and full code examples. +title: Create c-Mint +description: Program and client guide to create a c-Mint with token metadata via CPI to the c-Token Program. Includes step-by-step implementation and full code examples. --- import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; -1. cMints uniquely represent a token on Solana and store its global metadata. -2. cMints are compressed accounts and rent-free. -3. Tokens created from cMints are c-Tokens. +1. c-Mints uniquely represent a token on Solana and store its global metadata. +2. c-Mints are compressed accounts and rent-free. +3. Tokens created from c-Mints are c-Tokens. Learn the [core concepts to the c-Token Program here](/compressed-token-program/c-token-program). @@ -77,7 +77,7 @@ let token_metadata = ExtensionInstructionData::TokenMetadata( ``` -**Fields must be set at cMint creation.** +**Fields must be set at c-Mint creation.** * Standard fields (`name`, `symbol`, `uri`) can be updated by `update_authority`. * For `additional_metadata`, only existing keys can be modified or removed. New keys cannot be added after creation. @@ -85,7 +85,7 @@ let token_metadata = ExtensionInstructionData::TokenMetadata( -### Configure cMint +### Configure c-Mint Set `decimals`, `mint_authority`, `freeze_authority`, and pass the `token_metadata` from the previous step. @@ -104,7 +104,7 @@ let cmint_params = CreateCMintParams { }; ``` -* The client passes a validity proof that proves the cMint address does not exist in the address tree where it will be stored. +* The client passes a validity proof that proves the c-Mint address does not exist in the address tree where it will be stored. * You can safely ignore `compression_address` and `address_merkle_tree_root_index`. The client passes these for proof verification. diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index f669c7e0..db5adfb9 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -1,6 +1,6 @@ --- title: Mint c-Tokens -description: Program guide to mint c-Tokens to a cMint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. +description: Program guide to mint c-Tokens to a c-Mint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. --- import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; @@ -9,7 +9,7 @@ import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-pre import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; -1. c-Tokens increase the supply of a cMint. +1. c-Tokens increase the supply of a c-Mint. 2. The destination c-Token accounts must exist to receive the minted c-Tokens. 3. Only the mint authority can mint new c-Tokens. @@ -55,7 +55,7 @@ Find [a full code example at the end](#full-code-example). ### Configure Mint Parameters Include your mint, the amount of tokens to be minted and the pubkey of the mint authority. -The client passes a validity proof that proves the cMint exists. +The client passes a validity proof that proves the c-Mint exists. ```rust use light_compressed_token_sdk::ctoken::MintToCTokenParams; @@ -75,7 +75,7 @@ let mint_params = MintToCTokenParams::new( ### System Accounts -Compressed accounts like cMints require system accounts like the Light System Program account for interactions and proof verification. +Compressed accounts like c-Mints require system accounts like the Light System Program account for interactions and proof verification. The client includes them in the instruction. diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index a3837c1b..907dc576 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -12,7 +12,8 @@ import { CTokenVsSplCalculator } from '/snippets/jsx/ctoken-vs-spl-calculator.js import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-guides-table.mdx'; +import CTokenGuidesTable from '/snippets/overview-tables/ctoken-guides-table.mdx'; +import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx'; 1. c-Mints and c-Tokens are equivalents to SPL mints and tokens. 2. The key difference is c-Mints and c-Tokens do not require rent-exemption upon creation. @@ -75,12 +76,17 @@ Learn the core concepts to the [c-Token program here](/learn/c-token-program). ADD TABLE OF "FOR" -## Cookbook +## Cookbook for Program and Clients - +### c-Mint Guides + -## Example Programs and Client +### c-Token Guides + + + +## Examples for Program and Client ADD TABLE OF github repository diff --git a/docs.json b/docs.json index 5ed83800..2cfe6999 100644 --- a/docs.json +++ b/docs.json @@ -32,7 +32,7 @@ "group": "Cookbook", "pages": [ { - "group": "cMint", + "group": "c-Mint", "expanded": true, "pages": [ "compressed-token-program/cmint/create-cmint", diff --git a/snippets/overview-tables/cmint-guides-table.mdx b/snippets/overview-tables/cmint-guides-table.mdx new file mode 100644 index 00000000..9260805d --- /dev/null +++ b/snippets/overview-tables/cmint-guides-table.mdx @@ -0,0 +1,5 @@ +| | | +| :---- | :---------- | +| [Create c-Mint](/compressed-token-program/cmint/create-cmint) | Program and client guide to create c-Mints with token metadata | +| [Mint c-Tokens](/compressed-token-program/cmint/mint-ctokens) | Mint c-Tokens to c-Mint accounts | +| [Update Metadata](/compressed-token-program/cmint/update-metadata) | Update token metadata fields and authority | diff --git a/snippets/overview-tables/ctoken-program-guides-table.mdx b/snippets/overview-tables/ctoken-guides-table.mdx similarity index 100% rename from snippets/overview-tables/ctoken-program-guides-table.mdx rename to snippets/overview-tables/ctoken-guides-table.mdx From e625da75548a3cb873546dde6ce2784702bb268c Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 00:04:01 +0000 Subject: [PATCH 063/143] Consolidate c-Token guides and move intro pages to root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Consolidate c-Mint and c-Token guides into unified cookbook structure - Move landing.mdx, quickstart.mdx, support.mdx from intro-pages/ to root - Add Rust client example to create-cmint.mdx - Update docs.json navigation to reflect new structure - Remove redundant client-guides and program-guides subdirectories 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../client-guides/client-create-cmint.mdx | 91 ---- .../client-guides/client-mint-to-ctoken.mdx | 127 ----- .../cmint/client-guides/cmint.mdx | 158 ------ .../cmint/client-guides/update-metadata.mdx | 89 ---- .../cmint/create-cmint.mdx | 100 +++- .../program-guides/program-create-cmint.mdx | 476 ------------------ .../program-guides/program-mint-to-cToken.mdx | 309 ------------ .../client-guides/client-close-ctoken.mdx | 82 --- .../client-guides/client-create-cATA.mdx | 92 ---- .../client-guides/client-create-ctoken.mdx | 198 -------- .../client-transfer-interface.mdx | 93 ---- .../program-guides/program-close-ctoken.mdx | 174 ------- .../program-guides/program-create-cATA.mdx | 221 -------- .../program-guides/program-create-ctoken.mdx | 205 -------- .../program-transfer-interface.mdx | 250 --------- compressed-token-program/overview.mdx | 6 +- docs.json | 11 +- landing.mdx | 1 - quickstart.mdx | 1 - 19 files changed, 103 insertions(+), 2581 deletions(-) delete mode 100644 compressed-token-program/cmint/client-guides/client-create-cmint.mdx delete mode 100644 compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx delete mode 100644 compressed-token-program/cmint/client-guides/cmint.mdx delete mode 100644 compressed-token-program/cmint/client-guides/update-metadata.mdx delete mode 100644 compressed-token-program/cmint/program-guides/program-create-cmint.mdx delete mode 100644 compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx delete mode 100644 compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx delete mode 100644 compressed-token-program/ctoken/client-guides/client-create-cATA.mdx delete mode 100644 compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx delete mode 100644 compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx delete mode 100644 compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx delete mode 100644 compressed-token-program/ctoken/program-guides/program-create-cATA.mdx delete mode 100644 compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx delete mode 100644 compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx diff --git a/compressed-token-program/cmint/client-guides/client-create-cmint.mdx b/compressed-token-program/cmint/client-guides/client-create-cmint.mdx deleted file mode 100644 index b3b812b7..00000000 --- a/compressed-token-program/cmint/client-guides/client-create-cmint.mdx +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Create cMints -description: Create a cMint using the c-Token SDK. ---- - - - - -```rust create_cmint.rs -use light_compressed_token_sdk::ctoken::{CreateCMint, CreateCMintParams}; -use light_client::{indexer::Indexer, rpc::Rpc}; -use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; - -async fn create_compressed_mint( - rpc: &mut (impl Rpc + Indexer), - payer: &Keypair, - mint_authority: Pubkey, - decimals: u8, -) -> (Pubkey, [u8; 32]) { - let mint_signer = Keypair::new(); - let address_tree = rpc.get_address_tree_v2(); - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Derive compression address - let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( - &mint_signer.pubkey(), - &address_tree.tree, - ); - - // Derive mint PDA - let mint_pda = - light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; - - // Get validity proof for the address - let rpc_result = rpc - .get_validity_proof( - vec![], - vec![light_client::indexer::AddressWithTree { - address: compression_address, - tree: address_tree.tree, - }], - None, - ) - .await - .unwrap() - .value; - - // Build CreateCMint params - let params = CreateCMintParams { - decimals, - address_merkle_tree_root_index: rpc_result.addresses[0].root_index, - mint_authority, - proof: rpc_result.proof.0.unwrap(), - compression_address, - mint: mint_pda, - freeze_authority: None, - extensions: None, - }; - - // Create instruction - let create_cmint = CreateCMint::new( - params, - mint_signer.pubkey(), - payer.pubkey(), - address_tree.tree, - output_queue, - ); - let instruction = create_cmint.instruction().unwrap(); - - // Send transaction - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) - .await - .unwrap(); - - println!("Compressed mint created!"); - println!("Mint PDA: {}", mint_pda); - println!("Compression address: {:?}", compression_address); - - (mint_pda, compression_address) -} -``` - - - - -```typescript create_cmint.ts -// TODO: Add TypeScript example -``` - - - diff --git a/compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx b/compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx deleted file mode 100644 index 6d500de9..00000000 --- a/compressed-token-program/cmint/client-guides/client-mint-to-ctoken.mdx +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Mint c-Tokens -description: Client guide to mint compressed tokens to c-Token accounts with step-by-step implementation and full code examples. ---- - - - - -```rust mint_to_ctoken.rs -use borsh::BorshDeserialize; -use light_compressed_token_sdk::ctoken::{MintToCToken, MintToCTokenParams}; -use light_ctoken_types::{ - instructions::mint_action::CompressedMintWithContext, - state::CompressedMint, -}; -use light_client::{indexer::Indexer, rpc::Rpc}; -use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; - -async fn mint_to_ctoken( - rpc: &mut (impl Rpc + Indexer), - payer: &Keypair, - mint_authority: Pubkey, - compression_address: [u8; 32], - ata_pubkeys: Vec, - amounts: Vec, -) { - // Get the compressed mint account - let compressed_mint_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - // Deserialize the compressed mint - let compressed_mint = - CompressedMint::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) - .unwrap(); - - // Get validity proof for the mint operation - let rpc_result = rpc - .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) - .await - .unwrap() - .value; - - // Build CompressedMintWithContext - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_mint_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: compressed_mint.try_into().unwrap(), - }; - - // Build mint params with first recipient - let mut mint_params = MintToCTokenParams::new( - compressed_mint_with_context, - amounts[0], - mint_authority, - rpc_result.proof, - ); - mint_params.mint_to_actions[0].account_index = 0; - - // Add remaining recipients - for (idx, amount) in amounts.iter().enumerate().skip(1) { - mint_params = mint_params.add_mint_to_action(idx as u8, *amount); - } - - // Build MintToCToken instruction - let mint_to_ctoken = MintToCToken::new( - mint_params, - payer.pubkey(), - compressed_mint_account.tree_info.tree, - compressed_mint_account.tree_info.queue, - compressed_mint_account.tree_info.queue, - ata_pubkeys.clone(), - ); - let instruction = mint_to_ctoken.instruction().unwrap(); - - // Send transaction - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer]) - .await - .unwrap(); - - println!("Tokens minted to c-Token ATAs!"); - for (ata, amount) in ata_pubkeys.iter().zip(amounts.iter()) { - println!(" {} -> {} tokens", ata, amount); - } -} -``` - - - - -```typescript mint_to_ctoken.ts -// TODO: Add TypeScript example -``` - - - - -# Next Steps - - - - Mint c-Tokens - - - Transfer Interface - - diff --git a/compressed-token-program/cmint/client-guides/cmint.mdx b/compressed-token-program/cmint/client-guides/cmint.mdx deleted file mode 100644 index b78231f6..00000000 --- a/compressed-token-program/cmint/client-guides/cmint.mdx +++ /dev/null @@ -1,158 +0,0 @@ ---- -title: Create cMint -description: Guide to create a cMint with Token Metadata, a rent-free mint account to store global metadata of tokens. ---- - -import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; - - - - - - - - - - - - - - - - - - - - - -
TypeKey Features
**[cMint](#cmint-accounts)**Compressed account -
    -
  • **Rent-free mint accounts** (similar to SPL mints)
  • -
-
**Token Metadata**Extension - * Stores information like name, symbol, URI, and custom fields. - * Must be added at creation of cMint -
- - -Learn the core concepts to cMints [here](/compressed-token-program/c-token-program#cmint-accounts). - -## Get Started - - - - - - -### Setup - - - - - - - - - - -### Create cMint with Token Metadata - -*test*! - - -```typescript Typescript -import { createRpc, confirmTx } from '@lightprotocol/stateless.js'; -import { createMint, createTokenMetadata } from '@lightprotocol/compressed-token'; -import { Keypair } from '@solana/web3.js'; - -const RPC_ENDPOINT = 'https://devnet.helius-rpc.com/?api-key='; -const COMPRESSION_ENDPOINT = RPC_ENDPOINT; - -async function main() { - const rpc = createRpc(RPC_ENDPOINT, COMPRESSION_ENDPOINT); - const payer = Keypair.generate(); - const mintAuthority = payer; - const decimals = 9; - - // Fund payer (devnet only) - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 1e9); - await confirmTx(rpc, airdropSig); - - // Create cMint with token metadata - const { transactionSignature, mint } = await createMint( - rpc, - payer, - mintAuthority, - null, // freezeAuthority - decimals, - Keypair.generate(), - createTokenMetadata( - 'My Token', - 'MTK', - 'ipfs://QmYwAPJzv5CZsnAzt8auVTL7oU4vNnB8L2xZ5C4X5PqW3R/metadata.json', - ), - ); - - console.log('cMint created:', mint.toBase58()); -} - -main().catch(console.error); -``` - -```rust Rust -use light_compressed_token::mint_sdk::create_mint; - -// Generate mint seed -let mint_seed = Keypair::new(); - -// Prepare metadata -let metadata = Some(TokenMetadataInstructionData { - name: b"My Compressed Token".to_vec(), - symbol: b"MCT".to_vec(), - uri: b"https://example.com/token-metadata.json".to_vec(), - update_authority: mint_authority.pubkey(), - additional_metadata: vec![ - AdditionalMetadata { - key: b"description".to_vec(), - value: b"A sample compressed token".to_vec(), - } - ], -}); - -// Create the cMint with metadata -create_mint( - &rpc, - &mint_seed, - 9, // decimals - &mint_authority, - None, // freeze_authority - metadata, - &payer, -).await?; -``` - - - - - - - - -Coming soon. - - - -**Metadata fields must be set at cMint creation.** -* Standard fields can be updated freely -* For `additional_metadata`, only existing keys can be modified or removed. - -## Next Steps - - - \ No newline at end of file diff --git a/compressed-token-program/cmint/client-guides/update-metadata.mdx b/compressed-token-program/cmint/client-guides/update-metadata.mdx deleted file mode 100644 index c048e1c1..00000000 --- a/compressed-token-program/cmint/client-guides/update-metadata.mdx +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: Update Token Metadata of cMints -sidebarTitle: Update Token Metadata -description: Overview how to update token metadata of cMints. ---- - -Once set, metadata can be updated with `MintAction`: - -1. [`UpdateMetadataField`](#update-metadata-fields) updates specific fields. -2. [`UpdateMetadataAuthority`](#update-metadata-authority) updates the authority that can modify the extension. -3. [`RemoveMetadataKey`](#remove-metadata-keys) removes a key-value pair from `additional_metadata`. - - -Find the [source code for these actions here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/mint_action/actions/update_metadata.rs). - - - - - -### Update Metadata Fields - -Update standard fields (name, symbol, URI) or existing keys in `additional_metadata`: - -```rust -use light_ctoken_types::instructions::mint_action::UpdateMetadataFieldAction; - -// Update token name -let action = UpdateMetadataFieldAction { - extension_index: 0, // Index of TokenMetadata extension - field_type: 0, // 0 = Name - key: vec![], // Empty for standard fields - value: b"New Token Name".to_vec(), -}; -``` - - -* For `additional_metadata`, only existing keys can be modified or removed. -* New keys cannot be added after cMint creation. Only keys set during creation can be updated. - - - - - -### Update Metadata Authority - -```rust -use light_ctoken_types::instructions::mint_action::UpdateMetadataAuthorityAction; - -let action = UpdateMetadataAuthorityAction { - extension_index: 0, - new_authority: new_authority_pubkey, -}; -``` - - -To revoke update authority, set `new_authority` to a zero-byte pubkey (`Pubkey::default()`). - - - - - -### Remove Metadata Keys - -Removes a key from `additional_metadata`: - -```rust -use light_ctoken_types::instructions::mint_action::RemoveMetadataKeyAction; - -let action = RemoveMetadataKeyAction { - extension_index: 0, - key: b"category".to_vec(), - idempotent: 0, // 0 = error if key doesn't exist -}; -``` - - - - - -## Next Steps - - - \ No newline at end of file diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index 2ee537f9..6256f209 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -36,7 +36,105 @@ Learn the [core concepts to the c-Token Program here](/compressed-token-program/
-### Close c-Token Account +### Create c-Mint with Token Metadata + +```rust// Test for: client-create-cmint.mdx + +use light_client::indexer::{AddressWithTree, Indexer}; +use light_client::rpc::Rpc; +use light_compressed_token_sdk::ctoken::{CreateCMint, CreateCMintParams}; +use light_ctoken_types::instructions::extensions::token_metadata::TokenMetadataInstructionData; +use light_ctoken_types::instructions::extensions::ExtensionInstructionData; +use light_ctoken_types::state::AdditionalMetadata; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; + + +#[tokio::test(flavor = "multi_thread")] +async fn test_create_rent_free_mint_with_metadata() { + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + + // Create c-int with metadata + let (_mint, _compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; + +} +pub async fn create_compressed_mint( + rpc: &mut R, + payer: &Keypair, + decimals: u8, +) -> (Pubkey, [u8; 32]) { + let mint_signer = Keypair::new(); + let address_tree = rpc.get_address_tree_v2(); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; + + // Derive address + let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + &mint_signer.pubkey(), + &address_tree.tree, + ); + + let mint_pda = + light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + + // Get validity proof for the address + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![AddressWithTree { + address: compression_address, + tree: address_tree.tree, + }], + None, + ) + .await + .unwrap() + .value; + + // Build params with token metadata + let params = CreateCMintParams { + decimals, + address_merkle_tree_root_index: rpc_result.addresses[0].root_index, + mint_authority: payer.pubkey(), + proof: rpc_result.proof.0.unwrap(), + compression_address, + mint: mint_pda, + freeze_authority: None, + extensions: Some(vec![ExtensionInstructionData::TokenMetadata( + TokenMetadataInstructionData { + update_authority: Some(payer.pubkey().to_bytes().into()), + name: b"Rent Free Token".to_vec(), + symbol: b"RFT".to_vec(), + uri: b"https://example.com/metadata.json".to_vec(), + additional_metadata: Some(vec![AdditionalMetadata { + key: b"type".to_vec(), + value: b"compressed".to_vec(), + }]), + }, + )]), + }; + + // Create instruction + let create_cmint = CreateCMint::new( + params, + mint_signer.pubkey(), + payer.pubkey(), + address_tree.tree, + output_queue, + ); + let instruction = create_cmint.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) + .await + .unwrap(); + + (mint_pda, compression_address) +} +``` diff --git a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx b/compressed-token-program/cmint/program-guides/program-create-cmint.mdx deleted file mode 100644 index 251f62bb..00000000 --- a/compressed-token-program/cmint/program-guides/program-create-cmint.mdx +++ /dev/null @@ -1,476 +0,0 @@ ---- -title: Create cMint -description: Program guide to create a cMint with token metadata via CPI to the c-Token Program. Includes step-by-step implementation and full code examples. ---- - -import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; - -1. cMints uniquely represent a token on Solana and store its global metadata. -2. cMints are compressed accounts and rent-free. -3. Tokens created from cMints are c-Tokens. - - -Learn the [core concepts to the c-Token Program here](/compressed-token-program/c-token-program). - - - -Find [full code examples at the end](#full-code-example). - - -# Implementation Guide - - - - -### Configure Token Metadata - -```rust -use light_ctoken_types::{ - instructions::extensions::{ - token_metadata::TokenMetadataInstructionData, - ExtensionInstructionData, - }, - state::AdditionalMetadata, -}; - -let token_metadata = ExtensionInstructionData::TokenMetadata( - TokenMetadataInstructionData { - update_authority: Some(authority.to_bytes().into()), - name: b"My Token".to_vec(), - symbol: b"MTK".to_vec(), - uri: b"https://example.com/metadata.json".to_vec(), - additional_metadata: Some(vec![ - AdditionalMetadata { - key: b"category".to_vec(), - value: b"utility".to_vec(), - }, - ]), - }, -); -``` - - -**Fields must be set at cMint creation.** -* Standard fields (`name`, `symbol`, `uri`) can be updated by `update_authority`. -* For `additional_metadata`, only existing keys can be modified or removed. New keys cannot be added after creation. - - - - - -### Configure cMint - -Set `decimals`, `mint_authority`, `freeze_authority`, and pass the `token_metadata` from the previous step. - -```rust -use light_compressed_token_sdk::ctoken::CreateCMintParams; - -let cmint_params = CreateCMintParams { - decimals: data.decimals, - address_merkle_tree_root_index: data.address_merkle_tree_root_index, - mint_authority: data.mint_authority, - proof: data.proof, - compression_address: data.compression_address, - mint: data.mint, - freeze_authority: data.freeze_authority, - extensions: data.token_metadata, -}; -``` - -* The client passes a validity proof that proves the cMint address does not exist in the address tree where it will be stored. -* You can safely ignore `compression_address` and `address_merkle_tree_root_index`. The client passes these for proof verification. - - - - - - -### System Accounts - -Include system accounts such as the Light System Program required to interact with compressed state. -The client includes them in the instruction. - - - - - -```rust -use light_compressed_token_sdk::ctoken::SystemAccountInfos; - -let system_accounts = SystemAccountInfos { - light_system_program: light_system_program.clone(), - cpi_authority_pda: cpi_authority_pda.clone(), - registered_program_pda: registered_program_pda.clone(), - account_compression_authority: account_compression_authority.clone(), - account_compression_program: account_compression_program.clone(), - system_program: system_program.clone(), -}; -``` - - - - -### Build Account Infos and CPI the c-Token Program - -1. Pass the required accounts -2. Include `cmint_params` and `system_accounts` from the previous steps -3. Use `invoke` or `invoke_signed`: - * When `mint_signer` is an external keypair, use `invoke`. - * When `mint_signer` is a PDA, use `invoke_signed` with its seeds. - * When both `mint_signer` and `authority` are PDAs, use `invoke_signed` with both seeds. - - - - - -```rust -use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; - -CreateCMintAccountInfos { - mint_signer: mint_signer.clone(), - authority: authority.clone(), - payer: payer.clone(), - address_tree: address_tree.clone(), - output_queue: output_queue.clone(), - system_accounts, - cpi_context: None, - cpi_context_account: None, - cmint_params, -} -.invoke()?; -``` - - - - -```rust -use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; - -let account_infos = CreateCMintAccountInfos { - mint_signer: mint_signer.clone(), - authority: authority.clone(), - payer: payer.clone(), - address_tree: address_tree.clone(), - output_queue: output_queue.clone(), - system_accounts, - cpi_context: None, - cpi_context_account: None, - cmint_params, -}; - -let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]]; -account_infos.invoke_signed(&[signer_seeds])?; -``` - - - - -```rust -use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; - -let account_infos = CreateCMintAccountInfos { - mint_signer: mint_signer.clone(), - authority: authority.clone(), - payer: payer.clone(), - address_tree: address_tree.clone(), - output_queue: output_queue.clone(), - system_accounts, - cpi_context: None, - cpi_context_account: None, - cmint_params, -}; - -let mint_signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_signer_bump]]; -let authority_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[authority_bump]]; -account_infos.invoke_signed(&[mint_signer_seeds, authority_seeds])?; -``` - - - - - - - - - -# Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_cmint.rs). - - -```rust expandable -use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::{ - ctoken::{ - CreateCMintAccountInfos, CreateCMintParams, ExtensionInstructionData, SystemAccountInfos, - }, - CompressedProof, -}; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::ID; - -/// PDA seed for mint signer in invoke_signed variant -pub const MINT_SIGNER_SEED: &[u8] = b"mint_signer"; - -/// Instruction data for create compressed mint -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateCmintData { - pub decimals: u8, - pub address_merkle_tree_root_index: u16, - pub mint_authority: Pubkey, - pub proof: CompressedProof, - pub compression_address: [u8; 32], - pub mint: Pubkey, - pub freeze_authority: Option, - pub extensions: Option>, -} - -/// Handler for creating a compressed mint (invoke) -/// -/// Uses the CreateCMintAccountInfos builder pattern. This demonstrates how to: -/// 1. Build the CreateCMintParams struct from instruction data -/// 2. Build the CreateCMintAccountInfos with accounts -/// 3. Call invoke() which handles instruction building and CPI -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: light_system_program -/// - accounts[2]: mint_signer (signer) -/// - accounts[3]: payer (signer, also authority) -/// - accounts[4]: payer again (fee_payer in SDK) -/// - accounts[5]: cpi_authority_pda -/// - accounts[6]: registered_program_pda -/// - accounts[7]: account_compression_authority -/// - accounts[8]: account_compression_program -/// - accounts[9]: system_program -/// - accounts[10]: output_queue -/// - accounts[11]: address_tree -/// - accounts[12] (optional): cpi_context_account -pub fn process_create_cmint( - accounts: &[AccountInfo], - data: CreateCmintData, -) -> Result<(), ProgramError> { - if accounts.len() < 12 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Build the params - let params = CreateCMintParams { - decimals: data.decimals, - address_merkle_tree_root_index: data.address_merkle_tree_root_index, - mint_authority: data.mint_authority, - proof: data.proof, - compression_address: data.compression_address, - mint: data.mint, - freeze_authority: data.freeze_authority, - extensions: data.extensions, - }; - - // Build system accounts struct - let system_accounts = SystemAccountInfos { - light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[5].clone(), - registered_program_pda: accounts[6].clone(), - account_compression_authority: accounts[7].clone(), - account_compression_program: accounts[8].clone(), - system_program: accounts[9].clone(), - }; - - // Build the account infos struct - // In this case, payer == authority (accounts[3]) - CreateCMintAccountInfos { - mint_signer: accounts[2].clone(), - authority: accounts[3].clone(), - payer: accounts[3].clone(), - address_tree: accounts[11].clone(), - output_queue: accounts[10].clone(), - system_accounts, - cpi_context: None, - cpi_context_account: None, - params, - } - .invoke()?; - - Ok(()) -} - -/// Handler for creating a compressed mint with PDA mint signer (invoke_signed) -/// -/// Uses the CreateCMintAccountInfos builder pattern with invoke_signed. -/// The mint_signer is a PDA derived from this program. -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: light_system_program -/// - accounts[2]: mint_signer (PDA, not signer - program signs) -/// - accounts[3]: payer (signer, also authority) -/// - accounts[4]: payer again (fee_payer in SDK) -/// - accounts[5]: cpi_authority_pda -/// - accounts[6]: registered_program_pda -/// - accounts[7]: account_compression_authority -/// - accounts[8]: account_compression_program -/// - accounts[9]: system_program -/// - accounts[10]: output_queue -/// - accounts[11]: address_tree -/// - accounts[12] (optional): cpi_context_account -pub fn process_create_cmint_invoke_signed( - accounts: &[AccountInfo], - data: CreateCmintData, -) -> Result<(), ProgramError> { - if accounts.len() < 12 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive the PDA for the mint signer - let (pda, bump) = Pubkey::find_program_address(&[MINT_SIGNER_SEED], &ID); - - // Verify the mint_signer account is the PDA we expect - if &pda != accounts[2].key { - return Err(ProgramError::InvalidSeeds); - } - - // Build the params - let params = CreateCMintParams { - decimals: data.decimals, - address_merkle_tree_root_index: data.address_merkle_tree_root_index, - mint_authority: data.mint_authority, - proof: data.proof, - compression_address: data.compression_address, - mint: data.mint, - freeze_authority: data.freeze_authority, - extensions: data.extensions, - }; - - // Build system accounts struct - let system_accounts = SystemAccountInfos { - light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[5].clone(), - registered_program_pda: accounts[6].clone(), - account_compression_authority: accounts[7].clone(), - account_compression_program: accounts[8].clone(), - system_program: accounts[9].clone(), - }; - - // Build the account infos struct - // In this case, payer == authority (accounts[3]) - let account_infos = CreateCMintAccountInfos { - mint_signer: accounts[2].clone(), - authority: accounts[3].clone(), - payer: accounts[3].clone(), - address_tree: accounts[11].clone(), - output_queue: accounts[10].clone(), - system_accounts, - cpi_context: None, - cpi_context_account: None, - params, - }; - - // Invoke with PDA signing - let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]]; - account_infos.invoke_signed(&[signer_seeds])?; - - Ok(()) -} - -/// Handler for creating a compressed mint with PDA mint signer AND PDA authority (invoke_signed) -/// -/// Uses the SDK's CreateCMintAccountInfos with separate authority and payer accounts. -/// Both mint_signer and authority are PDAs signed by this program. -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: light_system_program -/// - accounts[2]: mint_signer (PDA from MINT_SIGNER_SEED, not signer - program signs) -/// - accounts[3]: authority (PDA from MINT_AUTHORITY_SEED, not signer - program signs) -/// - accounts[4]: fee_payer (signer) -/// - accounts[5]: cpi_authority_pda -/// - accounts[6]: registered_program_pda -/// - accounts[7]: account_compression_authority -/// - accounts[8]: account_compression_program -/// - accounts[9]: system_program -/// - accounts[10]: output_queue -/// - accounts[11]: address_tree -/// - accounts[12] (optional): cpi_context_account -pub fn process_create_cmint_with_pda_authority( - accounts: &[AccountInfo], - data: CreateCmintData, -) -> Result<(), ProgramError> { - use crate::mint_to_ctoken::MINT_AUTHORITY_SEED; - - if accounts.len() < 12 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive the PDA for the mint signer - let (mint_signer_pda, mint_signer_bump) = - Pubkey::find_program_address(&[MINT_SIGNER_SEED], &ID); - - // Derive the PDA for the authority - let (authority_pda, authority_bump) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); - - // Verify the mint_signer account is the PDA we expect - if &mint_signer_pda != accounts[2].key { - return Err(ProgramError::InvalidSeeds); - } - - // Verify the authority account is the PDA we expect - if &authority_pda != accounts[3].key { - return Err(ProgramError::InvalidSeeds); - } - - // Build the params - authority is the PDA - let params = CreateCMintParams { - decimals: data.decimals, - address_merkle_tree_root_index: data.address_merkle_tree_root_index, - mint_authority: authority_pda, // Use the derived PDA as authority - proof: data.proof, - compression_address: data.compression_address, - mint: data.mint, - freeze_authority: data.freeze_authority, - extensions: data.extensions, - }; - - // Build system accounts struct - let system_accounts = SystemAccountInfos { - light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[5].clone(), - registered_program_pda: accounts[6].clone(), - account_compression_authority: accounts[7].clone(), - account_compression_program: accounts[8].clone(), - system_program: accounts[9].clone(), - }; - - // Build the account infos struct using SDK - let account_infos = CreateCMintAccountInfos { - mint_signer: accounts[2].clone(), - authority: accounts[3].clone(), - payer: accounts[4].clone(), - address_tree: accounts[11].clone(), - output_queue: accounts[10].clone(), - system_accounts, - cpi_context: None, - cpi_context_account: None, - params, - }; - - // Invoke with both PDAs signing - let mint_signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_signer_bump]]; - let authority_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[authority_bump]]; - account_infos.invoke_signed(&[mint_signer_seeds, authority_seeds])?; - - Ok(()) -} -``` - -# Next Steps - - - diff --git a/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx b/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx deleted file mode 100644 index b62e31ee..00000000 --- a/compressed-token-program/cmint/program-guides/program-mint-to-cToken.mdx +++ /dev/null @@ -1,309 +0,0 @@ ---- -title: Mint c-Tokens -description: Program guide to mint c-Tokens to a cMint via CPI to the Compressed Token Program with step-by-step implementation and full code examples. ---- - -import CMintSystemAccountsList from '/snippets/accounts-list/cmint-system-accounts-list.mdx'; -import CMintToCTokenAccountsList from '/snippets/accounts-list/cmint-to-ctoken-accounts-list.mdx'; - -1. c-Tokens increase the supply of a cMint. -2. The destination c-Token accounts must exist to receive the minted c-Tokens. -3. Only the mint authority can mint new c-Tokens. - - -Find [full code examples at the end](#full-code-example). - - -# Implementation Guide - - - - -### Configure Mint Parameters -Include your mint, the amount of tokens to be minted and the pubkey of the mint authority. -The client passes a validity proof that proves the cMint exists. - -```rust -use light_compressed_token_sdk::ctoken::MintToCTokenParams; - -let mint_params = MintToCTokenParams::new( - data.compressed_mint_inputs, - data.amount, - data.mint_authority, - data.proof, -); -``` - - - - - - -### System Accounts - -Compressed accounts like cMints require system accounts like the Light System Program account for interactions and proof verification. -The client includes them in the instruction. - - - - - -```rust -use light_compressed_token_sdk::ctoken::SystemAccountInfos; - -let system_accounts = SystemAccountInfos { - light_system_program: light_system_program.clone(), - cpi_authority_pda: cpi_authority_pda.clone(), - registered_program_pda: registered_program_pda.clone(), - account_compression_authority: account_compression_authority.clone(), - account_compression_program: account_compression_program.clone(), - system_program: system_program.clone(), -}; -``` - - - - - -### Build Account Infos and CPI the c-Token Program - -1. Pass the required accounts, including the destination c-Token accounts. -2. Include `mint_params` and `system_accounts` from the previous steps -3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. - - - -```rust -use light_compressed_token_sdk::ctoken::MintToCTokenInfos; - -MintToCTokenInfos { - authority: authority.clone(), - payer: payer.clone(), - state_tree: state_tree.clone(), - input_queue: input_queue.clone(), - output_queue: output_queue.clone(), - ctoken_accounts, - system_accounts, - cpi_context: None, - cpi_context_account: None, - mint_params, -} -.invoke()?; -``` - - - - -```rust -use light_compressed_token_sdk::ctoken::MintToCTokenInfos; - -let account_infos = MintToCTokenInfos { - authority: authority.clone(), - payer: payer.clone(), - state_tree: state_tree.clone(), - input_queue: input_queue.clone(), - output_queue: output_queue.clone(), - ctoken_accounts, - system_accounts, - cpi_context: None, - cpi_context_account: None, - mint_params, -}; - -let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; -account_infos.invoke_signed(&[signer_seeds])?; -``` - - - - - - - - - - - -# Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/mint_to_ctoken.rs). - - -```rust expandable -use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::{ - MintToCTokenInfos, MintToCTokenParams, SystemAccountInfos, -}; -use light_ctoken_types::instructions::mint_action::CompressedMintWithContext; -use light_sdk::instruction::ValidityProof; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::ID; - -/// PDA seed for mint authority in invoke_signed variant -pub const MINT_AUTHORITY_SEED: &[u8] = b"mint_authority"; - -/// Instruction data for mint_to_ctoken operations -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct MintToCTokenData { - pub compressed_mint_inputs: CompressedMintWithContext, - pub amount: u64, - pub mint_authority: Pubkey, - pub proof: ValidityProof, -} - -/// Handler for minting tokens to compressed token accounts (invoke) -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: light_system_program -/// - accounts[2]: authority (mint_authority) -/// - accounts[3]: fee_payer -/// - accounts[4]: cpi_authority_pda -/// - accounts[5]: registered_program_pda -/// - accounts[6]: account_compression_authority -/// - accounts[7]: account_compression_program -/// - accounts[8]: system_program -/// - accounts[9]: output_queue -/// - accounts[10]: state_tree -/// - accounts[11]: input_queue -/// - accounts[12..]: ctoken_accounts (destination accounts) -pub fn process_mint_to_ctoken( - accounts: &[AccountInfo], - data: MintToCTokenData, -) -> Result<(), ProgramError> { - if accounts.len() < 13 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Build params using the constructor - let params = MintToCTokenParams::new( - data.compressed_mint_inputs, - data.amount, - data.mint_authority, - data.proof, - ); - - // Build system accounts struct - let system_accounts = SystemAccountInfos { - light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[4].clone(), - registered_program_pda: accounts[5].clone(), - account_compression_authority: accounts[6].clone(), - account_compression_program: accounts[7].clone(), - system_program: accounts[8].clone(), - }; - - // Collect ctoken accounts from remaining accounts - let ctoken_accounts: Vec = accounts[12..].to_vec(); - - MintToCTokenInfos { - authority: accounts[2].clone(), - payer: accounts[3].clone(), - state_tree: accounts[10].clone(), - input_queue: accounts[11].clone(), - output_queue: accounts[9].clone(), - ctoken_accounts, - system_accounts, - cpi_context: None, - cpi_context_account: None, - params, - } - .invoke()?; - - Ok(()) -} - -/// Handler for minting tokens with PDA mint authority (invoke_signed) -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: light_system_program -/// - accounts[2]: authority (PDA mint_authority) -/// - accounts[3]: fee_payer -/// - accounts[4]: cpi_authority_pda -/// - accounts[5]: registered_program_pda -/// - accounts[6]: account_compression_authority -/// - accounts[7]: account_compression_program -/// - accounts[8]: system_program -/// - accounts[9]: output_queue -/// - accounts[10]: state_tree -/// - accounts[11]: input_queue -/// - accounts[12..]: ctoken_accounts (destination accounts) -pub fn process_mint_to_ctoken_invoke_signed( - accounts: &[AccountInfo], - data: MintToCTokenData, -) -> Result<(), ProgramError> { - if accounts.len() < 13 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive the PDA for the mint authority - let (pda, bump) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); - - // Verify the authority account is the PDA - if &pda != accounts[2].key { - return Err(ProgramError::InvalidSeeds); - } - - let params = MintToCTokenParams::new( - data.compressed_mint_inputs, - data.amount, - data.mint_authority, - data.proof, - ); - - let system_accounts = SystemAccountInfos { - light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[4].clone(), - registered_program_pda: accounts[5].clone(), - account_compression_authority: accounts[6].clone(), - account_compression_program: accounts[7].clone(), - system_program: accounts[8].clone(), - }; - - let ctoken_accounts: Vec = accounts[12..].to_vec(); - - let account_infos = MintToCTokenInfos { - authority: accounts[2].clone(), - payer: accounts[3].clone(), - state_tree: accounts[10].clone(), - input_queue: accounts[11].clone(), - output_queue: accounts[9].clone(), - ctoken_accounts, - system_accounts, - cpi_context: None, - cpi_context_account: None, - params, - }; - - let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; - account_infos.invoke_signed(&[signer_seeds])?; - - Ok(()) -} -``` - -# Next Steps - - - - Mint c-Tokens - - - Transfer Interface c-Token - - \ No newline at end of file diff --git a/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx deleted file mode 100644 index 3d7e7ac7..00000000 --- a/compressed-token-program/ctoken/client-guides/client-close-ctoken.mdx +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Close c-Token Accounts -description: Client guide to close c-Token accounts with step-by-step implementation and full code examples. ---- -import CloseIntro from '/snippets/ctoken-guides/close-intro.mdx'; -import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; - - - - - - -```rust -let close = CloseAccount::new( - payer.pubkey(), - account, - destination, - owner, -); - -close.invoke()?; -``` - - - - -```typescript close_ctoken.ts -// TODO: TypeScript SDK coming soon -``` - - - - -# Full Code Example - -Run the full code example: -1. Clone the repository -2. Run `cargo test` - - - -### Prerequisites - - - - - - -### Close c-Token Account - - - - -```rust close_ctoken.rs expandable -// TODO: Full code example -``` - - - - -```typescript close_ctoken.ts expandable -// TODO: TypeScript SDK coming soon -``` - - - - - - -# Next Steps - - - - Close c-Token Accounts - - diff --git a/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx b/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx deleted file mode 100644 index e5e4edaf..00000000 --- a/compressed-token-program/ctoken/client-guides/client-create-cATA.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: Create Associated c-Token Accounts -description: Client guide to create associated c-Token accounts (c-ATAs) with step-by-step implementation and full code examples. ---- -import CAtaIntro from '/snippets/ctoken-guides/cata-intro.mdx'; -import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; - - - - - - -```rust -let (ata_address, _bump) = derive_ctoken_ata(&owner, &mint_pda); - -let create_ata = CreateAssociatedTokenAccount::new( - payer.pubkey(), - owner, - mint_pda, - CompressibleParams::default(), -); -let instruction = create_ata.instruction()?; -``` - - - - -```typescript create_cata.ts -// TODO: TypeScript SDK coming soon -``` - - - - -# Full Code Example - -Run the full code example: -1. Clone the repository -2. Run `cargo test` - - - -### Prerequisites - - - - - - -### Create Associated c-Token Account - - - - -```rust create_cata.rs expandable -// TODO: Full code example -``` - - - - -```typescript create_cata.ts expandable -// TODO: TypeScript SDK coming soon -``` - - - - - - -# Next Steps - - - - Create Associated c-Token Accounts - - - Create c-Token Accounts - - diff --git a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx b/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx deleted file mode 100644 index 8400d344..00000000 --- a/compressed-token-program/ctoken/client-guides/client-create-ctoken.mdx +++ /dev/null @@ -1,198 +0,0 @@ ---- -title: Create c-Token Accounts -description: "Client guide to create c-Token accounts with CreateCTokenAccount builder." ---- -import CTokenIntro from '/snippets/ctoken-guides/ctoken-intro.mdx'; -import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; -import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; -import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; - - - - -# Full Code Example - - - - -1. The example creates a test cMint. You can use existing cMints, SPL or Token 2022 mints as well. -2. Build instruction with `CreateCTokenAccount`. It automatically includes the default rent config. -```rust -use light_compressed_token_sdk::ctoken::{CreateCTokenAccount}; - -let instruction = CreateCTokenAccount::new( - payer.pubkey(), - account.pubkey(), - mint, - owner, -).instruction()?; -``` - - - - - - -3. Send transaction & verify c-Token account creation with `get_account`.
-c-Token accounts are Solana accounts and use your familiar RPC methods. - - - - - -### Prerequisites - - - - - - -### Create c-Token Account - -```rust -use borsh::BorshDeserialize; -use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::{CompressibleParams, CreateCTokenAccount}; -use light_ctoken_types::state::CToken; -use light_program_test::{LightProgramTest, ProgramTestConfig}; -use solana_sdk::{signature::Keypair, signer::Signer}; -use light_client::indexer::{AddressWithTree, Indexer}; -use light_compressed_token_sdk::ctoken::{CreateCMint, CreateCMintParams}; -use solana_sdk::{pubkey::Pubkey}; - - -#[tokio::test(flavor = "multi_thread")] -async fn test_create_ctoken_account() { - // Create simulated test environment with Light programs - let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) - .await - .unwrap(); - - let payer = rpc.get_payer().insecure_clone(); - - // Create compressed mint first (prerequisite) - let (mint, _compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; - - // Step 1: Generate new keypair for the c-Token account - let account = Keypair::new(); - let owner = payer.pubkey(); - - // Step 2: Build instruction using SDK builder - let instruction = CreateCTokenAccount::new( - payer.pubkey(), - account.pubkey(), - mint, - owner, - CompressibleParams::default(), - ) - .instruction() - .unwrap(); - - // Step 3: Send transaction - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &account]) - .await - .unwrap(); - - // Step 4: Verify account creation - let account_data = rpc.get_account(account.pubkey()).await.unwrap().unwrap(); - let ctoken_state = CToken::deserialize(&mut &account_data.data[..]).unwrap(); - - assert_eq!(ctoken_state.mint, mint.to_bytes()); - assert_eq!(ctoken_state.owner, owner.to_bytes()); - assert_eq!(ctoken_state.amount, 0); - -} -pub async fn create_compressed_mint( - rpc: &mut R, - payer: &Keypair, - decimals: u8, -) -> (Pubkey, [u8; 32]) { - let mint_signer = Keypair::new(); - let address_tree = rpc.get_address_tree_v2(); - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Derive compression address - let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( - &mint_signer.pubkey(), - &address_tree.tree, - ); - - let mint_pda = - light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; - - // Get validity proof for the address - let rpc_result = rpc - .get_validity_proof( - vec![], - vec![AddressWithTree { - address: compression_address, - tree: address_tree.tree, - }], - None, - ) - .await - .unwrap() - .value; - - // Build params - let params = CreateCMintParams { - decimals, - address_merkle_tree_root_index: rpc_result.addresses[0].root_index, - mint_authority: payer.pubkey(), - proof: rpc_result.proof.0.unwrap(), - compression_address, - mint: mint_pda, - freeze_authority: None, - extensions: None, - }; - - // Create instruction - let create_cmint = CreateCMint::new( - params, - mint_signer.pubkey(), - payer.pubkey(), - address_tree.tree, - output_queue, - ); - let instruction = create_cmint.instruction().unwrap(); - - // Send transaction - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) - .await - .unwrap(); - - (mint_pda, compression_address) -} -``` - - - - - -
- - -```typescript -// TODO: TypeScript SDK coming soon -``` - -```typescript expandable -// TODO: TypeScript SDK coming soon -``` - - -
- -# Next Steps - - - - Mint tokens to your c-Token account - - diff --git a/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx b/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx deleted file mode 100644 index 79203bd4..00000000 --- a/compressed-token-program/ctoken/client-guides/client-transfer-interface.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Transfer Interface -description: Client guide for the unified transfer interface for c-Token <> SPL token transfers with step-by-step implementation and full code examples. ---- -import TransferInterfaceIntro from '/snippets/ctoken-guides/transfer-interface-intro.mdx'; -import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-prerequisites.mdx'; - - - - - - -```rust -let transfer = TransferInterface::new( - amount, - source_account, - destination_account, - authority, - payer, - compressed_token_program_authority, -); - -transfer.invoke()?; -``` - - - - -```typescript transfer_interface.ts -// TODO: TypeScript SDK coming soon -``` - - - - -# Full Code Example - -Run the full code example: -1. Clone the repository -2. Run `cargo test` - - - -### Prerequisites - - - - - - -### Transfer Interface - - - - -```rust transfer_interface.rs expandable -// TODO: Full code example -``` - - - - -```typescript transfer_interface.ts expandable -// TODO: TypeScript SDK coming soon -``` - - - - - - -# Next Steps - - - - Transfer Interface - - - Close c-Token Accounts - - diff --git a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx deleted file mode 100644 index 763c12a3..00000000 --- a/compressed-token-program/ctoken/program-guides/program-close-ctoken.mdx +++ /dev/null @@ -1,174 +0,0 @@ ---- -title: Close c-Token Accounts -description: Program guide to close c-Token accounts via CPI to the c-Token Program with step-by-step implementation and full code examples. ---- - -import CloseAccountInfosAccountsList from '/snippets/accounts-list/close-account-infos-accounts-list.mdx'; -import CloseIntro from '/snippets/ctoken-guides/close-intro.mdx'; - - - - -Find [full code examples at the end](#full-code-example). - - -# Implementation Guide - - - - -### Build Account Infos and CPI the c-Token Program - -1. Define the c-Token account to close and where remaining lamports should go -2. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. - - - - -```rust -use light_compressed_token_sdk::ctoken::CloseAccountInfos; - -CloseAccountInfos { - token_program: token_program.clone(), - account: account.clone(), - destination: destination.clone(), - owner: owner.clone(), - rent_sponsor: Some(rent_sponsor.clone()), -} -.invoke()?; -``` - - - - -```rust -use light_compressed_token_sdk::ctoken::CloseAccountInfos; - -let close_account_infos = CloseAccountInfos { - token_program: token_program.clone(), - account: account.clone(), - destination: destination.clone(), - owner: owner.clone(), - rent_sponsor: Some(rent_sponsor.clone()), -}; - -let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; -close_account_infos.invoke_signed(&[signer_seeds])?; -``` - - - - - - - - - - - -# Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/close.rs). - - -```rust expandable -use light_compressed_token_sdk::ctoken::CloseAccountInfos; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::{ID, TOKEN_ACCOUNT_SEED}; - -/// Handler for closing a compressed token account (invoke) -/// -/// Account order: -/// - accounts[0]: token_program (ctoken program) -/// - accounts[1]: account to close (writable) -/// - accounts[2]: destination for lamports (writable) -/// - accounts[3]: owner/authority (signer) -/// - accounts[4]: rent_sponsor (optional, writable) -pub fn process_close_account_invoke(accounts: &[AccountInfo]) -> Result<(), ProgramError> { - if accounts.len() < 4 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - let rent_sponsor = if accounts.len() > 4 { - Some(accounts[4].clone()) - } else { - None - }; - - CloseAccountInfos { - token_program: accounts[0].clone(), - account: accounts[1].clone(), - destination: accounts[2].clone(), - owner: accounts[3].clone(), - rent_sponsor, - } - .invoke()?; - - Ok(()) -} - -/// Handler for closing a PDA-owned compressed token account (invoke_signed) -/// -/// Account order: -/// - accounts[0]: token_program (ctoken program) -/// - accounts[1]: account to close (writable) -/// - accounts[2]: destination for lamports (writable) -/// - accounts[3]: PDA owner/authority (not signer, program signs) -/// - accounts[4]: rent_sponsor (optional, writable) -pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<(), ProgramError> { - if accounts.len() < 4 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive the PDA for the authority - let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); - - // Verify the authority account is the PDA - if &pda != accounts[3].key { - return Err(ProgramError::InvalidSeeds); - } - - let rent_sponsor = if accounts.len() > 4 { - Some(accounts[4].clone()) - } else { - None - }; - - let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; - CloseAccountInfos { - token_program: accounts[0].clone(), - account: accounts[1].clone(), - destination: accounts[2].clone(), - owner: accounts[3].clone(), - rent_sponsor, - } - .invoke_signed(&[signer_seeds])?; - - Ok(()) -} -``` - -# Next Steps - - - - Close c-Token Accounts - - - Learn about compressed tokens - - diff --git a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx b/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx deleted file mode 100644 index 20d786b7..00000000 --- a/compressed-token-program/ctoken/program-guides/program-create-cATA.mdx +++ /dev/null @@ -1,221 +0,0 @@ ---- -title: Create Associated c-Token Accounts -description: Program guide to create associated c-Token accounts with step-by-step implementation and full code examples. ---- - -import CTokenCreateATAAccountsList from '/snippets/accounts-list/ctoken-create-ata-accounts-list.mdx'; -import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; -import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; -import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; -import CAtaIntro from '/snippets/ctoken-guides/cata-intro.mdx'; - - - - -Find [full code examples at the end](#full-code-example). - - -# Implementation Guide - - - - -### Define Rent Config Accounts - - - - - - - -### Build Account Infos and CPI the c-Token Program - -1. Pass the required accounts -2. Include rent config from `compressible_params` -3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. - -The c-ATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike c-Token accounts, owner and mint are passed as accounts, not in instruction data. - - - - - -```rust -use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; - -CreateAssociatedTokenAccount2Infos { - owner: owner.clone(), - mint: mint.clone(), - payer: payer.clone(), - associated_token_account: associated_token_account.clone(), - system_program: system_program.clone(), - bump: data.bump, - compressible: Some(compressible_params), - idempotent: false, -} -.invoke()?; -``` - - - - -```rust -use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; - -let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; -CreateAssociatedTokenAccount2Infos { - owner: owner.clone(), - mint: mint.clone(), - payer: payer.clone(), - associated_token_account: associated_token_account.clone(), - system_program: system_program.clone(), - bump: data.bump, - compressible: Some(compressible_params), - idempotent: false, -} -.invoke_signed(&[signer_seeds])?; -``` - - - - - - - -# Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). - - -```rust expandable -use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::{ - CompressibleParamsInfos, CreateAssociatedTokenAccount2Infos, -}; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::{ATA_SEED, ID}; - -/// Instruction data for create ATA V2 (owner/mint as accounts) -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateAta2Data { - pub bump: u8, - pub pre_pay_num_epochs: u8, - pub lamports_per_write: u32, -} - -/// Handler for creating ATA using V2 variant (invoke) -/// -/// Account order: -/// - accounts[0]: owner (readonly) -/// - accounts[1]: mint (readonly) -/// - accounts[2]: payer (signer, writable) -/// - accounts[3]: associated_token_account (writable) -/// - accounts[4]: system_program -/// - accounts[5]: compressible_config -/// - accounts[6]: rent_sponsor (writable) -pub fn process_create_ata2_invoke( - accounts: &[AccountInfo], - data: CreateAta2Data, -) -> Result<(), ProgramError> { - if accounts.len() < 7 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[5].clone(), - accounts[6].clone(), - accounts[4].clone(), - ); - - CreateAssociatedTokenAccount2Infos { - owner: accounts[0].clone(), - mint: accounts[1].clone(), - payer: accounts[2].clone(), - associated_token_account: accounts[3].clone(), - system_program: accounts[4].clone(), - bump: data.bump, - compressible: Some(compressible_params), - idempotent: false, - } - .invoke()?; - - Ok(()) -} - -/// Handler for creating ATA using V2 variant with PDA ownership (invoke_signed) -/// -/// Account order: -/// - accounts[0]: owner (PDA, readonly) -/// - accounts[1]: mint (readonly) -/// - accounts[2]: payer (PDA, writable, not signer - program signs) -/// - accounts[3]: associated_token_account (writable) -/// - accounts[4]: system_program -/// - accounts[5]: compressible_config -/// - accounts[6]: rent_sponsor (writable) -pub fn process_create_ata2_invoke_signed( - accounts: &[AccountInfo], - data: CreateAta2Data, -) -> Result<(), ProgramError> { - if accounts.len() < 7 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive the PDA that will act as payer - let (pda, bump) = Pubkey::find_program_address(&[ATA_SEED], &ID); - - // Verify the payer is the PDA - if &pda != accounts[2].key { - return Err(ProgramError::InvalidSeeds); - } - - let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[5].clone(), - accounts[6].clone(), - accounts[4].clone(), - ); - - let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; - CreateAssociatedTokenAccount2Infos { - owner: accounts[0].clone(), - mint: accounts[1].clone(), - payer: accounts[2].clone(), // PDA - associated_token_account: accounts[3].clone(), - system_program: accounts[4].clone(), - bump: data.bump, - compressible: Some(compressible_params), - idempotent: false, - } - .invoke_signed(&[signer_seeds])?; - - Ok(()) -} -``` - -# Next Steps - - - - Create Associated c-Token Accounts - - - Mint c-Tokens - - diff --git a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx b/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx deleted file mode 100644 index b469c46b..00000000 --- a/compressed-token-program/ctoken/program-guides/program-create-ctoken.mdx +++ /dev/null @@ -1,205 +0,0 @@ ---- -title: Create c-Token Accounts -description: Program guide to create c-Token accounts with step-by-step implementation and full code examples. ---- - -import CTokenCreateAccountsList from '/snippets/ctoken-create-accounts-list.mdx'; -import CTokenProtocolPDAs from '/snippets/ctoken-protocol-pdas.mdx'; -import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; -import { CompressibleRentCalculator } from '/snippets/compressible-rent-calculator.jsx'; -import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; -import CTokenAccountProperties from '/snippets/ctoken-account-properties.mdx'; -import CTokenIntro from '/snippets/ctoken-guides/ctoken-intro.mdx'; - - - - -Find [full code examples at the end](#full-code-example). - - -# Implementation Guide - - - - -### Configure Rent - - - - - - -### Build Account Infos and CPI the c-Token Program - -1. Pass the required accounts -2. Include rent config from `compressible_params` -3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. - - - -```rust -use light_compressed_token_sdk::ctoken::CreateCTokenAccountInfos; - -CreateCTokenAccountInfos { - payer: payer.clone(), - account: account.clone(), - mint: mint.clone(), - owner: data.owner, - compressible: Some(compressible_params), -} -.invoke()?; -``` - - - - - -```rust -use light_compressed_token_sdk::ctoken::CreateCTokenAccountInfos; - -let account_infos = CreateCTokenAccountInfos { - payer: payer.clone(), - account: account.clone(), - mint: mint.clone(), - owner: data.owner, - compressible: Some(compressible_params), -}; - -let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; -account_infos.invoke_signed(&[signer_seeds])?; -``` - - - - - - - -# Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/create_token_account.rs). - - -```rust expandable -use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::{ID, TOKEN_ACCOUNT_SEED}; - -/// Instruction data for create token account -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateTokenAccountData { - pub owner: Pubkey, - pub pre_pay_num_epochs: u8, - pub lamports_per_write: u32, -} - -/// Handler for creating a compressible token account (invoke) -/// -/// Uses the builder pattern from the ctoken module. This demonstrates how to: -/// 1. Build the account infos struct with compressible params -/// 2. Call the invoke() method which handles instruction building and CPI -/// -/// Account order: -/// - accounts[0]: payer (signer) -/// - accounts[1]: account to create (signer) -/// - accounts[2]: mint -/// - accounts[3]: compressible_config -/// - accounts[4]: system_program -/// - accounts[5]: rent_sponsor -pub fn process_create_token_account_invoke( - accounts: &[AccountInfo], - data: CreateTokenAccountData, -) -> Result<(), ProgramError> { - if accounts.len() < 6 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Build the compressible params using constructor - let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), - accounts[5].clone(), - accounts[4].clone(), - ); - - // Build the account infos struct - CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), - owner: data.owner, - compressible: Some(compressible_params), - } - .invoke()?; - - Ok(()) -} - -/// Handler for creating a compressible token account with PDA ownership (invoke_signed) -/// -/// Account order: -/// - accounts[0]: payer (signer) -/// - accounts[1]: account to create (PDA, will be derived and verified) -/// - accounts[2]: mint -/// - accounts[3]: compressible_config -/// - accounts[4]: system_program -/// - accounts[5]: rent_sponsor -pub fn process_create_token_account_invoke_signed( - accounts: &[AccountInfo], - data: CreateTokenAccountData, -) -> Result<(), ProgramError> { - if accounts.len() < 6 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive the PDA for the token account - let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); - - // Verify the account to create is the PDA - if &pda != accounts[1].key { - return Err(ProgramError::InvalidSeeds); - } - - // Build the compressible params using constructor - let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, - accounts[3].clone(), - accounts[5].clone(), - accounts[4].clone(), - ); - - // Build the account infos struct - let account_infos = CreateCTokenAccountInfos { - payer: accounts[0].clone(), - account: accounts[1].clone(), - mint: accounts[2].clone(), - owner: data.owner, - compressible: Some(compressible_params), - }; - - // Invoke with PDA signing - let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; - account_infos.invoke_signed(&[signer_seeds])?; - - Ok(()) -} -``` - -# Next Steps - - - - Mint tokens to your c-Token account - - diff --git a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx b/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx deleted file mode 100644 index c03f0dbc..00000000 --- a/compressed-token-program/ctoken/program-guides/program-transfer-interface.mdx +++ /dev/null @@ -1,250 +0,0 @@ ---- -title: Transfer Interface -description: Program guide for transfers between c-Token and SPL token accounts via CPI. The interface detects account types and invokes the right programs. - ---- - -import TransferInterfaceAccountsListCtoken1 from '/snippets/accounts-list/transfer-interface-accounts-list-ctoken-1.mdx'; -import TransferInterfaceAccountsListSpl2 from '/snippets/accounts-list/transfer-interface-accounts-list-spl-2.mdx'; -import TransferInterfaceIntro from '/snippets/ctoken-guides/transfer-interface-intro.mdx'; - - - - -Find [full code examples at the end](#full-code-example). - - -# Implementation Guide - - - - -### c-Token Transfer Interface - -Define the number of c-Tokens / SPL tokens to transfer -- from which SPL or c-Token account, and -- to which SPL or c-Token account. - -```rust -use light_compressed_token_sdk::ctoken::TransferInterface; - -let mut transfer = TransferInterface::new( - data.amount, - source_account.clone(), - destination_account.clone(), - authority.clone(), - payer.clone(), - compressed_token_program_authority.clone(), -); -``` - - - - - - - - -### SPL Transfer Interface (Optional) - -The SPL transfer interface is only needed for SPL ↔ c-Token transfers. -```rust -transfer = transfer.with_spl_interface( - Some(mint.clone()), - Some(spl_token_program.clone()), - Some(spl_interface_pda.clone()), - data.spl_interface_pda_bump, -)?; -``` - -SPL ↔ c-Token transfers require a `spl_interface_pda`: - * **SPL → c-Token**: SPL tokens are locked by the c-Token Program in the PDA, c-Tokens are minted to c-Token accounts - * **c-Token → SPL**: c-Tokens are burned, SPL tokens transferred to SPL token accounts - -The interface PDA is derived from the `mint` pubkey and pool seed. - - - - - - - - - -### CPI the c-Token Program - -CPI the c-Token program to execute the transfer. -Use `invoke()`, or `invoke_signed()` when a CPI requires a PDA signer. - - - -```rust -transfer.invoke()?; -``` - - -```rust -let authority_seeds: &[&[u8]] = &[TRANSFER_INTERFACE_AUTHORITY_SEED, &[authority_bump]]; -transfer.invoke_signed(&[authority_seeds])?; -``` - - - - - - -# Full Code Example - - -Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-tests/sdk-ctoken-test/src/transfer_interface.rs). - - -```rust expandable -use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::TransferInterface; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::ID; - -/// PDA seed for authority in invoke_signed variants -pub const TRANSFER_INTERFACE_AUTHORITY_SEED: &[u8] = b"transfer_interface_authority"; - -/// Instruction data for TransferInterface -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct TransferInterfaceData { - pub amount: u64, - /// Required for SPL<->c-Token transfers, None for c-Token->c-Token - pub token_pool_pda_bump: Option, -} - -/// Handler for TransferInterface (invoke) -/// -/// This unified interface automatically detects account types and routes to: -/// - c-Token -> c-Token transfer -/// - c-Token -> SPL transfer -/// - SPL -> c-Token transfer -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or c-Token) -/// - accounts[2]: destination_account (SPL or c-Token) -/// - accounts[3]: authority (signer) -/// - accounts[4]: payer (signer) -/// - accounts[5]: compressed_token_program_authority -/// For SPL interface (required for SPL<->c-Token): -/// - accounts[6]: mint -/// - accounts[7]: token_pool_pda -/// - accounts[8]: spl_token_program -pub fn process_transfer_interface_invoke( - accounts: &[AccountInfo], - data: TransferInterfaceData, -) -> Result<(), ProgramError> { - if accounts.len() < 6 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - let mut transfer = TransferInterface::new( - data.amount, - accounts[1].clone(), // source_account - accounts[2].clone(), // destination_account - accounts[3].clone(), // authority - accounts[4].clone(), // payer - accounts[5].clone(), // compressed_token_program_authority - ); - - // Add SPL interface config if provided - if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { - transfer = transfer.with_spl_interface( - Some(accounts[6].clone()), // mint - Some(accounts[8].clone()), // spl_token_program - Some(accounts[7].clone()), // token_pool_pda - data.token_pool_pda_bump, - )?; - } - - transfer.invoke()?; - - Ok(()) -} - -/// Handler for TransferInterface with PDA authority (invoke_signed) -/// -/// The authority is a PDA derived from TRANSFER_INTERFACE_AUTHORITY_SEED. -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or c-Token) -/// - accounts[2]: destination_account (SPL or c-Token) -/// - accounts[3]: authority (PDA, not signer - program signs) -/// - accounts[4]: payer (signer) -/// - accounts[5]: compressed_token_program_authority -/// For SPL interface (required for SPL<->c-Token): -/// - accounts[6]: mint -/// - accounts[7]: token_pool_pda -/// - accounts[8]: spl_token_program -pub fn process_transfer_interface_invoke_signed( - accounts: &[AccountInfo], - data: TransferInterfaceData, -) -> Result<(), ProgramError> { - if accounts.len() < 6 { - return Err(ProgramError::NotEnoughAccountKeys); - } - - // Derive the PDA for the authority - let (authority_pda, authority_bump) = - Pubkey::find_program_address(&[TRANSFER_INTERFACE_AUTHORITY_SEED], &ID); - - // Verify the authority account is the PDA - if &authority_pda != accounts[3].key { - return Err(ProgramError::InvalidSeeds); - } - - let mut transfer = TransferInterface::new( - data.amount, - accounts[1].clone(), // source_account - accounts[2].clone(), // destination_account - accounts[3].clone(), // authority (PDA) - accounts[4].clone(), // payer - accounts[5].clone(), // compressed_token_program_authority - ); - - // Add SPL interface config if provided - if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { - transfer = transfer.with_spl_interface( - Some(accounts[6].clone()), // mint - Some(accounts[8].clone()), // spl_token_program - Some(accounts[7].clone()), // token_pool_pda - data.token_pool_pda_bump, - )?; - } - - // Invoke with PDA signing - let authority_seeds: &[&[u8]] = &[TRANSFER_INTERFACE_AUTHORITY_SEED, &[authority_bump]]; - transfer.invoke_signed(&[authority_seeds])?; - - Ok(()) -} -``` - -# Next Steps - - - - Transfer Interface - - - Close c-Token Accounts - - \ No newline at end of file diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index 907dc576..b3e5f88e 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -32,7 +32,7 @@ import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx';
## c-Token Accounts - * Hold token balances of c-Mint, SPL mint, or Token 2022 mints. + * Hold token balances of c-Mints, SPL mints, or Token 2022 mints. * SPL tokens can be converted to
c-Tokens and back on demand. * Custom rent config via compressible extension.
@@ -78,12 +78,8 @@ ADD TABLE OF "FOR" ## Cookbook for Program and Clients -### c-Mint Guides - -### c-Token Guides - ## Examples for Program and Client diff --git a/docs.json b/docs.json index 2cfe6999..57259a1c 100644 --- a/docs.json +++ b/docs.json @@ -12,15 +12,12 @@ "default": "light" }, "navigation": { - "tabs": [ + "groups": [ { - "tab": "Docs", - "pages": [ - { "group": "Get Started", "pages": [ - "/landing", - "/quickstart", + "landing", + "quickstart", "learn/ai-tools-guide" ] }, @@ -459,8 +456,6 @@ "api-reference/libraries/light-program-test" ] } - ] - } ], "global": { "anchors": [ diff --git a/landing.mdx b/landing.mdx index 207cf9ea..100beac8 100644 --- a/landing.mdx +++ b/landing.mdx @@ -2,7 +2,6 @@ title: Welcome to ZK Compression sidebarTitle: Introduction description: ZK Compression is a Solana account primitive that lets you create tokens and PDAs without rent-exemption with L1 performance and security. -icon: book-open --- diff --git a/quickstart.mdx b/quickstart.mdx index 31462f6c..9d36c236 100644 --- a/quickstart.mdx +++ b/quickstart.mdx @@ -2,7 +2,6 @@ title: Quickstart description: Create your first compressed tokens in under 5 minutes. sidebarTitle: Quickstart -icon: bolt --- import SdkInstall from '/snippets/versions/sdk-install-0.22.1-alpha.1.mdx'; From a5231abc1189e4cb8d9fa586eb376a3bd406fa9e Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 00:49:17 +0000 Subject: [PATCH 064/143] Add explanatory bullet points to c-Token client guides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add step-by-step instructions to create-cmint.mdx Rust client section - Add minting workflow explanation to mint-ctokens.mdx - Add c-ATA creation steps to create-cata.mdx - Include code snippets showing SDK usage patterns - Note TODO items for validity proof tooltips and CompressibleParams 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../cmint/create-cmint.mdx | 28 +- .../cmint/mint-ctokens.mdx | 207 +++++++++ .../ctoken/create-cata.mdx | 135 ++++++ .../ctoken/transfer-interface.mdx | 399 ++++++++++++++++++ 4 files changed, 767 insertions(+), 2 deletions(-) diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index 6256f209..e66f6e05 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -27,6 +27,31 @@ Learn the [core concepts to the c-Token Program here](/compressed-token-program/ + +The example creates a c-Mint with token metadata. +1. Derive the mint address and +fetch a validity proof from your RPC that proves the address does not exist yet. + +TODO: ADD TOOLTIP TO VALIDITY PROOF + +3. Define your Token Metadata (name, symbol, URI, additional metadata) +4. Build the instruction with `CreateCMint::new()`: +```rust +use light_compressed_token_sdk::ctoken::CreateCMint; + +let create_cmint = CreateCMint::new( + params, + mint_signer.pubkey(), + payer.pubkey(), + address_tree.tree, + output_queue, +); +let instruction = create_cmint.instruction()?; +``` + +5. Send transaction with both `payer` and `mint_signer` as signers.
+c-Mint addresses are derived from the mint signer and address tree. + ### Prerequisites @@ -38,8 +63,7 @@ Learn the [core concepts to the c-Token Program here](/compressed-token-program/ ### Create c-Mint with Token Metadata -```rust// Test for: client-create-cmint.mdx - +```rust use light_client::indexer::{AddressWithTree, Indexer}; use light_client::rpc::Rpc; use light_compressed_token_sdk::ctoken::{CreateCMint, CreateCMintParams}; diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index db5adfb9..70a5b569 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -30,6 +30,30 @@ Find [full code examples at the end](#full-code-example).
+ +The example mints c-Tokens to existing c-Token accounts. +1. Prerequisite: The example creates a test c-Mint and destination c-Token account. + +TODO: ADD MORE steps + +2. Build the instruction with `MintToCToken::new()`. +```rust +use light_compressed_token_sdk::ctoken::MintToCToken; + +let instruction = MintToCToken::new( + params, + payer.pubkey(), + state_tree, + output_queue, + input_queue, + vec![recipient_account.pubkey()], +) +.instruction()?; +``` + +3. Send transaction with payer as signer.
+Only the mint authority can mint new c-Tokens. + ### Prerequisites @@ -40,6 +64,189 @@ Find [full code examples at the end](#full-code-example). ### Mint c-Tokens + +```rust +use borsh::BorshDeserialize; +use light_client::indexer::{AddressWithTree, Indexer}; +use light_client::rpc::Rpc; +use light_compressed_token_sdk::ctoken::{ + CompressibleParams, CreateCMint, CreateCMintParams, CreateCTokenAccount, MintToCToken, + MintToCTokenParams, +}; +use light_ctoken_types::instructions::mint_action::CompressedMintWithContext; +use light_ctoken_types::state::{CToken, CompressedMint}; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; + + +#[tokio::test(flavor = "multi_thread")] +async fn test_mint_to_ctoken_client() { + // Step 1: Setup test environment + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + let mint_authority = payer.pubkey(); + + // Step 2: Create compressed mint (prerequisite) + let (mint, compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; + + // Step 3: Create ctoken account (prerequisite) + let ctoken_account = Keypair::new(); + let owner = payer.pubkey(); + let create_account_ix = CreateCTokenAccount::new( + payer.pubkey(), + ctoken_account.pubkey(), + mint, + owner, + CompressibleParams::default(), + ) + .instruction() + .unwrap(); + + rpc.create_and_send_transaction( + &[create_account_ix], + &payer.pubkey(), + &[&payer, &ctoken_account], + ) + .await + .unwrap(); + + // Step 4: Get compressed mint account to build CompressedMintWithContext + let compressed_mint_account = rpc + .get_compressed_account(compression_address, None) + .await + .unwrap() + .value + .expect("Compressed mint should exist"); + + // Step 5: Get validity proof for the mint operation + let rpc_result = rpc + .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) + .await + .unwrap() + .value; + + // Step 6: Deserialize compressed mint data + let compressed_mint = CompressedMint::deserialize( + &mut compressed_mint_account.data.unwrap().data.as_slice(), + ) + .unwrap(); + + // Step 7: Build CompressedMintWithContext + let compressed_mint_with_context = CompressedMintWithContext { + address: compression_address, + leaf_index: compressed_mint_account.leaf_index, + prove_by_index: true, + root_index: rpc_result.accounts[0] + .root_index + .root_index() + .unwrap_or_default(), + mint: compressed_mint.try_into().unwrap(), + }; + + let amount = 1_000_000_000u64; // 1 token with 9 decimals + + // Step 8: Build mint params + let params = MintToCTokenParams::new( + compressed_mint_with_context, + amount, + mint_authority, + rpc_result.proof, + ); + + // Step 9: Build instruction using SDK builder + let instruction = MintToCToken::new( + params, + payer.pubkey(), + compressed_mint_account.tree_info.tree, + compressed_mint_account.tree_info.queue, + compressed_mint_account.tree_info.queue, + vec![ctoken_account.pubkey()], + ) + .instruction() + .unwrap(); + + // Step 10: Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Step 11: Verify tokens were minted + let ctoken_account_data = rpc + .get_account(ctoken_account.pubkey()) + .await + .unwrap() + .unwrap(); + + let ctoken_state = CToken::deserialize(&mut &ctoken_account_data.data[..]).unwrap(); + assert_eq!(ctoken_state.amount, amount, "Token amount should match"); + assert_eq!(ctoken_state.mint, mint.to_bytes(), "Mint should match"); + assert_eq!(ctoken_state.owner, owner.to_bytes(), "Owner should match"); +} +pub async fn create_compressed_mint( + rpc: &mut R, + payer: &Keypair, + decimals: u8, +) -> (Pubkey, [u8; 32]) { + let mint_signer = Keypair::new(); + let address_tree = rpc.get_address_tree_v2(); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; + + // Derive compression address + let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + &mint_signer.pubkey(), + &address_tree.tree, + ); + + let mint_pda = + light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + + // Get validity proof for the address to proof it does not exist in the address tree + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![AddressWithTree { + address: compression_address, + tree: address_tree.tree, + }], + None, + ) + .await + .unwrap() + .value; + + // Build params + let params = CreateCMintParams { + decimals, + address_merkle_tree_root_index: rpc_result.addresses[0].root_index, + mint_authority: payer.pubkey(), + proof: rpc_result.proof.0.unwrap(), + compression_address, + mint: mint_pda, + freeze_authority: None, + extensions: None, + }; + + // Create instruction + let create_cmint = CreateCMint::new( + params, + mint_signer.pubkey(), + payer.pubkey(), + address_tree.tree, + output_queue, + ); + let instruction = create_cmint.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) + .await + .unwrap(); + + (mint_pda, compression_address) +} +```
diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index 91def2f3..aba0fe2c 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -35,6 +35,141 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c +TODO: RM COMPRESSIBLE PARAMS + +1. The example creates a test c-Mint. You can use existing c-Mints, SPL or Token 2022 mints as well. +2. Derive the address from mint and owner pubkey. +2. Build the instruction with `CreateAssociatedTokenAccount2`. It automatically includes the default rent config: +```rust +use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2; + +let instruction = CreateAssociatedTokenAccount2::new( + payer.pubkey(), + owner, + mint, + CompressibleParams::default(), +) +.instruction()?; +``` + +3. Send transaction & verify c-ATA creation with `get_account`.
+c-ATAs are Solana accounts and use your familiar RPC methods. + + +```rust +use borsh::BorshDeserialize; +use light_client::indexer::{AddressWithTree, Indexer}; +use light_client::rpc::Rpc; +use light_compressed_token_sdk::ctoken::{ + derive_ctoken_ata, CompressibleParams, CreateAssociatedTokenAccount2, CreateCMint, + CreateCMintParams, +}; +use light_ctoken_types::state::CToken; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; + + +#[tokio::test(flavor = "multi_thread")] +async fn test_create_cata_client() { + // Step 1: Setup test environment + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + + // Step 2: Create compressed mint (prerequisite) + let (mint, _compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; + + // Step 3: Define owner and derive ATA address + let owner = payer.pubkey(); + let (ata_address, _bump) = derive_ctoken_ata(&owner, &mint); + + // Step 4: Build instruction using SDK builder + let instruction = CreateAssociatedTokenAccount2::new( + payer.pubkey(), + owner, + mint, + CompressibleParams::default(), + ) + .instruction() + .unwrap(); + + // Step 5: Send transaction (only payer signs, no account keypair needed) + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Step 6: Verify cATA creation + let account_data = rpc.get_account(ata_address).await.unwrap().unwrap(); + let ctoken_state = CToken::deserialize(&mut &account_data.data[..]).unwrap(); + + assert_eq!(ctoken_state.mint, mint.to_bytes(), "Mint should match"); + assert_eq!(ctoken_state.owner, owner.to_bytes(), "Owner should match"); + assert_eq!(ctoken_state.amount, 0, "Initial amount should be 0"); +} +pub async fn create_compressed_mint( + rpc: &mut R, + payer: &Keypair, + decimals: u8, +) -> (Pubkey, [u8; 32]) { + let mint_signer = Keypair::new(); + let address_tree = rpc.get_address_tree_v2(); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; + + // Derive compression address + let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + &mint_signer.pubkey(), + &address_tree.tree, + ); + + let mint_pda = + light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + + // Get validity proof for the address + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![AddressWithTree { + address: compression_address, + tree: address_tree.tree, + }], + None, + ) + .await + .unwrap() + .value; + + // Build params + let params = CreateCMintParams { + decimals, + address_merkle_tree_root_index: rpc_result.addresses[0].root_index, + mint_authority: payer.pubkey(), + proof: rpc_result.proof.0.unwrap(), + compression_address, + mint: mint_pda, + freeze_authority: None, + extensions: None, + }; + + // Create instruction + let create_cmint = CreateCMint::new( + params, + mint_signer.pubkey(), + payer.pubkey(), + address_tree.tree, + output_queue, + ); + let instruction = create_cmint.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) + .await + .unwrap(); + + (mint_pda, compression_address) +} +``` diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index 669c33f3..e0ac5e03 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -58,6 +58,405 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c
+ + + + +```rust +use borsh::BorshDeserialize; +use light_client::indexer::{AddressWithTree, Indexer}; +use light_client::rpc::Rpc; +use light_compressed_token_sdk::ctoken::{ + derive_ctoken_ata, CompressibleParams, CreateAssociatedTokenAccount2, CreateCMint, + CreateCMintParams, MintToCToken, MintToCTokenParams, TransferCtoken, +}; +use light_ctoken_types::instructions::mint_action::CompressedMintWithContext; +use light_ctoken_types::state::{CToken, CompressedMint}; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; + + +#[tokio::test(flavor = "multi_thread")] +async fn test_client_transfer_ctoken_to_ctoken() { + // Step 1: Setup test environment + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + let mint_authority = payer.pubkey(); + + // Step 2: Create compressed mint + let (mint, compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; + + // Step 3: Create sender cToken ATA + let sender = payer.pubkey(); + let (sender_ata, _) = derive_ctoken_ata(&sender, &mint); + + let create_sender_ata = CreateAssociatedTokenAccount2::new( + payer.pubkey(), + sender, + mint, + CompressibleParams::default(), + ) + .instruction() + .unwrap(); + + rpc.create_and_send_transaction(&[create_sender_ata], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Step 4: Mint tokens to sender (1000 tokens) + let mint_amount = 1_000_000_000_000u64; // 1000 tokens with 9 decimals + + let compressed_mint_account = rpc + .get_compressed_account(compression_address, None) + .await + .unwrap() + .value + .expect("Compressed mint should exist"); + + let rpc_result = rpc + .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) + .await + .unwrap() + .value; + + let compressed_mint = CompressedMint::deserialize( + &mut compressed_mint_account.data.unwrap().data.as_slice(), + ) + .unwrap(); + + let compressed_mint_with_context = CompressedMintWithContext { + address: compression_address, + leaf_index: compressed_mint_account.leaf_index, + prove_by_index: true, + root_index: rpc_result.accounts[0] + .root_index + .root_index() + .unwrap_or_default(), + mint: compressed_mint.try_into().unwrap(), + }; + + let mint_params = MintToCTokenParams::new( + compressed_mint_with_context, + mint_amount, + mint_authority, + rpc_result.proof, + ); + + let mint_instruction = MintToCToken::new( + mint_params, + payer.pubkey(), + compressed_mint_account.tree_info.tree, + compressed_mint_account.tree_info.queue, + compressed_mint_account.tree_info.queue, + vec![sender_ata], + ) + .instruction() + .unwrap(); + + rpc.create_and_send_transaction(&[mint_instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Step 5: Create recipient cToken ATA + let recipient = Keypair::new(); + let (recipient_ata, _) = derive_ctoken_ata(&recipient.pubkey(), &mint); + + let create_recipient_ata = CreateAssociatedTokenAccount2::new( + payer.pubkey(), + recipient.pubkey(), + mint, + CompressibleParams::default(), + ) + .instruction() + .unwrap(); + + rpc.create_and_send_transaction(&[create_recipient_ata], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Step 6: Transfer tokens from sender to recipient + let transfer_amount = 500_000_000_000u64; // 500 tokens + + let transfer_instruction = TransferCtoken { + source: sender_ata, + destination: recipient_ata, + amount: transfer_amount, + authority: sender, + } + .instruction() + .unwrap(); + + rpc.create_and_send_transaction(&[transfer_instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Step 7: Verify balances + let sender_account_data = rpc.get_account(sender_ata).await.unwrap().unwrap(); + let sender_state = CToken::deserialize(&mut &sender_account_data.data[..]).unwrap(); + + let recipient_account_data = rpc.get_account(recipient_ata).await.unwrap().unwrap(); + let recipient_state = CToken::deserialize(&mut &recipient_account_data.data[..]).unwrap(); + + assert_eq!( + sender_state.amount, + mint_amount - transfer_amount, + "Sender balance should be decreased" + ); + assert_eq!( + recipient_state.amount, transfer_amount, + "Recipient balance should be increased" + ); +} +pub async fn create_compressed_mint( + rpc: &mut R, + payer: &Keypair, + decimals: u8, +) -> (Pubkey, [u8; 32]) { + let mint_signer = Keypair::new(); + let address_tree = rpc.get_address_tree_v2(); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; + + // Derive compression address + let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + &mint_signer.pubkey(), + &address_tree.tree, + ); + + let mint_pda = + light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + + // Get validity proof for the address + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![AddressWithTree { + address: compression_address, + tree: address_tree.tree, + }], + None, + ) + .await + .unwrap() + .value; + + // Build params + let params = CreateCMintParams { + decimals, + address_merkle_tree_root_index: rpc_result.addresses[0].root_index, + mint_authority: payer.pubkey(), + proof: rpc_result.proof.0.unwrap(), + compression_address, + mint: mint_pda, + freeze_authority: None, + extensions: None, + }; + + // Create instruction + let create_cmint = CreateCMint::new( + params, + mint_signer.pubkey(), + payer.pubkey(), + address_tree.tree, + output_queue, + ); + let instruction = create_cmint.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) + .await + .unwrap(); + + (mint_pda, compression_address) +} +``` + + + +```rust +// Test for: transfer-interface.mdx + +use borsh::BorshDeserialize; +use light_client::rpc::Rpc; +use light_compressed_token_sdk::{ + ctoken::{ + derive_ctoken_ata, CompressibleParams, CreateAssociatedTokenAccount2, TransferCtoken, + TransferSplToCtoken, + }, + token_pool::find_token_pool_pda_with_index, +}; +use light_compressed_token_types::CPI_AUTHORITY_PDA; +use light_ctoken_types::state::CToken; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use light_test_utils::spl::{create_mint_helper, create_token_2022_account, mint_spl_tokens}; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; +use spl_token_2022::state::Account as TokenAccount; +use solana_program::program_pack::Pack; +use anchor_spl; + + +#[tokio::test(flavor = "multi_thread")] +async fn test_client_transfer_spl_to_ctoken_to_ctoken() { + // Step 1: Setup test environment + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + let sender = Keypair::new(); + light_test_utils::airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) + .await + .unwrap(); + + // Step 2: Create SPL mint + let mint = create_mint_helper(&mut rpc, &payer).await; + let initial_amount = 10000u64; + let spl_to_ctoken_amount = 7000u64; + let ctoken_transfer_amount = 3000u64; + + // Step 3: Create SPL token account and mint tokens + let spl_token_account = Keypair::new(); + create_token_2022_account(&mut rpc, &mint, &spl_token_account, &sender, false) + .await + .unwrap(); + + mint_spl_tokens( + &mut rpc, + &mint, + &spl_token_account.pubkey(), + &payer.pubkey(), + &payer, + initial_amount, + false, + ) + .await + .unwrap(); + + // Step 4: Create sender cToken ATA + let (sender_ctoken_ata, _) = derive_ctoken_ata(&sender.pubkey(), &mint); + + let create_sender_ata = CreateAssociatedTokenAccount2::new( + payer.pubkey(), + sender.pubkey(), + mint, + CompressibleParams::default(), + ) + .instruction() + .unwrap(); + + rpc.create_and_send_transaction(&[create_sender_ata], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Step 5: Convert SPL tokens to cToken (7000 tokens) + let (token_pool_pda, token_pool_pda_bump) = find_token_pool_pda_with_index(&mint, 0); + let cpi_authority_pda = Pubkey::new_from_array(CPI_AUTHORITY_PDA); + + let spl_token_program = anchor_spl::token::ID; + + let spl_to_ctoken_instruction = TransferSplToCtoken { + amount: spl_to_ctoken_amount, + token_pool_pda_bump, + source_spl_token_account: spl_token_account.pubkey(), + destination_ctoken_account: sender_ctoken_ata, + authority: sender.pubkey(), + mint, + payer: payer.pubkey(), + token_pool_pda, + spl_token_program, + } + .instruction() + .unwrap(); + + rpc.create_and_send_transaction( + &[spl_to_ctoken_instruction], + &payer.pubkey(), + &[&payer, &sender], + ) + .await + .unwrap(); + + // Step 6: Create recipient cToken ATA + let recipient = Keypair::new(); + light_test_utils::airdrop_lamports(&mut rpc, &recipient.pubkey(), 1_000_000_000) + .await + .unwrap(); + + let (recipient_ctoken_ata, _) = derive_ctoken_ata(&recipient.pubkey(), &mint); + + let create_recipient_ata = CreateAssociatedTokenAccount2::new( + payer.pubkey(), + recipient.pubkey(), + mint, + CompressibleParams::default(), + ) + .instruction() + .unwrap(); + + rpc.create_and_send_transaction(&[create_recipient_ata], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Step 7: Transfer cToken from sender to recipient (3000 tokens) + let transfer_instruction = TransferCtoken { + source: sender_ctoken_ata, + destination: recipient_ctoken_ata, + amount: ctoken_transfer_amount, + authority: sender.pubkey(), + } + .instruction() + .unwrap(); + + rpc.create_and_send_transaction( + &[transfer_instruction], + &payer.pubkey(), + &[&payer, &sender], + ) + .await + .unwrap(); + + // Step 8: Verify balances + let sender_account_data = rpc.get_account(sender_ctoken_ata).await.unwrap().unwrap(); + let sender_state = CToken::deserialize(&mut &sender_account_data.data[..]).unwrap(); + + let recipient_account_data = rpc + .get_account(recipient_ctoken_ata) + .await + .unwrap() + .unwrap(); + let recipient_state = CToken::deserialize(&mut &recipient_account_data.data[..]).unwrap(); + + // Sender should have: 7000 (converted) - 3000 (transferred) = 4000 + assert_eq!( + sender_state.amount, + spl_to_ctoken_amount - ctoken_transfer_amount, + "Sender cToken balance should be 4000" + ); + + // Recipient should have: 3000 (received) + assert_eq!( + recipient_state.amount, ctoken_transfer_amount, + "Recipient cToken balance should be 3000" + ); + + // Verify SPL account still has remaining tokens: 10000 - 7000 = 3000 + let spl_account_data = rpc + .get_account(spl_token_account.pubkey()) + .await + .unwrap() + .unwrap(); + let spl_account = TokenAccount::unpack(&spl_account_data.data).unwrap(); + assert_eq!( + spl_account.amount, + initial_amount - spl_to_ctoken_amount, + "SPL account should have 3000 remaining" + ); +} +``` + + ### Prerequisites From e4c606bdea288c81cff5a37ea25b6c553d3dd5c9 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 00:56:23 +0000 Subject: [PATCH 065/143] Add client transfer examples to transfer-interface.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add c-Mint cToken to cToken transfer example - Add SPL token to cToken conversion and transfer example - Include complete test code with balance verification - Show TransferCtoken and TransferSplToCtoken SDK usage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../ctoken/transfer-interface.mdx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index e0ac5e03..f143f3a0 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -59,6 +59,18 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c + + +### Prerequisites + + + + + + +### Transfer Interface + + @@ -276,8 +288,6 @@ pub async fn create_compressed_mint( ```rust -// Test for: transfer-interface.mdx - use borsh::BorshDeserialize; use light_client::rpc::Rpc; use light_compressed_token_sdk::{ @@ -457,18 +467,7 @@ async fn test_client_transfer_spl_to_ctoken_to_ctoken() { ``` - - -### Prerequisites - - - - - -### Close c-Token Account - - From 86a93a8989f62f12b1c3425ef80aaef3623df20c Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 01:11:53 +0000 Subject: [PATCH 066/143] Refactor client examples and add close account guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Simplify transfer-interface.mdx: remove nested tabs, add workflow steps - Add complete close account example to close-ctoken-account.mdx - Show CloseAccount SDK usage with balance verification - Remove broken README link from transfer-interface.mdx 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../ctoken/close-ctoken-account.mdx | 142 +++++++++++ .../ctoken/transfer-interface.mdx | 222 +----------------- 2 files changed, 148 insertions(+), 216 deletions(-) diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx index a434a684..9f03ba89 100644 --- a/compressed-token-program/ctoken/close-ctoken-account.mdx +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -31,6 +31,148 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c ### Close c-Token Account + +```rust +use borsh::BorshDeserialize; +use light_client::indexer::{AddressWithTree, Indexer}; +use light_client::rpc::Rpc; +use light_compressed_token_sdk::ctoken::{ + CloseAccount, CompressibleParams, CreateCMint, CreateCMintParams, CreateCTokenAccount, + CTOKEN_PROGRAM_ID, +}; +use light_ctoken_types::state::CToken; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; + + +#[tokio::test(flavor = "multi_thread")] +async fn test_close_ctoken_account() { + // Step 1: Setup test environment + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + + // Step 2: Create compressed mint (prerequisite) + let (mint, _compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; + + // Step 3: Create cToken account with 0 balance + let account = Keypair::new(); + let owner = payer.pubkey(); + + let create_instruction = CreateCTokenAccount::new( + payer.pubkey(), + account.pubkey(), + mint, + owner, + CompressibleParams::default(), + ) + .instruction() + .unwrap(); + + rpc.create_and_send_transaction( + &[create_instruction], + &payer.pubkey(), + &[&payer, &account], + ) + .await + .unwrap(); + + // Step 4: Verify account exists before closing + let account_before_close = rpc.get_account(account.pubkey()).await.unwrap(); + assert!( + account_before_close.is_some(), + "Account should exist before closing" + ); + + let ctoken_state = CToken::deserialize(&mut &account_before_close.unwrap().data[..]).unwrap(); + assert_eq!(ctoken_state.amount, 0, "Account balance must be 0 to close"); + + // Step 5: Build close instruction using SDK builder + let close_instruction = CloseAccount::new( + CTOKEN_PROGRAM_ID, + account.pubkey(), + payer.pubkey(), // Destination for remaining lamports + owner, + ) + .instruction() + .unwrap(); + + // Step 6: Send close transaction + rpc.create_and_send_transaction(&[close_instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Step 7: Verify account is closed + let account_after_close = rpc.get_account(account.pubkey()).await.unwrap(); + assert!( + account_after_close.is_none(), + "Account should be closed and no longer exist" + ); +} +pub async fn create_compressed_mint( + rpc: &mut R, + payer: &Keypair, + decimals: u8, +) -> (Pubkey, [u8; 32]) { + let mint_signer = Keypair::new(); + let address_tree = rpc.get_address_tree_v2(); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; + + // Derive compression address + let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + &mint_signer.pubkey(), + &address_tree.tree, + ); + + let mint_pda = + light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + + // Get validity proof for the address + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![AddressWithTree { + address: compression_address, + tree: address_tree.tree, + }], + None, + ) + .await + .unwrap() + .value; + + // Build params + let params = CreateCMintParams { + decimals, + address_merkle_tree_root_index: rpc_result.addresses[0].root_index, + mint_authority: payer.pubkey(), + proof: rpc_result.proof.0.unwrap(), + compression_address, + mint: mint_pda, + freeze_authority: None, + extensions: None, + }; + + // Create instruction + let create_cmint = CreateCMint::new( + params, + mint_signer.pubkey(), + payer.pubkey(), + address_tree.tree, + output_queue, + ); + let instruction = create_cmint.instruction().unwrap(); + + // Send transaction + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) + .await + .unwrap(); + + (mint_pda, compression_address) +} +``` diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index f143f3a0..e3b0f9cb 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -43,7 +43,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c * For example, **SPL → c-Token** can be used for transfers from Alice's SPL token account to her own c-Token account. -* You can use this to **convert existing SPL tokens to c-Tokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the c-Token program](/compressed-token-program/ctoken/README). +* You can use this to **convert existing SPL tokens to c-Tokens** with **sponsored rent-exemption**. # Get Started @@ -59,6 +59,10 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c +1. Creates SPL mint, SPL token accounts, and mint SPL tokens +2. Sends SPL tokens to c-Token account. c-Tokens are minted. +3. Transfers c-Tokens to another c-Token account. + ### Prerequisites @@ -72,220 +76,8 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c - - -```rust -use borsh::BorshDeserialize; -use light_client::indexer::{AddressWithTree, Indexer}; -use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::{ - derive_ctoken_ata, CompressibleParams, CreateAssociatedTokenAccount2, CreateCMint, - CreateCMintParams, MintToCToken, MintToCTokenParams, TransferCtoken, -}; -use light_ctoken_types::instructions::mint_action::CompressedMintWithContext; -use light_ctoken_types::state::{CToken, CompressedMint}; -use light_program_test::{LightProgramTest, ProgramTestConfig}; -use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; - - -#[tokio::test(flavor = "multi_thread")] -async fn test_client_transfer_ctoken_to_ctoken() { - // Step 1: Setup test environment - let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) - .await - .unwrap(); - - let payer = rpc.get_payer().insecure_clone(); - let mint_authority = payer.pubkey(); - - // Step 2: Create compressed mint - let (mint, compression_address) = create_compressed_mint(&mut rpc, &payer, 9).await; - - // Step 3: Create sender cToken ATA - let sender = payer.pubkey(); - let (sender_ata, _) = derive_ctoken_ata(&sender, &mint); - - let create_sender_ata = CreateAssociatedTokenAccount2::new( - payer.pubkey(), - sender, - mint, - CompressibleParams::default(), - ) - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[create_sender_ata], &payer.pubkey(), &[&payer]) - .await - .unwrap(); +SPL Mint: SPLToken -> cToken and cToken -> cToken - // Step 4: Mint tokens to sender (1000 tokens) - let mint_amount = 1_000_000_000_000u64; // 1000 tokens with 9 decimals - - let compressed_mint_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - let rpc_result = rpc - .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) - .await - .unwrap() - .value; - - let compressed_mint = CompressedMint::deserialize( - &mut compressed_mint_account.data.unwrap().data.as_slice(), - ) - .unwrap(); - - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_mint_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: compressed_mint.try_into().unwrap(), - }; - - let mint_params = MintToCTokenParams::new( - compressed_mint_with_context, - mint_amount, - mint_authority, - rpc_result.proof, - ); - - let mint_instruction = MintToCToken::new( - mint_params, - payer.pubkey(), - compressed_mint_account.tree_info.tree, - compressed_mint_account.tree_info.queue, - compressed_mint_account.tree_info.queue, - vec![sender_ata], - ) - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[mint_instruction], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - - // Step 5: Create recipient cToken ATA - let recipient = Keypair::new(); - let (recipient_ata, _) = derive_ctoken_ata(&recipient.pubkey(), &mint); - - let create_recipient_ata = CreateAssociatedTokenAccount2::new( - payer.pubkey(), - recipient.pubkey(), - mint, - CompressibleParams::default(), - ) - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[create_recipient_ata], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - - // Step 6: Transfer tokens from sender to recipient - let transfer_amount = 500_000_000_000u64; // 500 tokens - - let transfer_instruction = TransferCtoken { - source: sender_ata, - destination: recipient_ata, - amount: transfer_amount, - authority: sender, - } - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[transfer_instruction], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - - // Step 7: Verify balances - let sender_account_data = rpc.get_account(sender_ata).await.unwrap().unwrap(); - let sender_state = CToken::deserialize(&mut &sender_account_data.data[..]).unwrap(); - - let recipient_account_data = rpc.get_account(recipient_ata).await.unwrap().unwrap(); - let recipient_state = CToken::deserialize(&mut &recipient_account_data.data[..]).unwrap(); - - assert_eq!( - sender_state.amount, - mint_amount - transfer_amount, - "Sender balance should be decreased" - ); - assert_eq!( - recipient_state.amount, transfer_amount, - "Recipient balance should be increased" - ); -} -pub async fn create_compressed_mint( - rpc: &mut R, - payer: &Keypair, - decimals: u8, -) -> (Pubkey, [u8; 32]) { - let mint_signer = Keypair::new(); - let address_tree = rpc.get_address_tree_v2(); - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Derive compression address - let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( - &mint_signer.pubkey(), - &address_tree.tree, - ); - - let mint_pda = - light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; - - // Get validity proof for the address - let rpc_result = rpc - .get_validity_proof( - vec![], - vec![AddressWithTree { - address: compression_address, - tree: address_tree.tree, - }], - None, - ) - .await - .unwrap() - .value; - - // Build params - let params = CreateCMintParams { - decimals, - address_merkle_tree_root_index: rpc_result.addresses[0].root_index, - mint_authority: payer.pubkey(), - proof: rpc_result.proof.0.unwrap(), - compression_address, - mint: mint_pda, - freeze_authority: None, - extensions: None, - }; - - // Create instruction - let create_cmint = CreateCMint::new( - params, - mint_signer.pubkey(), - payer.pubkey(), - address_tree.tree, - output_queue, - ); - let instruction = create_cmint.instruction().unwrap(); - - // Send transaction - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_signer]) - .await - .unwrap(); - - (mint_pda, compression_address) -} -``` - - ```rust use borsh::BorshDeserialize; @@ -465,8 +257,6 @@ async fn test_client_transfer_spl_to_ctoken_to_ctoken() { ); } ``` - - From 200e30fbb1843b6b4f86a75e42dabf00bbd6625b Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 01:14:26 +0000 Subject: [PATCH 067/143] Update navigation label and add page transition animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename 'PDAs' to 'Compressed PDAs' in docs.json navigation - Add fadeIn animation to page transitions in styles.css 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs.json | 2 +- styles.css | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs.json b/docs.json index 57259a1c..a1945fce 100644 --- a/docs.json +++ b/docs.json @@ -109,7 +109,7 @@ ] }, { - "group": "PDAs", + "group": "Compressed PDAs", "pages": [ "compressed-pdas/create-a-program-with-compressed-pdas", { diff --git a/styles.css b/styles.css index bc9f64f5..2121992c 100644 --- a/styles.css +++ b/styles.css @@ -154,4 +154,21 @@ [data-callout="error"] { background: rgba(239, 68, 68, 0.08) !important; border-color: rgba(239, 68, 68, 0.3) !important; +} + +/* Page Transition Animation */ +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* Target Mintlify's main content area */ +article, +[data-testid="content"], +main > div:first-child { + animation: fadeIn 0.6s ease-out; } \ No newline at end of file From ff351a7269d56e1d6b5ea6f1f472daac9998041d Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 01:22:46 +0000 Subject: [PATCH 068/143] Fix structure and numbering in create-cata.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove unused CompressibleRentCalculator import - Fix step numbering (was 1,2,2,3 now 1,2,3,4) - Reorganize Steps structure for better flow - Remove TODO comment about CompressibleParams 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../ctoken/create-cata.mdx | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index aba0fe2c..0a5254fc 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -5,7 +5,6 @@ description: Client and program guide to create associated c-Token accounts with import CTokenCreateATAAccountsList from '/snippets/accounts-list/ctoken-create-ata-accounts-list.mdx'; import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; -import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; import CTokenConfigureRent from '/snippets/ctoken-configure-rent.mdx'; import CAtaIntro from '/snippets/ctoken-guides/cata-intro.mdx'; import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; @@ -35,11 +34,22 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c -TODO: RM COMPRESSIBLE PARAMS + + + +### Prerequisites + + + + + + + +### Create c-ATA 1. The example creates a test c-Mint. You can use existing c-Mints, SPL or Token 2022 mints as well. 2. Derive the address from mint and owner pubkey. -2. Build the instruction with `CreateAssociatedTokenAccount2`. It automatically includes the default rent config: +3. Build the instruction with `CreateAssociatedTokenAccount2`. It automatically includes the default rent config: ```rust use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2; @@ -52,10 +62,9 @@ let instruction = CreateAssociatedTokenAccount2::new( .instruction()?; ``` -3. Send transaction & verify c-ATA creation with `get_account`.
+4. Send transaction & verify c-ATA creation with `get_account`.
c-ATAs are Solana accounts and use your familiar RPC methods. - ```rust use borsh::BorshDeserialize; use light_client::indexer::{AddressWithTree, Indexer}; @@ -170,21 +179,8 @@ pub async fn create_compressed_mint( (mint_pda, compression_address) } ``` - - - -### Prerequisites - - - - - - - -### Create c-ATA -
From a1d6aec029bb406553fff9308243ab1ea3c4ab45 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 02:17:48 +0000 Subject: [PATCH 069/143] Fix broken links in ctoken docs and snippets --- compressed-token-program/c-token-program.mdx | 4 ++-- compressed-token-program/cmint/README.mdx | 4 ++-- compressed-token-program/cmint/create-cmint.mdx | 2 +- compressed-token-program/cmint/mint-ctokens.mdx | 4 ++-- compressed-token-program/ctoken/create-cata.mdx | 2 +- compressed-token-program/ctoken/create-ctoken.mdx | 2 +- compressed-token-program/overview.mdx | 2 +- compressed-token-program/stash.mdx | 4 ---- landing.mdx | 2 +- learn/c-token-program.mdx | 4 ++-- snippets/overview-tables/ctoken-guides-table.mdx | 8 ++++---- 11 files changed, 17 insertions(+), 21 deletions(-) delete mode 100644 compressed-token-program/stash.mdx diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx index 0eea197b..f3827704 100644 --- a/compressed-token-program/c-token-program.mdx +++ b/compressed-token-program/c-token-program.mdx @@ -332,7 +332,7 @@ Compressed token accounts store token balance, owner, and other information like We recommend to use compressed tokens for **token distribution** or **storage of inactive tokens**. -**c-Token accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. +**c-Token accounts** with the **compressible extension are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. @@ -365,7 +365,7 @@ We recommend to use compressed tokens for **token distribution** or **storage of title=" Learn how to create cMints" icon="chevron-right" color="#0066ff" - href="/compressible/mint-actions" + href="/compressed-token-program/cmint/create-cmint" horizontal > \ No newline at end of file diff --git a/compressed-token-program/cmint/README.mdx b/compressed-token-program/cmint/README.mdx index 4632c92c..81c482b4 100644 --- a/compressed-token-program/cmint/README.mdx +++ b/compressed-token-program/cmint/README.mdx @@ -45,14 +45,14 @@ Follow our client or program guides for cMints. title="Client" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/cmint/client-guides/cmint" + href="/compressed-token-program/cmint/create-cmint" horizontal /> \ No newline at end of file diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index e66f6e05..a0040f61 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -623,7 +623,7 @@ pub fn process_create_cmint_with_pda_authority( title="Learn how to mint c-Tokens" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/program-mint-c-Token" + href="/compressed-token-program/cmint/mint-ctokens" horizontal /> diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index 70a5b569..9f783f98 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -535,7 +535,7 @@ pub fn process_mint_to_ctoken_invoke_signed( title="Client Guide" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/client-guides/client-mint-to-ctoken" + href="/compressed-token-program/ctoken/create-ctoken" horizontal > Mint c-Tokens @@ -544,7 +544,7 @@ pub fn process_mint_to_ctoken_invoke_signed( title="Program Guide" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/ctoken/program-guides/program-close-ctoken" + href="/compressed-token-program/ctoken/transfer-interface" horizontal > Transfer Interface c-Token diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index 0a5254fc..44ff1b07 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -377,6 +377,6 @@ pub fn process_create_ata2_invoke_signed( title="Mint c-Tokens" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/cmint/mint-ctoken" + href="/compressed-token-program/cmint/mint-ctokens" horizontal /> diff --git a/compressed-token-program/ctoken/create-ctoken.mdx b/compressed-token-program/ctoken/create-ctoken.mdx index 93e78035..80199a7c 100644 --- a/compressed-token-program/ctoken/create-ctoken.mdx +++ b/compressed-token-program/ctoken/create-ctoken.mdx @@ -373,6 +373,6 @@ pub fn process_create_token_account_invoke_signed( title="Mint c-Tokens" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/cmint/mint-ctoken" + href="/compressed-token-program/cmint/mint-ctokens" horizontal /> diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index b3e5f88e..d042abda 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -92,6 +92,6 @@ ADD TABLE OF github repository title="Create compressed token accounts" icon="chevron-right" color="#0066ff" - href="/compressed-token-program/compressed-tokens/basic-guides/how-to-create-compressed-token-accounts" + href="/compressed-tokens/guides/how-to-create-compressed-token-accounts" horizontal /> \ No newline at end of file diff --git a/compressed-token-program/stash.mdx b/compressed-token-program/stash.mdx deleted file mode 100644 index c6acfe7e..00000000 --- a/compressed-token-program/stash.mdx +++ /dev/null @@ -1,4 +0,0 @@ - -**SPL mints and tokens** owned by the [Token Program](https://github.com/solana-program/token) or [Token-2022](https://github.com/solana-program/token-2022) **require rent** by default.
-You can **transfer SPL token accounts to c-Token accounts** for **sponsored rent-exemption** while keeping the **same interoparability**. -
\ No newline at end of file diff --git a/landing.mdx b/landing.mdx index 5286f026..3d5ef519 100644 --- a/landing.mdx +++ b/landing.mdx @@ -240,7 +240,7 @@ codex mcp add deepwiki -- npx -y mcp-remote@latest https://mcp.deepwiki.com/mcp title="Quickstart: Mint Compressed Tokens in less than 5 minutes" icon="chevron-right" color="#0066ff" - href="/intro-pages/quickstart" + href="/quickstart" horizontal >
diff --git a/learn/c-token-program.mdx b/learn/c-token-program.mdx index 13ad8e8f..ec8827af 100644 --- a/learn/c-token-program.mdx +++ b/learn/c-token-program.mdx @@ -323,7 +323,7 @@ Compressed token accounts store token balance, owner, and other information like We recommend to use compressed tokens for **token distribution** or **storage of inactive tokens**. -**c-Token accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. +**c-Token accounts** with the **compressible extension are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. @@ -356,7 +356,7 @@ We recommend to use compressed tokens for **token distribution** or **storage of title=" Learn how to create cMints" icon="chevron-right" color="#0066ff" - href="/compressible/mint-actions" + href="/compressed-token-program/cmint/create-cmint" horizontal > \ No newline at end of file diff --git a/snippets/overview-tables/ctoken-guides-table.mdx b/snippets/overview-tables/ctoken-guides-table.mdx index 47bc4092..5f8af9ec 100644 --- a/snippets/overview-tables/ctoken-guides-table.mdx +++ b/snippets/overview-tables/ctoken-guides-table.mdx @@ -1,6 +1,6 @@ | | | | :---- | :---------- | -| [Create c-Token Accounts](/compressed-token-program/ctoken/program-guides/program-create-ctoken) | Uses `CompressibleParamsInfos` and `CreateCTokenAccountInfos` | -| [Create Associated c-Token Accounts](/compressed-token-program/ctoken/program-guides/program-create-c-ATA) | Uses `CompressibleParamsInfos` and `CreateAssociatedTokenAccountInfos` | -| [Transfer Interface](/compressed-token-program/ctoken/program-guides/program-transfer-interface) | For transfers between c-Token and SPL token accounts.
Uses `TransferInterface` and `with_spl_interface` | -| [Close c-Token Accounts](/compressed-token-program/ctoken/program-guides/program-close-ctoken) | Uses `CloseCTokenAccountInfos` | +| [Create c-Token Accounts](/compressed-token-program/ctoken/create-ctoken) | Uses `CompressibleParamsInfos` and `CreateCTokenAccountInfos` | +| [Create Associated c-Token Accounts](/compressed-token-program/ctoken/create-cata) | Uses `CompressibleParamsInfos` and `CreateAssociatedTokenAccountInfos` | +| [Transfer Interface](/compressed-token-program/ctoken/transfer-interface) | For transfers between c-Token and SPL token accounts.
Uses `TransferInterface` and `with_spl_interface` | +| [Close c-Token Accounts](/compressed-token-program/ctoken/close-ctoken-account) | Uses `CloseCTokenAccountInfos` | From 6d5a9ba29ca43c1afc9d1be9799f049be22dd4a5 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 02:21:26 +0000 Subject: [PATCH 070/143] Update ctoken creation costs in overview --- compressed-token-program/overview.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index d042abda..60dd5e65 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -20,8 +20,8 @@ import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx'; | Creation Cost | SPL | c-Token | |:---------------------|:------------------|:----------------------| -| **Mint Account** | 1,461,600 lamports | ~25,000 lamports | -| **Token Account** | 2,039,280 lamports | ~22,288 lamports | +| **Mint Account** | 1,461,600 lamports | 15,000 lamports | +| **Token Account** | 2,039,280 lamports | 22,288 lamports | @@ -76,13 +76,13 @@ Learn the core concepts to the [c-Token program here](/learn/c-token-program). ADD TABLE OF "FOR" -## Cookbook for Program and Clients +## Cookbook -## Examples for Program and Client +## Examples ADD TABLE OF github repository From cbebc62408587786bc0878bf9109022d7653139f Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 02:43:01 +0000 Subject: [PATCH 071/143] Update c-Token overview: refine costs and descriptions --- compressed-token-program/overview.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index 60dd5e65..b1971d2c 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -34,7 +34,7 @@ import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx'; ## c-Token Accounts * Hold token balances of c-Mints, SPL mints, or Token 2022 mints. * SPL tokens can be converted to
c-Tokens and back on demand. - * Custom rent config via compressible extension. + * Rent-exemption sponsorship reduces account creation cost.
@@ -42,7 +42,6 @@ import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx'; - Learn the core concepts to the [c-Token program here](/learn/c-token-program). From ce882ff71ae2b93ca6d1f56a89832606721f4337 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 03:25:47 +0000 Subject: [PATCH 072/143] Update landing page table: use lamports, add use cases --- compressed-token-program/overview.mdx | 4 ++-- landing.mdx | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index b1971d2c..382ebfd1 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -27,14 +27,14 @@ import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx'; ## c-Mint Accounts * Uniquely represent a token and store its global metadata. - * Mint's address serves as the token's unique identifier. + * Mint address serves as the token's unique identifier. * c-Mints are compressed accounts and rent-free. ## c-Token Accounts * Hold token balances of c-Mints, SPL mints, or Token 2022 mints. * SPL tokens can be converted to
c-Tokens and back on demand. - * Rent-exemption sponsorship reduces account creation cost. + * Custom rent config reduces account creation cost.
diff --git a/landing.mdx b/landing.mdx index 3d5ef519..3ba03267 100644 --- a/landing.mdx +++ b/landing.mdx @@ -1,18 +1,18 @@ --- title: Welcome to ZK Compression sidebarTitle: Introduction -description: ZK Compression is a Solana account primitive that lets you create tokens and PDAs without rent-exemption with L1 performance and security. +description: ZK Compression is a Solana account primitive that lets you create token mints, tokens and PDAs without rent-exemption with L1 performance and security. --- ![](/images/banner-image.png) -| | Solana Account | Compressed Account | Cost Reduction | -|:-------------------------|:------------------|:-----------------------|:----------------| -| 100-byte PDA Account | 0.0016 SOL | **~ 0.00001 SOL** | **160x** | -| 100 Token Accounts | ~ 0.2 SOL | **~ 0.00004 SOL** | **5000x** | - +| Creation Cost | Solana Accounts | ZK Compression | Use Case | +|:---------------------|:-------------------|:----------------|:--------------------| +| **100-byte PDA** | 1,600,000 lamports | 15,000 lamports | App State | +| **Mint Account** | 1,461,600 lamports | 15,000 lamports | Mints with Metadata | +| **Token Account** | 2,039,280 lamports | 22,288 lamports | Tokens | @@ -30,12 +30,12 @@ description: ZK Compression is a Solana account primitive that lets you create t - Create SPL-compatible tokens 5000x cheaper. + Create Mints with Metadata and Tokens with 98% less cost. Date: Fri, 5 Dec 2025 03:28:05 +0000 Subject: [PATCH 073/143] Add compressed tokens card to landing page features --- landing.mdx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/landing.mdx b/landing.mdx index 3ba03267..19a32633 100644 --- a/landing.mdx +++ b/landing.mdx @@ -28,7 +28,7 @@ description: ZK Compression is a Solana account primitive that lets you create t ## Features - + Store your app state in accounts with 98% less cost. + + Distribute Tokens with 99% less cost. + ## Quickstart From 0e5315d3571aadfd83dd64f8bb9e9967e15f3a20 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 03:32:13 +0000 Subject: [PATCH 074/143] Add compressed tokens link and reorder usage cards --- compressed-token-program/overview.mdx | 3 +++ compressed-tokens/README.mdx | 13 +++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index 382ebfd1..5fd8db74 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -38,6 +38,9 @@ import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx'; + +If you simply want to distribute tokens, please refer to [this page](/compressed-tokens/overview). + #### Rent Config for c-Tokens diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx index af691f37..1ea56d37 100644 --- a/compressed-tokens/README.mdx +++ b/compressed-tokens/README.mdx @@ -16,17 +16,18 @@ import { TokenAccountCompressedVsSpl } from '/snippets/jsx/token-account-compres ## Recommended Usage of Compressed Tokens + #### [Token Distribution](#advanced-guides) + * Distribute tokens without paying up front rent per recipient. + * Cost reduction for airdrops, rewards, etc: + + + + #### Storage of Inactive Token Accounts * Most (associated) token accounts are not frequently written to. * **Store token accounts rent-free** when inactive * [c-Tokens](/compressed-token-program/ctoken/README) are automatically compressed/decompressed, when active/inactive and include **sponsored rent-exemption**. [Learn more here](/compressed-token-program/ctoken/README). - - #### [Token Distribution](#advanced-guides) - * Distribute tokens without paying up front rent per recipient. - * Cost reduction for airdrops, payments, rewards, etc: - - Leading **wallets** like Phantom and Backpack **support compressed tokens**. The UI does not distinguish between SPL and compressed tokens. From 418a8d9f2ec990f81665cd3da431eecf5384d374 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 03:42:03 +0000 Subject: [PATCH 075/143] Fix broken links: update /compressed-tokens/overview to /compressed-tokens --- .../create-a-program-with-compressed-pdas.mdx | 7 +- compressed-token-program/overview.mdx | 2 +- compressed-tokens/overview.mdx | 77 ------------------- landing.mdx | 8 +- learn/ai-tools-guide.mdx | 2 +- learn/core-concepts/considerations.mdx | 2 +- quickstart.mdx | 2 +- resources/addresses-and-urls.mdx | 2 +- resources/cli-installation.mdx | 2 +- 9 files changed, 13 insertions(+), 91 deletions(-) delete mode 100644 compressed-tokens/overview.mdx diff --git a/compressed-pdas/create-a-program-with-compressed-pdas.mdx b/compressed-pdas/create-a-program-with-compressed-pdas.mdx index b480645b..ae0a2cc3 100644 --- a/compressed-pdas/create-a-program-with-compressed-pdas.mdx +++ b/compressed-pdas/create-a-program-with-compressed-pdas.mdx @@ -8,10 +8,9 @@ import DevelopmentEnvironmentSetup from '/snippets/setup/development-environment Compressed PDAs provide full functionality of accounts at PDAs, without per-account rent cost. -| Creation | Regular PDA Account | Compressed PDA | Cost Reduction | -| :------------- | :--------------------- | :---------------------- | :------------------ | -| 100-byte PDA | ~ 0.0016 SOL | **~ 0.00001 SOL** | ***160x*** | - +| Creation | Regular PDA Account | Compressed PDA | +| :------------- | :--------------------- | :---------------------- | +| 100-byte PDA | ~1,600,000 | 15,000 | Compressed PDAs are derived using a specific program address and seed, like regular PDAs. Custom programs invoke the Light System program to create and update accounts, instead of the System program. #### Compressed PDAs at a Glance diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/overview.mdx index 5fd8db74..52b014d1 100644 --- a/compressed-token-program/overview.mdx +++ b/compressed-token-program/overview.mdx @@ -39,7 +39,7 @@ import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx'; -If you simply want to distribute tokens, please refer to [this page](/compressed-tokens/overview). +If you simply want to distribute tokens, please refer to [this page](/compressed-tokens). #### Rent Config for c-Tokens diff --git a/compressed-tokens/overview.mdx b/compressed-tokens/overview.mdx deleted file mode 100644 index a1bae66f..00000000 --- a/compressed-tokens/overview.mdx +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: Overview -description: Compressed tokens provide full SPL token functionality without per-account rent cost. ---- - -import GuidesTable from '/snippets/overview-tables/compressed-tokens-guides-table.mdx'; -import AdvancedGuidesTable from '/snippets/overview-tables/compressed-tokens-advanced-guides-table.mdx'; -import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; -import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; - -| Creation | Regular SPL Token | Compressed Token | Cost Reduction | -|:---------------------|:------------------|:----------------------|:---------------| -| 100 Token Accounts | ~ 0.2 SOL | **~ 0.00004 SOL** | **5000x** | - -Compressed token accounts store information about an individual's ownership of a specific token (mint). Different from regular token accounts, they don't require an Associated Token Account (ATA) per token holder. - -For example, this simplifies [token distribution](/compressed-tokens/advanced-guides/create-an-airdrop), since you don't need to allocate a token account per recipient. - -#### Compressed Tokens at a Glance - - - - Create token accounts without upfront rent exempt balance. - - - Compatible with SPL tokens and Solana programs. - - - Supported by leading wallets including Phantom and Backpack. - - - -# Start building - -Developing with compressed tokens works similar SPL tokens and involves minimal setup: - - - - -### Install dependencies - - - - - - -### Set up your developer environment - - - - - - - -### Get started - - - -## Guides - - - - -## Advanced Guides - - - - -# Next Steps - - \ No newline at end of file diff --git a/landing.mdx b/landing.mdx index 19a32633..cb8bfcbc 100644 --- a/landing.mdx +++ b/landing.mdx @@ -10,9 +10,9 @@ description: ZK Compression is a Solana account primitive that lets you create t | Creation Cost | Solana Accounts | ZK Compression | Use Case | |:---------------------|:-------------------|:----------------|:--------------------| -| **100-byte PDA** | 1,600,000 lamports | 15,000 lamports | App State | -| **Mint Account** | 1,461,600 lamports | 15,000 lamports | Mints with Metadata | -| **Token Account** | 2,039,280 lamports | 22,288 lamports | Tokens | +| **100-byte PDA** | ~1,600,000 lamports | 15,000 lamports | App State | +| **Mint Account** | ~1,500,600 lamports | 15,000 lamports | Mints with Metadata | +| **Token Account** | ~2,039,280 lamports | 22,288 lamports | Tokens | @@ -47,7 +47,7 @@ description: ZK Compression is a Solana account primitive that lets you create t diff --git a/learn/ai-tools-guide.mdx b/learn/ai-tools-guide.mdx index 1484f08d..4463958a 100644 --- a/learn/ai-tools-guide.mdx +++ b/learn/ai-tools-guide.mdx @@ -185,7 +185,7 @@ Start testing your AI tools with compressed tokens or PDAs. title="Compressed Tokens" icon="chevron-right" color="#0066ff" - href="/compressed-tokens/overview" + href="/compressed-tokens" /> diff --git a/resources/addresses-and-urls.mdx b/resources/addresses-and-urls.mdx index f38f3364..5bb9b0ac 100644 --- a/resources/addresses-and-urls.mdx +++ b/resources/addresses-and-urls.mdx @@ -178,7 +178,7 @@ Start building with Compressed Tokens or PDAs title="Compressed Tokens" icon="chevron-right" color="#0066ff" - href="/compressed-tokens/overview" + href="/compressed-tokens" horizontal /> Date: Fri, 5 Dec 2025 14:18:07 +0000 Subject: [PATCH 076/143] add persistent banner --- .windsurf/rules/mint.md | 128 -------------------------------------- docs.json | 4 ++ landing.mdx | 7 ++- learn/c-token-program.mdx | 46 +++++++------- 4 files changed, 31 insertions(+), 154 deletions(-) delete mode 100644 .windsurf/rules/mint.md diff --git a/.windsurf/rules/mint.md b/.windsurf/rules/mint.md deleted file mode 100644 index ed84568a..00000000 --- a/.windsurf/rules/mint.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -trigger: always_on ---- - ---- -name: light-protocol-documentation-writer -description: Configures Claude as a pragmatic technical writer following strict style guides, frontmatter requirements, and Git workflow rules for MDX documentation. Use PROACTIVELY when working on technical documentation, MDX files, content strategy, or documentation structure tasks. -allowed-tools: [read, edit, glob, grep, mcp__deepwiki__read_wiki_structure, mcp__deepwiki__read_wiki_contents, mcp__deepwiki__ask_question] ---- - -You are an experienced, pragmatic technical writer with robust content strategy and content design experience. You elegantly create just enough docs to solve users' needs and get them back to the product quickly. - -Rule #1: If you want an exception to ANY rule, YOU MUST STOP and get explicit permission from Ethan first. BREAKING THE LETTER OR SPIRIT OF THE RULES IS FAILURE. - -## Working relationship - -- We're colleagues working together your name is "Windie" -- You can push back on ideas-this can lead to better documentation. Cite sources and explain your reasoning when you do so -- ALWAYS ask for clarification rather than making assumptions -- NEVER lie, guess, or make up information -- You are much better read than I am. I have more nuanced understanding about our users. We work together to solve problems with our combined strengths. -- When you disagree with my approach, YOU MUST push back, citing specific reasons if you have them. -- YOU MUST call out bad ideas, unreasonable expectations, and mistakes. -- NEVER be agreeable just to be nice - I need your honest technical judgment. -- NEVER tell me I'm "absolutely right" or anything like that. You ARE NOT a sycophant. -- We can be humorous and playful, but not when it gets in the way of the task at hand. Save it for when a project is finished or we need levity during a tough project. -- YOU MUST ALWAYS ask for clarification rather than making assumptions. -- If you're having trouble, YOU MUST STOP and ask for help, especially for tasks where human input would be valuable. -- If you are making an inferrance, stop and ask me for confirmation or say that you need more information - -## Project context -- Format: MDX files with YAML frontmatter -- Config: docs.json for navigation, theme, settings - - See the docs.json schema: https://mintlify.com/docs.json -- Components reference: - - Quick reference: mintlify-docs/quick-reference/ - - All components: mintlify-docs/docs/components/ -- Directory structure: - - client-library/ - Client library documentation (TypeScript and Rust) - - c-token/ - Compressed token documentation - - mintlify-docs/ - Local Mintlify documentation (gitignored) - - images/ - Image assets - - logo/ - Logo files - -## Content strategy -- We document just enough so that users are successful. Too much content makes it hard to find what people are looking for. Too little makes it too challenging to accomplish users' goals. -- Prioritize accuracy and usability of information -- Make content evergreen when possible -- Search for existing information before adding new content. Avoid duplication unless it is done for a strategic reason -- Check existing patterns for consistency -- Start by making the smallest reasonable changes - -## Frontmatter requirements for pages -- title: Clear, descriptive page title -- description: Concise summary for SEO/navigation - -## Writing standards -- Second-person voice ("you") -- Prerequisites at start of procedural content -- Test all code examples before publishing -- Match style and formatting of existing pages -- Include both basic and advanced use cases -- Language tags on all code blocks -- Alt text on all images -- Relative paths for internal links -- Use broadly applicable examples rather than overly specific business cases -- Lead with context when helpful - explain what something is before diving into implementation details -- Use sentence case for all headings ("Getting started", not "Getting Started") -- Use sentence case for code block titles ("Expandable example", not "Expandable Example") -- Prefer active voice and direct language -- Remove unnecessary words while maintaining clarity -- Break complex instructions into clear numbered steps -- Make language more precise and contextual -- Use [Lucide](https://lucide.dev) icon library - -### Language and tone standards -- **Avoid promotional language**: Never use phrases like "rich heritage," "breathtaking," "captivates," "stands as a testament," "plays a vital role,""enables","comprehensive" or similar marketing language in technical documentation -- **Reduce conjunction overuse**: Limit use of "moreover," "furthermore," "additionally," "on the other hand" - favor direct, clear statements -- **Avoid editorializing**: Remove phrases like "it's important to note," "this article will," "in conclusion," or personal interpretations -- **No undue emphasis**: Avoid overstating importance or significance of routine technical concepts - -### Technical accuracy standards -- **Verify all links**: Every external reference must be tested and functional before publication -- **Use precise citations**: Replace vague references with specific documentation, version numbers, and accurate sources -- **Maintain consistency**: Use consistent terminology, formatting, and language variety throughout all documentation -- **Valid technical references**: Ensure all code examples, API references, and technical specifications are current and accurate - -### Formatting discipline - -- **Purposeful formatting**: Use bold, italics, and emphasis only when it serves the user's understanding, not for visual appeal -- **Clean structure**: Avoid excessive formatting, emoji, or decorative elements that don't add functional value -- **Standard heading case**: Use sentence case for headings unless project style guide specifies otherwise -- **Minimal markup**: Keep formatting clean and functional, avoiding unnecessary markdown or styling - -### Component introductions -- Start with action-oriented language: "Use [component] to..." rather than "The [component] component..." -- Be specific about what components can contain or do -- Make introductions practical and user-focused - -### Property descriptions -- End all property descriptions with periods for consistency -- Be specific and helpful rather than generic -- Add scope clarification where needed (e.g., "For Font Awesome icons only:") -- Use proper technical terminology ("boolean" not "bool") - -### Code examples -- Keep examples simple and practical -- Use consistent formatting and naming -- Provide clear, actionable examples rather than showing multiple options when one will do - -## Content organization -- Structure content in the order users need it -- Combine related information to reduce redundancy -- Use specific links (direct to relevant pages rather than generic dashboards) -- Put most commonly needed information first - -## Git workflow -- NEVER use --no-verify when committing -- Ask how to handle uncommitted changes before starting -- Create a new branch when no clear branch exists for changes -- Commit frequently throughout development -- NEVER skip or disable pre-commit hooks - -## Do not -- Skip frontmatter on any MDX file -- Use absolute URLs for internal links -- Include untested code examples -- Make assumptions - always ask for clarification diff --git a/docs.json b/docs.json index a1945fce..4cfe918f 100644 --- a/docs.json +++ b/docs.json @@ -11,6 +11,10 @@ "appearance": { "default": "light" }, + "banner": { + "content": "c-Token is live on Devnet. Start testing and create mints or tokens without rent-exemption!", + "dismissible": false + }, "navigation": { "groups": [ { diff --git a/landing.mdx b/landing.mdx index cb8bfcbc..05e03fa5 100644 --- a/landing.mdx +++ b/landing.mdx @@ -8,11 +8,12 @@ description: ZK Compression is a Solana account primitive that lets you create t ![](/images/banner-image.png) -| Creation Cost | Solana Accounts | ZK Compression | Use Case | +| Creation Cost | Solana | Compression | Use Case | |:---------------------|:-------------------|:----------------|:--------------------| -| **100-byte PDA** | ~1,600,000 lamports | 15,000 lamports | App State | | **Mint Account** | ~1,500,600 lamports | 15,000 lamports | Mints with Metadata | | **Token Account** | ~2,039,280 lamports | 22,288 lamports | Tokens | +| **100-byte PDA** | ~1,600,000 lamports | 15,000 lamports | App State | + @@ -30,7 +31,7 @@ description: ZK Compression is a Solana account primitive that lets you create t @@ -21,7 +21,7 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t - **[cMint](#cmint-accounts)** + **[c-Mint](#cmint-accounts)** Compressed account
    @@ -35,7 +35,7 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t Solana account
      -
    • **Store tokens** of **cMints, SPL mints, or Token 2022 mints**
    • +
    • **Store tokens** of **c-Mints, SPL mints, or Token 2022 mints**
    • **Sponsored rent** exemption via [compressible extension](#compressible)
    • Use for **active token accounts** with frequent writes (trading, etc.)
    @@ -56,16 +56,16 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t -# cMint Accounts +# c-Mint Accounts -* **cMints are compressed accounts** and **cannot be decompressed**. -* SPL mints can not be compressed to cMints. +* **c-Mints are compressed accounts** and **cannot be decompressed**. +* SPL mints can not be compressed to c-Mints. -cMints **uniquely represent a token on Solana and store its global metadata**, similar to SPL mint accounts with few core differences: -1. cMint accounts are **rent-free**. -2. Tokens created from cMints are **c-Tokens**. +c-Mints **uniquely represent a token on Solana and store its global metadata**, similar to SPL mint accounts with few core differences: +1. c-Mint accounts are **rent-free**. +2. Tokens created from c-Mints are **c-Tokens**. 3. Token metadata (name, symbol, URI) is stored as an extension in the struct. @@ -73,7 +73,7 @@ cMints **uniquely represent a token on Solana and store its global metadata**, s Diagram showing cMint compressed account structure with three components: Hash (identifier for cMint in purple box), Account (struct containing BaseMint with SPL-compatible fields, cMint Data for program state, and optional Extensions for Token Metadata), and BasemintData (containing Supply, Decimals, Mint Authority, and Freeze Authority fields) with Token Metadata extension @@ -82,7 +82,7 @@ cMints **uniquely represent a token on Solana and store its global metadata**, s pub struct CompressedMint { // SPL mint layout pub base: BaseMint, - // cMint state used by the Compressed Token Program + // c-Mint state used by the Compressed Token Program pub metadata: CompressedMintMetadata // Field for Token Metadata extension pub extensions: Option>, @@ -92,14 +92,14 @@ cMints **uniquely represent a token on Solana and store its global metadata**, s -Find the [source code of cMints here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/mint/compressed_mint.rs). +Find the [source code of c-Mints here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/mint/compressed_mint.rs). -The `metadata` field is used by the Compressed Token Program to store the internal state of a cMint. +The `metadata` field is used by the Compressed Token Program to store the internal state of a c-Mint. The `BaseMint` field replicates the field layout and serialization format of [SPL Mint accounts](https://solana.com/docs/tokens#mint-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens and mints. -Here is how cMints and SPL mints compare: +Here is how c-Mints and SPL mints compare: @@ -107,7 +107,7 @@ Here is how cMints and SPL mints compare: Field - cMint + c-Mint SPL Mint @@ -138,7 +138,7 @@ Here is how cMints and SPL mints compare: ✓ - cMint Data + c-Mint Data ✓ - @@ -178,9 +178,9 @@ Here is how cMints and SPL mints compare: A c-Token account holds token balances like SPL Token accounts: -* A wallet needs a c-Token account for each cMint it wants to hold, with the wallet address set as the c-Token account owner. -* Each wallet can own multiple c-Token accounts for the same cMint. -* A c-Token account can only have one owner and hold units of one cMint. +* A wallet needs a c-Token account for each c-Mint it wants to hold, with the wallet address set as the c-Token account owner. +* Each wallet can own multiple c-Token accounts for the same c-Mint. +* A c-Token account can only have one owner and hold units of one c-Mint. @@ -300,14 +300,14 @@ This extension makes c-Token accounts 200x cheaper than SPL token accounts # Associated c-Token Account **Associated c-Token** accounts (c-ATAs) follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): -* Each wallet needs its own c-Token account to hold tokens from the same cMint. +* Each wallet needs its own c-Token account to hold tokens from the same c-Mint. * The address for c-ATAs is deterministically derived with the owner’s address, c-Token program ID, and mint address. ```rust let seeds = [ owner.as_ref(), // Wallet address (32 bytes) program_id.as_ref(), // Compressed Token Program ID (32 bytes) - mint.as_ref(), // cMint address (32 bytes) + mint.as_ref(), // c-Mint address (32 bytes) bump.as_ref(), // Bump seed (1 byte) ]; ``` @@ -353,7 +353,7 @@ We recommend to use compressed tokens for **token distribution** or **storage of # Next Steps Date: Fri, 5 Dec 2025 15:04:23 +0000 Subject: [PATCH 077/143] update landing. hide extensions (will be on devnet in 2 weeks or so) --- .windsurf/workflows/ask-deepwiki.md | 4 +- .../{overview.mdx => README.mdx} | 0 compressed-token-program/c-token-program.mdx | 371 ------------------ compressed-token-program/cmint/README.mdx | 2 +- .../cmint/create-cmint.mdx | 9 +- compressed-token-program/ctoken/README.mdx | 139 ------- .../ctoken/extensions.mdx | 1 + compressed-tokens/README.mdx | 2 +- docs.json | 9 +- landing.mdx | 4 +- .../transfer-interface-intro.mdx | 2 +- 11 files changed, 17 insertions(+), 526 deletions(-) rename compressed-token-program/{overview.mdx => README.mdx} (100%) delete mode 100644 compressed-token-program/c-token-program.mdx delete mode 100644 compressed-token-program/ctoken/README.mdx diff --git a/.windsurf/workflows/ask-deepwiki.md b/.windsurf/workflows/ask-deepwiki.md index 012cc54c..889bdb23 100644 --- a/.windsurf/workflows/ask-deepwiki.md +++ b/.windsurf/workflows/ask-deepwiki.md @@ -15,7 +15,7 @@ Query the Light Protocol repository via DeepWiki MCP with precise technical answ **MANDATORY STEPS BEFORE EXECUTION:** 1. Read this complete file -2. Read [Global CLAUDE.md](/home/tilo/.claude/CLAUDE.md) +2. Read Global CLAUDE.md 3. Read terminology reference for precision rules **VERIFICATION CHECKLIST:** @@ -23,7 +23,7 @@ Query the Light Protocol repository via DeepWiki MCP with precise technical answ - [ ] Are you familiar with precision rules (avoid vague verbs)? - [ ] Will you provide file:line references? -**Navigation**: [Global README](/home/tilo/.claude/README.md) | [Commands](/home/tilo/.claude/commands/) | [Global CLAUDE.md](/home/tilo/.claude/CLAUDE.md) +**Navigation**: Global README | Commands | Global CLAUDE.md ## Command Process diff --git a/compressed-token-program/overview.mdx b/compressed-token-program/README.mdx similarity index 100% rename from compressed-token-program/overview.mdx rename to compressed-token-program/README.mdx diff --git a/compressed-token-program/c-token-program.mdx b/compressed-token-program/c-token-program.mdx deleted file mode 100644 index f3827704..00000000 --- a/compressed-token-program/c-token-program.mdx +++ /dev/null @@ -1,371 +0,0 @@ ---- -title: Compressed Token Program -sidebarTitle: Overview New -description: Overview of cMints, c-Tokens, and compressed token accounts. ---- - -import CompressibleVsSolanaRent from '/snippets/compressible-vs-solana-rent.mdx'; -import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; -import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; -import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; - - -The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/tree/main/programs/compressed-token) extends existing Solana token standards with cMints, c-Tokens and compressed tokens. - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Account TypeKey Features
    **[cMint](#cmint-accounts)**Compressed account -
      -
    • **Rent-free mint accounts** (similar to SPL mints)
    • -
    • Custom [Token Metadata](#token-metadata) **extension**
    • -
    -
    **[c-Token](#ctoken-account)**Solana account -
      -
    • **Store tokens** of **cMints, SPL mints, or Token 2022 mints**
    • -
    • **Sponsored rent** exemption via [compressible extension](#compressible)
    • -
    • Use for **active token accounts** with frequent writes (trading, etc.)
    • -
    -
    **[Compressed Token](#compressed-token-account)**Compressed account -
      -
    • **Compressed account** with `TokenData` field
    • -
    • **Rent-free** and SPL-compatible
    • -
    • Use for **storage of inactive tokens** and **token distribution**
    • -
    • c-Token accounts are automatically compressed/decompressed when active/inactive.
    • -
    -
    - -_add graphic of all tokens here_ - - - -**SPL mints and tokens** owned by the [Token Program](https://github.com/solana-program/token) or [Token-2022](https://github.com/solana-program/token-2022) **require rent** by default.
    -You can **migrate SPL token accounts to c-Tokens** with compressible extension for **sponsored rent-exemption** while keeping the **same interoparability**. -
    - -# cMint Accounts - - -* **cMints are compressed accounts** and **cannot be decompressed**. -* SPL mints can not be compressed to cMints. - - -cMints **uniquely represent a token on Solana and store its global metadata**, similar to SPL mint accounts with few core differences: -1. cMint accounts are **rent-free**. -2. Tokens created from cMints are **c-Tokens** (fully compatible with SPL tokens). -3. Token metadata (name, symbol, URI) is stored as an extension within the compressed mint account. - - - - - Diagram showing cMint compressed account structure with three components: Hash (identifier for cMint in purple box), Account (struct containing BaseMint with SPL-compatible fields, cMint Data for program state, and optional Extensions for Token Metadata), and BasemintData (containing Supply, Decimals, Mint Authority, and Freeze Authority fields) with Token Metadata extension - - - - ```rust - pub struct CompressedMint { - // SPL mint layout - pub base: BaseMint, - // cMint state used by the Compressed Token Program - pub metadata: CompressedMintMetadata - // Field for Token Metadata extension - pub extensions: Option>, - } - ``` - - - - -Find the [source code of cMints here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/mint/compressed_mint.rs). - - -The `metadata` field is used by the Compressed Token Program to store the internal state of a cMint. - -The `BaseMint` field replicates the field layout and serialization format of [SPL Mint accounts](https://solana.com/docs/tokens#mint-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens and mints. - -Here is how cMints and SPL mints compare: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FieldcMintSPL Mint
    mint_authority
    supply
    decimals
    is_initialized
    freeze_authority
    cMint Data-
    Extensionsvia Token-2022
    -
    - - ```rust - pub struct BaseMint { - /// Optional authority used to mint new tokens. The mint authority may only - /// be provided during mint creation. If no mint authority is present - /// then the mint has a fixed supply and no further tokens may be - /// minted. - pub mint_authority: Option, - /// Total supply of tokens. - pub supply: u64, - /// Number of base 10 digits to the right of the decimal place. - pub decimals: u8, - /// Is initialized - for SPL compatibility - pub is_initialized: bool, - /// Optional authority to freeze token accounts. - pub freeze_authority: Option, - } - ``` - -
    - -# c-Token Account - - -**c-Token accounts are Solana accounts**, not compressed accounts. - - -A c-Token account holds token balances like SPL Token accounts: -* A wallet needs a c-Token account for each cMint it wants to hold, with the wallet address set as the c-Token account owner. -* Each wallet can own multiple c-Token accounts for the same cMint. -* A c-Token account can only have one owner and hold units of one cMint. - - - - - Diagram showing c-Token Solana account structure with three components: Address (identifier for c-Token account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Owner set to Compressed Token Program), and AccountData (containing Mint, Owner, Amount, and Extensions fields) - - - - ```rust - /// Ctoken account structure (with extensions). - pub struct CToken { - pub mint: Pubkey, - pub owner: Pubkey, - pub amount: u64, - pub delegate: Option, // instruction not implemented yet - pub state: u8, - pub is_native: Option, - pub delegated_amount: u64, // instruction not implemented yet - pub close_authority: Option, - pub extensions: Option>, // Optional extensions e.g. compressible - } - ``` - - - - -Find the [source code of c-Token here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken_struct.rs). - - -c-Token accounts replicate the field layout and serialization format of [SPL Token accounts](https://solana.com/docs/tokens#token-account). The struct is serialized with Borsh to match the on-chain format of SPL tokens. - -Here is how c-Tokens and SPL tokens compare: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Fieldc-TokenSPL Token Account
    mint
    owner
    amount
    delegateunimplemented
    state
    is_native
    delegated_amountunimplemented
    close_authority
    extensionsvia Token-2022
    - -### Compressible Extension - -c-Token accounts are compressible: - - - -This extension makes c-Token accounts 200x cheaper than SPL token accounts - - | | c-Token | SPL | - |-----------|--------|-----| - | allocate | 0.000011 | 0.002 | - | rent for 24h | 0.000011 | - | - - - - - - - - - - - - -# Associated c-Token Account - -**Associated c-Token** accounts (c-ATAs) follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA): -* Each wallet needs its own c-Token account to hold tokens from the same cMint. -* The address for c-ATAs is deterministically derived with the owner’s address, c-Token program ID, and mint address. - -```rust -let seeds = [ - owner.as_ref(), // Wallet address (32 bytes) - program_id.as_ref(), // Compressed Token Program ID (32 bytes) - mint.as_ref(), // cMint address (32 bytes) - bump.as_ref(), // Bump seed (1 byte) -]; -``` - - -Find the [source code to associated c-Token accounts here](https://github.com/Lightprotocol/light-protocol/blob/main/programs/compressed-token/program/src/create_associated_token_account.rs). - - -# Compressed Token Account - -Compressed token accounts store token balance, owner, and other information like SPL and c-Tokens. Any c-Token or SPL token can be compressed/decompressed at will. - -We recommend to use compressed tokens for **token distribution** or **storage of inactive tokens**. - - -**c-Token accounts** with the **compressible extension are automatically compressed** with no writes in 27,000 slots (3h) and decompressed with new writes. - - - - - - Diagram showing compressed token account structure with three components: Hash (identifier for compressed token account in purple box), Account (struct containing Data bytes, Executable flag, Lamports balance, and Address set to None), and AccountData (containing Mint, Owner, Amount, and Extensions fields marked as unimplemented) - - - - ```rust - pub struct TokenData { - pub mint: Pubkey, - pub owner: Pubkey, - pub amount: u64, - pub delegate: Option, - pub state: u8, - /// Placeholder for TokenExtension tlv data (unimplemented) - pub tlv: Option>, - } - ``` - - - -# Next Steps - - - \ No newline at end of file diff --git a/compressed-token-program/cmint/README.mdx b/compressed-token-program/cmint/README.mdx index 81c482b4..ee7ba3ca 100644 --- a/compressed-token-program/cmint/README.mdx +++ b/compressed-token-program/cmint/README.mdx @@ -13,7 +13,7 @@ import { SolanaRentCalculator } from '/snippets/jsx/solana-rent-calculator.jsx'; |-----------|--------|-----| | creation cost | 25,000 lamports | 1,461,600 lamports | -Learn the [core concepts to the c-Token Program here](/compressed-token-program/c-token-program). +Learn the [core concepts to the c-Token Program here](/compressed-token-program/README.mdx). # Get Started diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index a0040f61..e744e957 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -12,7 +12,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c 3. Tokens created from c-Mints are c-Tokens. -Learn the [core concepts to the c-Token Program here](/compressed-token-program/c-token-program). +Learn the [core concepts to the c-Token Program here](/compressed-token-program/README.mdx). # Get Started @@ -30,12 +30,13 @@ Learn the [core concepts to the c-Token Program here](/compressed-token-program/ The example creates a c-Mint with token metadata. 1. Derive the mint address and -fetch a validity proof from your RPC that proves the address does not exist yet. +fetch a validity proof from your RPC that proves the address does not exist yet. TODO: ADD TOOLTIP TO VALIDITY PROOF -3. Define your Token Metadata (name, symbol, URI, additional metadata) -4. Build the instruction with `CreateCMint::new()`: +3. Configure mint and your token metadata (name, symbol, URI, additional metadata) +4. Build the instruction with `CreateCMint::new()`. + ```rust use light_compressed_token_sdk::ctoken::CreateCMint; diff --git a/compressed-token-program/ctoken/README.mdx b/compressed-token-program/ctoken/README.mdx deleted file mode 100644 index c466ff73..00000000 --- a/compressed-token-program/ctoken/README.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Overview -description: "Overview to c-Tokens and reference to client guides, program guides, and example repository." ---- -import { CTokenVsSplCalculator } from '/snippets/jsx/ctoken-vs-spl-calculator.jsx'; -import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; -import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; -import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; -import CTokenProgramGuidesTable from '/snippets/overview-tables/ctoken-program-guides-table.mdx'; - -1. c-Token & associated c-Token accounts (c-ATAs) are **Solana accounts** that
    **hold balances** of **cMint, SPL mint, or Token 2022 mint** tokens. -2. c-Token accounts and c-ATAs are **compressible** that reduces account creation cost. - -| | c-Token account | SPL token account | -|-----------|--------|-----| -| **Creation Cost** | 22,288 lamports | 2,039,280 lamports | - -## Compressible Rent - -For c-Tokens and c-ATAs, the compressible extension implements a **custom rent config**: - - - -### Example -```markdown expandable -┌─────────────┐ -│ Created │ -│ (24h rent) │ -├─────────────┤ -│ Active │ -└──────┬──────┘ - │ T+0 - ▼ -┌─────────────┐ -│ Transfer │ -│ (16h rent) │ -│ no top-up │ -├─────────────┤ -│ Active │ -└──────┬──────┘ - │ T+8h - ▼ -┌─────────────┐ -│ Transfer │ -│ (10h rent) │ -│ no top-up │ -├─────────────┤ -│ Active │ -└──────┬──────┘ - │ T+14h - ▼ -┌─────────────┐ -│ Transfer │ -│(4h→7h rent) │ -│ top-up │ -├─────────────┤ -│ Active │ -└──────┬──────┘ - │ T+20h - ▼ -┌─────────────┐ -│ Transfer │ -│ (5h rent) │ -│ no top-up │ -├─────────────┤ -│ Active │ -└──────┬──────┘ - │ T+22h - ▼ -┌─────────────┐ -│ No activity │ -│ (1h rent) │ -├─────────────┤ -│Compressible │ -└──────┬──────┘ - │ T+28h - ▼ -┌─────────────┐ -│ Compressed │ -│ — │ -├─────────────┤ -│ Inactive │ -└──────┬──────┘ - │ T+29h - ▼ -┌─────────────┐ -│ Transfer │ -│ (3h rent) │ -│ top-up │ -├─────────────┤ -│ Reactivated │ -└──────┬──────┘ - │ T+32h - ▼ -┌─────────────┐ -│ No activity │ -│ (1h rent) │ -├─────────────┤ -│Compressible │ -└──────┬──────┘ - │ T+34h - ▼ -┌─────────────┐ -│ Compressed │ -│ — │ -├─────────────┤ -│ Inactive │ -└─────────────┘ - T+35h -``` - -## Get started - -The guides and examples use the `light_compressed_token_sdk`. - - - -### Guides - - - - - - -### Examples - -add github link - - - -## Next Steps - - \ No newline at end of file diff --git a/compressed-token-program/ctoken/extensions.mdx b/compressed-token-program/ctoken/extensions.mdx index 368d420a..42590d04 100644 --- a/compressed-token-program/ctoken/extensions.mdx +++ b/compressed-token-program/ctoken/extensions.mdx @@ -1,6 +1,7 @@ --- title: Extensions description: Overview to Token 2022 extensions supported by c-Tokens. +hidden: true --- diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx index 1ea56d37..ea39493e 100644 --- a/compressed-tokens/README.mdx +++ b/compressed-tokens/README.mdx @@ -26,7 +26,7 @@ import { TokenAccountCompressedVsSpl } from '/snippets/jsx/token-account-compres #### Storage of Inactive Token Accounts * Most (associated) token accounts are not frequently written to. * **Store token accounts rent-free** when inactive - * [c-Tokens](/compressed-token-program/ctoken/README) are automatically compressed/decompressed, when active/inactive and include **sponsored rent-exemption**. [Learn more here](/compressed-token-program/ctoken/README). + * [c-Tokens](/compressed-token-program/README.mdx) are automatically compressed/decompressed, when active/inactive and include **sponsored rent-exemption**. [Learn more here](/compressed-token-program/README.mdx). Leading **wallets** like Phantom and Backpack **support compressed tokens**. The UI does not distinguish between SPL and compressed tokens. diff --git a/docs.json b/docs.json index 4cfe918f..548697e5 100644 --- a/docs.json +++ b/docs.json @@ -12,7 +12,7 @@ "default": "light" }, "banner": { - "content": "c-Token is live on Devnet. Start testing and create mints or tokens without rent-exemption!", + "content": "c-Token is live on Devnet. Start testing and create mints or tokens without rent-exemption!", "dismissible": false }, "navigation": { @@ -26,9 +26,9 @@ ] }, { - "group": "c-Token", + "group": "c-Token (on Devnet)", "pages": [ - "compressed-token-program/overview", + "compressed-token-program/README", { "group": "Cookbook", "pages": [ @@ -64,8 +64,7 @@ "compressed-token-program/integrate/for-wallet-applications", "compressed-token-program/integrate/for-payment-companies" ] - }, - "compressed-token-program/ctoken/extensions" + } ] }, { diff --git a/landing.mdx b/landing.mdx index 05e03fa5..6af9f77f 100644 --- a/landing.mdx +++ b/landing.mdx @@ -8,7 +8,7 @@ description: ZK Compression is a Solana account primitive that lets you create t ![](/images/banner-image.png) -| Creation Cost | Solana | Compression | Use Case | +| Creation Cost | Solana | ZK Compression | Use Case | |:---------------------|:-------------------|:----------------|:--------------------| | **Mint Account** | ~1,500,600 lamports | 15,000 lamports | Mints with Metadata | | **Token Account** | ~2,039,280 lamports | 22,288 lamports | Tokens | @@ -32,7 +32,7 @@ description: ZK Compression is a Solana account primitive that lets you create t diff --git a/snippets/ctoken-guides/transfer-interface-intro.mdx b/snippets/ctoken-guides/transfer-interface-intro.mdx index 063240ca..702dff0d 100644 --- a/snippets/ctoken-guides/transfer-interface-intro.mdx +++ b/snippets/ctoken-guides/transfer-interface-intro.mdx @@ -32,5 +32,5 @@ * For example, **SPL → c-Token** can be used for transfers from Alice's SPL token account to her own c-Token account. -* You can use this to **convert existing SPL tokens to c-Tokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the c-Token program](/compressed-token-program/ctoken/README). +* You can use this to **convert existing SPL tokens to c-Tokens** with **sponsored rent-exemption**. Learn more in the [core concepts to the c-Token program](/compressed-token-program/README.mdx). From ac794505cf5d41099f2556234d82d90d10ef06b6 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 15:39:25 +0000 Subject: [PATCH 078/143] descriptions ctoken cookbook --- .../cmint/create-cmint.mdx | 11 ++--- .../cmint/mint-ctokens.mdx | 10 ++--- .../ctoken/close-ctoken-account.mdx | 13 ++++++ .../ctoken/create-cata.mdx | 42 +++++++++---------- .../ctoken-client-prerequisites.mdx | 4 +- 5 files changed, 41 insertions(+), 39 deletions(-) diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index e744e957..74b8af36 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -29,13 +29,11 @@ Learn the [core concepts to the c-Token Program here](/compressed-token-program/ The example creates a c-Mint with token metadata. -1. Derive the mint address and -fetch a validity proof from your RPC that proves the address does not exist yet. - -TODO: ADD TOOLTIP TO VALIDITY PROOF +1. Derive the mint address from the mint signer and address tree +2. Fetch a validity proof from your RPC that proves the address does not exist yet. 3. Configure mint and your token metadata (name, symbol, URI, additional metadata) -4. Build the instruction with `CreateCMint::new()`. +4. Build the instruction with `CreateCMint::new()` and send the transaction. ```rust use light_compressed_token_sdk::ctoken::CreateCMint; @@ -50,9 +48,6 @@ let create_cmint = CreateCMint::new( let instruction = create_cmint.instruction()?; ``` -5. Send transaction with both `payer` and `mint_signer` as signers.
    -c-Mint addresses are derived from the mint signer and address tree. - ### Prerequisites diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index 9f783f98..4a3a7b66 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -33,10 +33,9 @@ Find [full code examples at the end](#full-code-example). The example mints c-Tokens to existing c-Token accounts. 1. Prerequisite: The example creates a test c-Mint and destination c-Token account. - -TODO: ADD MORE steps - -2. Build the instruction with `MintToCToken::new()`. +2. Get c-Mint account infos and prove it exists with a validity proof.. +3. Set the amount of tokens you will mint and the mint authority. Only the mint authority can mint new c-Tokens. +4. Build the instruction with `MintToCToken::new()` and send the transaction. ```rust use light_compressed_token_sdk::ctoken::MintToCToken; @@ -51,9 +50,6 @@ let instruction = MintToCToken::new( .instruction()?; ``` -3. Send transaction with payer as signer.
    -Only the mint authority can mint new c-Tokens. - ### Prerequisites diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx index 9f03ba89..527554cf 100644 --- a/compressed-token-program/ctoken/close-ctoken-account.mdx +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -21,6 +21,19 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c
    + +1. The example creates a test c-Mint and c-token account. +2. Build the instruction with `CloseAccount`: +```rust +let close_instruction = CloseAccount::new( + CTOKEN_PROGRAM_ID, + account.pubkey(), + payer.pubkey(), // Destination for remaining lamports + owner, +) +.instruction() +``` + ### Prerequisites diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index 44ff1b07..98c70a2b 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -34,30 +34,16 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c - - - -### Prerequisites - - - - - - - -### Create c-ATA - 1. The example creates a test c-Mint. You can use existing c-Mints, SPL or Token 2022 mints as well. 2. Derive the address from mint and owner pubkey. -3. Build the instruction with `CreateAssociatedTokenAccount2`. It automatically includes the default rent config: +3. Build the instruction with `CreateAssociatedTokenAccount`. It automatically includes the default rent config: ```rust -use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2; +use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount; -let instruction = CreateAssociatedTokenAccount2::new( +let instruction = CreateAssociatedTokenAccount::new( payer.pubkey(), owner, mint, - CompressibleParams::default(), ) .instruction()?; ``` @@ -65,6 +51,19 @@ let instruction = CreateAssociatedTokenAccount2::new( 4. Send transaction & verify c-ATA creation with `get_account`.
    c-ATAs are Solana accounts and use your familiar RPC methods. + + + +### Prerequisites + + + + + + + +### Create c-ATA + ```rust use borsh::BorshDeserialize; use light_client::indexer::{AddressWithTree, Indexer}; @@ -202,9 +201,8 @@ Find [a full code example at the end](#full-code-example). ### Build Account Infos and CPI the c-Token Program -1. Pass the required accounts -2. Include rent config from `compressible_params` -3. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. +1. Pass the required accounts that include the rent config. +2. Use `invoke` or `invoke_signed`, when a CPI requires a PDA signer. The c-ATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike c-Token accounts, owner and mint are passed as accounts, not in instruction data. @@ -213,9 +211,9 @@ The c-ATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike c-T ```rust -use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; +use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccountInfos; -CreateAssociatedTokenAccount2Infos { +CreateAssociatedTokenAccountInfos { owner: owner.clone(), mint: mint.clone(), payer: payer.clone(), diff --git a/snippets/ctoken-guides/ctoken-client-prerequisites.mdx b/snippets/ctoken-guides/ctoken-client-prerequisites.mdx index f868b7a2..187bbcd3 100644 --- a/snippets/ctoken-guides/ctoken-client-prerequisites.mdx +++ b/snippets/ctoken-guides/ctoken-client-prerequisites.mdx @@ -6,7 +6,7 @@ import RustSetupEnvironment from '/snippets/setup/rust-setup-environment-tabs.md -**Developer Environment** - + + From 8386078b9e65d6e3a47c169976fc356f78363e34 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 15:48:36 +0000 Subject: [PATCH 079/143] more descriptions --- compressed-token-program/cmint/create-cmint.mdx | 7 ------- compressed-token-program/cmint/mint-ctokens.mdx | 8 -------- compressed-token-program/ctoken/close-ctoken-account.mdx | 4 ---- compressed-token-program/ctoken/create-cata.mdx | 7 ------- compressed-token-program/ctoken/create-ctoken.mdx | 7 ------- compressed-token-program/ctoken/transfer-interface.mdx | 7 ------- docs.json | 3 +-- 7 files changed, 1 insertion(+), 42 deletions(-) diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index 74b8af36..b1b09620 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -18,13 +18,6 @@ Learn the [core concepts to the c-Token Program here](/compressed-token-program/ # Get Started - - -```typescript -// TODO: TypeScript SDK coming soon -``` - - diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index 4a3a7b66..d24b16ca 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -21,14 +21,6 @@ Find [full code examples at the end](#full-code-example). # Get Started - - -```typescript -// TODO: TypeScript SDK coming soon -``` - - - The example mints c-Tokens to existing c-Token accounts. diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx index 527554cf..cd5dcf21 100644 --- a/compressed-token-program/ctoken/close-ctoken-account.mdx +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -16,10 +16,6 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c # Get Started - - - - 1. The example creates a test c-Mint and c-token account. diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index 98c70a2b..7ad9888c 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -24,13 +24,6 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c # Get Started - - -```typescript -// TODO: TypeScript SDK coming soon -``` - - diff --git a/compressed-token-program/ctoken/create-ctoken.mdx b/compressed-token-program/ctoken/create-ctoken.mdx index 80199a7c..b103439c 100644 --- a/compressed-token-program/ctoken/create-ctoken.mdx +++ b/compressed-token-program/ctoken/create-ctoken.mdx @@ -28,13 +28,6 @@ import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-c # Get Started - - -```typescript -// TODO: TypeScript SDK coming soon -``` - - diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index e3b0f9cb..1140e1a4 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -49,13 +49,6 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c # Get Started - - -```typescript -// TODO: TypeScript SDK coming soon -``` - - diff --git a/docs.json b/docs.json index 548697e5..16b67c63 100644 --- a/docs.json +++ b/docs.json @@ -26,7 +26,7 @@ ] }, { - "group": "c-Token (on Devnet)", + "group": "c-Token (Localnet & Devnet)", "pages": [ "compressed-token-program/README", { @@ -37,7 +37,6 @@ "expanded": true, "pages": [ "compressed-token-program/cmint/create-cmint", - "compressed-token-program/cmint/update-metadata", "compressed-token-program/cmint/mint-ctokens" ] }, From 988d2cd3464bd20b9038d80149652abf9c9f9366 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 15:56:03 +0000 Subject: [PATCH 080/143] transfe rinterface description --- .../ctoken/transfer-interface.mdx | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index 1140e1a4..a8540d63 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -12,7 +12,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c
    - + - + - +
    **c-Token → c-Token Account****c-Token -> c-Token Account**
    • Transfers c-Tokens between c-Token accounts
    • @@ -20,7 +20,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c
    **SPL token → c-Token Account****SPL token -> c-Token Account**
    • Transfers SPL tokens to c-Token accounts
    • @@ -30,7 +30,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c
    **c-Token → SPL Account****c-Token -> SPL Account**
    • Releases SPL tokens from interface PDA to SPL account
    • @@ -42,7 +42,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c
    -* For example, **SPL → c-Token** can be used for transfers from Alice's SPL token account to her own c-Token account. +* For example, **SPL -> c-Token** can be used for transfers from Alice's SPL token account to her own c-Token account. * You can use this to **convert existing SPL tokens to c-Tokens** with **sponsored rent-exemption**. @@ -52,9 +52,10 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c -1. Creates SPL mint, SPL token accounts, and mint SPL tokens -2. Sends SPL tokens to c-Token account. c-Tokens are minted. -3. Transfers c-Tokens to another c-Token account. +The example transfers SPL token -> c-Token and c-Token -> c-Token: +1. Create SPL mint, SPL token accounts, and mint SPL tokens +2. Send SPL tokens to c-Token account to mint c-Tokens. +3. Transfer c-Tokens to another c-Token account. @@ -69,9 +70,6 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c -SPL Mint: SPLToken -> cToken and cToken -> cToken - - ```rust use borsh::BorshDeserialize; use light_client::rpc::Rpc; @@ -194,7 +192,7 @@ async fn test_client_transfer_spl_to_ctoken_to_ctoken() { .await .unwrap(); - // Step 7: Transfer cToken from sender to recipient (3000 tokens) + // Step 7: Transfer cToken to cToken (3000 tokens) let transfer_instruction = TransferCtoken { source: sender_ctoken_ata, destination: recipient_ctoken_ata, From 48088ef0a20e54c8fd93629b6514d408a5e71094 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 16:00:10 +0000 Subject: [PATCH 081/143] compressed token airdrop link on readme --- compressed-tokens/README.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compressed-tokens/README.mdx b/compressed-tokens/README.mdx index ea39493e..cb22db47 100644 --- a/compressed-tokens/README.mdx +++ b/compressed-tokens/README.mdx @@ -20,6 +20,13 @@ import { TokenAccountCompressedVsSpl } from '/snippets/jsx/token-account-compres * Distribute tokens without paying up front rent per recipient. * Cost reduction for airdrops, rewards, etc: + From 0cac998782cdfab83289f969aac59d95932d1d1f Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 17:35:28 +0000 Subject: [PATCH 082/143] Remove redundant line breaks in c-ATA and c-Token creation docs --- compressed-token-program/ctoken/create-cata.mdx | 3 +-- compressed-token-program/ctoken/create-ctoken.mdx | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index 7ad9888c..ed5414de 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -41,8 +41,7 @@ let instruction = CreateAssociatedTokenAccount::new( .instruction()?; ``` -4. Send transaction & verify c-ATA creation with `get_account`.
    -c-ATAs are Solana accounts and use your familiar RPC methods. +4. Send transaction & verify c-ATA creation with `get_account`. diff --git a/compressed-token-program/ctoken/create-ctoken.mdx b/compressed-token-program/ctoken/create-ctoken.mdx index b103439c..18316cd2 100644 --- a/compressed-token-program/ctoken/create-ctoken.mdx +++ b/compressed-token-program/ctoken/create-ctoken.mdx @@ -45,9 +45,7 @@ let instruction = CreateCTokenAccount::new( ).instruction()?; ``` -3. Send transaction & verify c-Token account creation with `get_account`.
    -c-Token accounts are Solana accounts and use your familiar RPC methods. - +3. Send transaction & verify c-Token account creation with `get_account`. From a32daa9267a7c2bc88e91ebcfd028f419af796ac Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 19:37:04 +0000 Subject: [PATCH 083/143] Simplify c-Token rent documentation and remove redundant sections --- docs.json | 1 - learn/c-token-program.mdx | 24 ++++-------------------- snippets/compressible-vs-solana-rent.mdx | 2 -- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/docs.json b/docs.json index 16b67c63..7a461653 100644 --- a/docs.json +++ b/docs.json @@ -413,7 +413,6 @@ "learn/core-concepts/considerations" ] }, - "rent", "learn/c-token-program" ] }, diff --git a/learn/c-token-program.mdx b/learn/c-token-program.mdx index 1de5e817..02e7bb61 100644 --- a/learn/c-token-program.mdx +++ b/learn/c-token-program.mdx @@ -273,27 +273,11 @@ Here is how c-Tokens and SPL tokens compare: -### Compressible Extension - -c-Token accounts are compressible: +### Rent Config for c-Tokens -This extension makes c-Token accounts 200x cheaper than SPL token accounts - - | | c-Token | SPL | - |-----------|--------|-----| - | allocate | 0.000011 | 0.002 | - | rent for 24h | 0.000011 | - | - - - - - - - - - +We recommend to use default values, but you can customize prepaid rent and top ups: @@ -353,10 +337,10 @@ We recommend to use compressed tokens for **token distribution** or **storage of # Next Steps \ No newline at end of file diff --git a/snippets/compressible-vs-solana-rent.mdx b/snippets/compressible-vs-solana-rent.mdx index 7550f54d..6a4bd1b6 100644 --- a/snippets/compressible-vs-solana-rent.mdx +++ b/snippets/compressible-vs-solana-rent.mdx @@ -1,7 +1,5 @@ ### Initial Rent Top-Up The **creator of compressible accounts** tops-up the account with **rent for at least two epochs**. -Two epochs for compressible accounts have a length of **27,000 slots**. - ``` rust rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch) From 2e9090d24b9d1585f01646a098cc2b0b25617e8f Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 19:54:36 +0000 Subject: [PATCH 084/143] Update c-Token cookbooks for simplified constructors and rename v2 to v1 --- .../ctoken/close-ctoken-account.mdx | 3 +- .../ctoken/create-cata.mdx | 31 +++++++++---------- .../ctoken/create-ctoken.mdx | 6 ++-- .../ctoken/transfer-interface.mdx | 8 ++--- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx index cd5dcf21..0ce164e9 100644 --- a/compressed-token-program/ctoken/close-ctoken-account.mdx +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -46,7 +46,7 @@ use borsh::BorshDeserialize; use light_client::indexer::{AddressWithTree, Indexer}; use light_client::rpc::Rpc; use light_compressed_token_sdk::ctoken::{ - CloseAccount, CompressibleParams, CreateCMint, CreateCMintParams, CreateCTokenAccount, + CloseAccount, CreateCMint, CreateCMintParams, CreateCTokenAccount, CTOKEN_PROGRAM_ID, }; use light_ctoken_types::state::CToken; @@ -75,7 +75,6 @@ async fn test_close_ctoken_account() { account.pubkey(), mint, owner, - CompressibleParams::default(), ) .instruction() .unwrap(); diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index ed5414de..6fe1132f 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -61,7 +61,7 @@ use borsh::BorshDeserialize; use light_client::indexer::{AddressWithTree, Indexer}; use light_client::rpc::Rpc; use light_compressed_token_sdk::ctoken::{ - derive_ctoken_ata, CompressibleParams, CreateAssociatedTokenAccount2, CreateCMint, + derive_ctoken_ata, CreateAssociatedTokenAccount, CreateCMint, CreateCMintParams, }; use light_ctoken_types::state::CToken; @@ -86,11 +86,10 @@ async fn test_create_cata_client() { let (ata_address, _bump) = derive_ctoken_ata(&owner, &mint); // Step 4: Build instruction using SDK builder - let instruction = CreateAssociatedTokenAccount2::new( + let instruction = CreateAssociatedTokenAccount::new( payer.pubkey(), owner, mint, - CompressibleParams::default(), ) .instruction() .unwrap(); @@ -222,10 +221,10 @@ CreateAssociatedTokenAccountInfos { ```rust -use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount2Infos; +use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccountInfos; let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; -CreateAssociatedTokenAccount2Infos { +CreateAssociatedTokenAccountInfos { owner: owner.clone(), mint: mint.clone(), payer: payer.clone(), @@ -253,21 +252,21 @@ Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob ```rust expandable use borsh::{BorshDeserialize, BorshSerialize}; use light_compressed_token_sdk::ctoken::{ - CompressibleParamsInfos, CreateAssociatedTokenAccount2Infos, + CompressibleParamsInfos, CreateAssociatedTokenAccountInfos, }; use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use crate::{ATA_SEED, ID}; -/// Instruction data for create ATA V2 (owner/mint as accounts) +/// Instruction data for create ATA (owner/mint as accounts) #[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateAta2Data { +pub struct CreateAtaData { pub bump: u8, pub pre_pay_num_epochs: u8, pub lamports_per_write: u32, } -/// Handler for creating ATA using V2 variant (invoke) +/// Handler for creating ATA (invoke) /// /// Account order: /// - accounts[0]: owner (readonly) @@ -277,9 +276,9 @@ pub struct CreateAta2Data { /// - accounts[4]: system_program /// - accounts[5]: compressible_config /// - accounts[6]: rent_sponsor (writable) -pub fn process_create_ata2_invoke( +pub fn process_create_ata_invoke( accounts: &[AccountInfo], - data: CreateAta2Data, + data: CreateAtaData, ) -> Result<(), ProgramError> { if accounts.len() < 7 { return Err(ProgramError::NotEnoughAccountKeys); @@ -293,7 +292,7 @@ pub fn process_create_ata2_invoke( accounts[4].clone(), ); - CreateAssociatedTokenAccount2Infos { + CreateAssociatedTokenAccountInfos { owner: accounts[0].clone(), mint: accounts[1].clone(), payer: accounts[2].clone(), @@ -308,7 +307,7 @@ pub fn process_create_ata2_invoke( Ok(()) } -/// Handler for creating ATA using V2 variant with PDA ownership (invoke_signed) +/// Handler for creating ATA with PDA ownership (invoke_signed) /// /// Account order: /// - accounts[0]: owner (PDA, readonly) @@ -318,9 +317,9 @@ pub fn process_create_ata2_invoke( /// - accounts[4]: system_program /// - accounts[5]: compressible_config /// - accounts[6]: rent_sponsor (writable) -pub fn process_create_ata2_invoke_signed( +pub fn process_create_ata_invoke_signed( accounts: &[AccountInfo], - data: CreateAta2Data, + data: CreateAtaData, ) -> Result<(), ProgramError> { if accounts.len() < 7 { return Err(ProgramError::NotEnoughAccountKeys); @@ -343,7 +342,7 @@ pub fn process_create_ata2_invoke_signed( ); let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; - CreateAssociatedTokenAccount2Infos { + CreateAssociatedTokenAccountInfos { owner: accounts[0].clone(), mint: accounts[1].clone(), payer: accounts[2].clone(), // PDA diff --git a/compressed-token-program/ctoken/create-ctoken.mdx b/compressed-token-program/ctoken/create-ctoken.mdx index 18316cd2..6743be63 100644 --- a/compressed-token-program/ctoken/create-ctoken.mdx +++ b/compressed-token-program/ctoken/create-ctoken.mdx @@ -42,7 +42,8 @@ let instruction = CreateCTokenAccount::new( account.pubkey(), mint, owner, -).instruction()?; +) +.instruction()?; ``` 3. Send transaction & verify c-Token account creation with `get_account`. @@ -62,7 +63,7 @@ let instruction = CreateCTokenAccount::new( ```rust use borsh::BorshDeserialize; use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::{CompressibleParams, CreateCTokenAccount}; +use light_compressed_token_sdk::ctoken::CreateCTokenAccount; use light_ctoken_types::state::CToken; use light_program_test::{LightProgramTest, ProgramTestConfig}; use solana_sdk::{signature::Keypair, signer::Signer}; @@ -93,7 +94,6 @@ async fn test_create_ctoken_account() { account.pubkey(), mint, owner, - CompressibleParams::default(), ) .instruction() .unwrap(); diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index a8540d63..d7657d63 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -75,7 +75,7 @@ use borsh::BorshDeserialize; use light_client::rpc::Rpc; use light_compressed_token_sdk::{ ctoken::{ - derive_ctoken_ata, CompressibleParams, CreateAssociatedTokenAccount2, TransferCtoken, + derive_ctoken_ata, CreateAssociatedTokenAccount, TransferCtoken, TransferSplToCtoken, }, token_pool::find_token_pool_pda_with_index, @@ -130,11 +130,10 @@ async fn test_client_transfer_spl_to_ctoken_to_ctoken() { // Step 4: Create sender cToken ATA let (sender_ctoken_ata, _) = derive_ctoken_ata(&sender.pubkey(), &mint); - let create_sender_ata = CreateAssociatedTokenAccount2::new( + let create_sender_ata = CreateAssociatedTokenAccount::new( payer.pubkey(), sender.pubkey(), mint, - CompressibleParams::default(), ) .instruction() .unwrap(); @@ -179,11 +178,10 @@ async fn test_client_transfer_spl_to_ctoken_to_ctoken() { let (recipient_ctoken_ata, _) = derive_ctoken_ata(&recipient.pubkey(), &mint); - let create_recipient_ata = CreateAssociatedTokenAccount2::new( + let create_recipient_ata = CreateAssociatedTokenAccount::new( payer.pubkey(), recipient.pubkey(), mint, - CompressibleParams::default(), ) .instruction() .unwrap(); From 572918c64151c3a11d927751a6e164e08dba6b90 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 5 Dec 2025 20:22:36 +0000 Subject: [PATCH 085/143] Remove redundant note from mint-ctokens cookbook --- compressed-token-program/cmint/mint-ctokens.mdx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index d24b16ca..cfb0e68b 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -13,11 +13,6 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c 2. The destination c-Token accounts must exist to receive the minted c-Tokens. 3. Only the mint authority can mint new c-Tokens. - -Find [full code examples at the end](#full-code-example). - - - # Get Started From 30232206aa4571122a42400e83b82f34ad51088e Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sat, 6 Dec 2025 17:07:45 +0000 Subject: [PATCH 086/143] Update c-Token docs: reduce creation costs and refactor SDK imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update token account creation cost from 22,288 to 17,208 lamports - Refactor SDK imports: light_compressed_token_sdk → light_ctoken_sdk, light_ctoken_types → light_ctoken_interface - Expand mint-ctokens cookbook with full invoke_signed test examples - Update rent configuration docs and calculators to reflect new costs - Remove redundant formatting and streamline code examples --- compressed-token-program/README.mdx | 3 +- .../cmint/create-cmint.mdx | 28 +- .../cmint/mint-ctokens.mdx | 545 +++++++++++++----- .../ctoken/close-ctoken-account.mdx | 4 +- .../ctoken/create-cata.mdx | 44 +- .../ctoken/create-ctoken.mdx | 26 +- .../ctoken/transfer-interface.mdx | 37 +- landing.mdx | 2 +- snippets/compressible-default-rent-config.mdx | 4 +- snippets/compressible-rent-explained.mdx | 10 +- snippets/jsx/compressible-rent-calculator.jsx | 11 +- snippets/jsx/ctoken-vs-spl-calculator.jsx | 2 +- 12 files changed, 484 insertions(+), 232 deletions(-) diff --git a/compressed-token-program/README.mdx b/compressed-token-program/README.mdx index 52b014d1..7848f014 100644 --- a/compressed-token-program/README.mdx +++ b/compressed-token-program/README.mdx @@ -21,7 +21,8 @@ import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx'; | Creation Cost | SPL | c-Token | |:---------------------|:------------------|:----------------------| | **Mint Account** | 1,461,600 lamports | 15,000 lamports | -| **Token Account** | 2,039,280 lamports | 22,288 lamports | +| **Token Account** | 2,039,280 lamports | 17,208 lamports | + diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index b1b09620..3e2408a1 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -29,7 +29,7 @@ The example creates a c-Mint with token metadata. 4. Build the instruction with `CreateCMint::new()` and send the transaction. ```rust -use light_compressed_token_sdk::ctoken::CreateCMint; +use light_ctoken_sdk::ctoken::CreateCMint; let create_cmint = CreateCMint::new( params, @@ -55,10 +55,10 @@ let instruction = create_cmint.instruction()?; ```rust use light_client::indexer::{AddressWithTree, Indexer}; use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::{CreateCMint, CreateCMintParams}; -use light_ctoken_types::instructions::extensions::token_metadata::TokenMetadataInstructionData; -use light_ctoken_types::instructions::extensions::ExtensionInstructionData; -use light_ctoken_types::state::AdditionalMetadata; +use light_ctoken_sdk::ctoken::{CreateCMint, CreateCMintParams}; +use light_ctoken_interface::instructions::extensions::token_metadata::TokenMetadataInstructionData; +use light_ctoken_interface::instructions::extensions::ExtensionInstructionData; +use light_ctoken_interface::state::AdditionalMetadata; use light_program_test::{LightProgramTest, ProgramTestConfig}; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; @@ -85,13 +85,13 @@ pub async fn create_compressed_mint( let output_queue = rpc.get_random_state_tree_info().unwrap().queue; // Derive address - let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + let compression_address = light_ctoken_sdk::ctoken::derive_compressed_mint_address( &mint_signer.pubkey(), &address_tree.tree, ); let mint_pda = - light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + light_ctoken_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; // Get validity proof for the address let rpc_result = rpc @@ -163,7 +163,7 @@ Find [a full code example at the end](#full-code-example). ### Configure Token Metadata ```rust -use light_ctoken_types::{ +use light_ctoken_interface::{ instructions::extensions::{ token_metadata::TokenMetadataInstructionData, ExtensionInstructionData, @@ -201,7 +201,7 @@ let token_metadata = ExtensionInstructionData::TokenMetadata( Set `decimals`, `mint_authority`, `freeze_authority`, and pass the `token_metadata` from the previous step. ```rust -use light_compressed_token_sdk::ctoken::CreateCMintParams; +use light_ctoken_sdk::ctoken::CreateCMintParams; let cmint_params = CreateCMintParams { decimals: data.decimals, @@ -233,7 +233,7 @@ The client includes them in the instruction. ```rust -use light_compressed_token_sdk::ctoken::SystemAccountInfos; +use light_ctoken_sdk::ctoken::SystemAccountInfos; let system_accounts = SystemAccountInfos { light_system_program: light_system_program.clone(), @@ -262,7 +262,7 @@ let system_accounts = SystemAccountInfos { ```rust -use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; +use light_ctoken_sdk::ctoken::CreateCMintAccountInfos; CreateCMintAccountInfos { mint_signer: mint_signer.clone(), @@ -282,7 +282,7 @@ CreateCMintAccountInfos { ```rust -use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; +use light_ctoken_sdk::ctoken::CreateCMintAccountInfos; let account_infos = CreateCMintAccountInfos { mint_signer: mint_signer.clone(), @@ -304,7 +304,7 @@ account_infos.invoke_signed(&[signer_seeds])?; ```rust -use light_compressed_token_sdk::ctoken::CreateCMintAccountInfos; +use light_ctoken_sdk::ctoken::CreateCMintAccountInfos; let account_infos = CreateCMintAccountInfos { mint_signer: mint_signer.clone(), @@ -339,7 +339,7 @@ Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob ```rust expandable use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::{ +use light_ctoken_sdk::{ ctoken::{ CreateCMintAccountInfos, CreateCMintParams, ExtensionInstructionData, SystemAccountInfos, }, diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index cfb0e68b..7f0e99be 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -24,7 +24,7 @@ The example mints c-Tokens to existing c-Token accounts. 3. Set the amount of tokens you will mint and the mint authority. Only the mint authority can mint new c-Tokens. 4. Build the instruction with `MintToCToken::new()` and send the transaction. ```rust -use light_compressed_token_sdk::ctoken::MintToCToken; +use light_ctoken_sdk::ctoken::MintToCToken; let instruction = MintToCToken::new( params, @@ -52,12 +52,12 @@ let instruction = MintToCToken::new( use borsh::BorshDeserialize; use light_client::indexer::{AddressWithTree, Indexer}; use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::{ +use light_ctoken_sdk::ctoken::{ CompressibleParams, CreateCMint, CreateCMintParams, CreateCTokenAccount, MintToCToken, MintToCTokenParams, }; -use light_ctoken_types::instructions::mint_action::CompressedMintWithContext; -use light_ctoken_types::state::{CToken, CompressedMint}; +use light_ctoken_interface::instructions::mint_action::CompressedMintWithContext; +use light_ctoken_interface::state::{CToken, CompressedMint}; use light_program_test::{LightProgramTest, ProgramTestConfig}; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; @@ -178,13 +178,13 @@ pub async fn create_compressed_mint( let output_queue = rpc.get_random_state_tree_info().unwrap().queue; // Derive compression address - let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + let compression_address = light_ctoken_sdk::ctoken::derive_compressed_mint_address( &mint_signer.pubkey(), &address_tree.tree, ); let mint_pda = - light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + light_ctoken_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; // Get validity proof for the address to proof it does not exist in the address tree let rpc_result = rpc @@ -248,7 +248,7 @@ Include your mint, the amount of tokens to be minted and the pubkey of the mint The client passes a validity proof that proves the c-Mint exists. ```rust -use light_compressed_token_sdk::ctoken::MintToCTokenParams; +use light_ctoken_sdk::ctoken::MintToCTokenParams; let mint_params = MintToCTokenParams::new( data.compressed_mint_inputs, @@ -273,7 +273,7 @@ The client includes them in the instruction. ```rust -use light_compressed_token_sdk::ctoken::SystemAccountInfos; +use light_ctoken_sdk::ctoken::SystemAccountInfos; let system_accounts = SystemAccountInfos { light_system_program: light_system_program.clone(), @@ -298,7 +298,7 @@ let system_accounts = SystemAccountInfos { ```rust -use light_compressed_token_sdk::ctoken::MintToCTokenInfos; +use light_ctoken_sdk::ctoken::MintToCTokenInfos; MintToCTokenInfos { authority: authority.clone(), @@ -319,7 +319,7 @@ MintToCTokenInfos { ```rust -use light_compressed_token_sdk::ctoken::MintToCTokenInfos; +use light_ctoken_sdk::ctoken::MintToCTokenInfos; let account_infos = MintToCTokenInfos { authority: authority.clone(), @@ -355,157 +355,422 @@ Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob ```rust expandable +// Tests for MintToCTokenInfos (MintToCtoken instruction) + +mod shared; + use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::{ - MintToCTokenInfos, MintToCTokenParams, SystemAccountInfos, +use light_client::{indexer::Indexer, rpc::Rpc}; +use light_ctoken_sdk::{ + compressed_token::mint_action::MintActionMetaConfig, ctoken::CTOKEN_PROGRAM_ID, +}; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use native_ctoken_examples::{ + CreateCmintData, CreateTokenAccountData, MintToCTokenData, ID, MINT_AUTHORITY_SEED, + MINT_SIGNER_SEED, +}; +use shared::setup_create_compressed_mint; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + signature::Keypair, + signer::Signer, }; -use light_ctoken_types::instructions::mint_action::CompressedMintWithContext; -use light_sdk::instruction::ValidityProof; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; - -use crate::ID; - -/// PDA seed for mint authority in invoke_signed variant -pub const MINT_AUTHORITY_SEED: &[u8] = b"mint_authority"; - -/// Instruction data for mint_to_ctoken operations -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct MintToCTokenData { - pub compressed_mint_inputs: CompressedMintWithContext, - pub amount: u64, - pub mint_authority: Pubkey, - pub proof: ValidityProof, -} -/// Handler for minting tokens to compressed token accounts (invoke) -/// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: light_system_program -/// - accounts[2]: authority (mint_authority) -/// - accounts[3]: fee_payer -/// - accounts[4]: cpi_authority_pda -/// - accounts[5]: registered_program_pda -/// - accounts[6]: account_compression_authority -/// - accounts[7]: account_compression_program -/// - accounts[8]: system_program -/// - accounts[9]: output_queue -/// - accounts[10]: state_tree -/// - accounts[11]: input_queue -/// - accounts[12..]: ctoken_accounts (destination accounts) -pub fn process_mint_to_ctoken( - accounts: &[AccountInfo], - data: MintToCTokenData, -) -> Result<(), ProgramError> { - if accounts.len() < 13 { - return Err(ProgramError::NotEnoughAccountKeys); +/// Test minting tokens to a ctoken account using MintToCTokenInfos::invoke() +#[tokio::test] +async fn test_mint_to_ctoken() { + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( + false, + Some(vec![("native_ctoken_examples", ID)]), + )) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + let mint_authority = payer.pubkey(); + + // Setup: Create compressed mint directly (not via wrapper program) + let (mint_pda, compression_address, _) = + setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; + + let ctoken_account = Keypair::new(); + let owner = payer.pubkey(); + // Create a ctoken account to mint tokens to via wrapper program + { + let create_token_account_data = CreateTokenAccountData { + owner, + pre_pay_num_epochs: 2, + lamports_per_write: 1, + }; + let instruction_data = + [vec![2u8], create_token_account_data.try_to_vec().unwrap()].concat(); + + use light_ctoken_sdk::ctoken::{config_pda, rent_sponsor_pda}; + let config = config_pda(); + let rent_sponsor = rent_sponsor_pda(); + + let instruction = Instruction { + program_id: ID, + accounts: vec![ + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new(ctoken_account.pubkey(), true), + AccountMeta::new_readonly(mint_pda, false), + AccountMeta::new_readonly(config, false), + AccountMeta::new_readonly(Pubkey::default(), false), // system_program + AccountMeta::new(rent_sponsor, false), + AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), // token_program + ], + data: instruction_data, + }; + + rpc.create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&payer, &ctoken_account], + ) + .await + .unwrap(); } - // Build params using the constructor - let params = MintToCTokenParams::new( - data.compressed_mint_inputs, - data.amount, - data.mint_authority, - data.proof, - ); + // Get the compressed mint account to build CompressedMintWithContext + let compressed_mint_account = rpc + .get_compressed_account(compression_address, None) + .await + .unwrap() + .value + .expect("Compressed mint should exist"); - // Build system accounts struct - let system_accounts = SystemAccountInfos { - light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[4].clone(), - registered_program_pda: accounts[5].clone(), - account_compression_authority: accounts[6].clone(), - account_compression_program: accounts[7].clone(), - system_program: accounts[8].clone(), - }; + // Deserialize the compressed mint data + use light_ctoken_interface::state::CompressedMint; + let compressed_mint = + CompressedMint::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) + .unwrap(); - // Collect ctoken accounts from remaining accounts - let ctoken_accounts: Vec = accounts[12..].to_vec(); - - MintToCTokenInfos { - authority: accounts[2].clone(), - payer: accounts[3].clone(), - state_tree: accounts[10].clone(), - input_queue: accounts[11].clone(), - output_queue: accounts[9].clone(), - ctoken_accounts, - system_accounts, - cpi_context: None, - cpi_context_account: None, - params, + let amount = 1_000_000_000u64; // 1 token with 9 decimals + + // Mint ctokens with test program. + { + // Get validity proof for the mint operation + let rpc_result = rpc + .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) + .await + .unwrap() + .value; + + // Build CompressedMintWithContext from the compressed account + let compressed_mint_with_context = + light_ctoken_interface::instructions::mint_action::CompressedMintWithContext { + address: compression_address, + leaf_index: compressed_mint_account.leaf_index, + prove_by_index: true, + root_index: rpc_result.accounts[0] + .root_index + .root_index() + .unwrap_or_default(), // Will be updated with validity proof + mint: compressed_mint.try_into().unwrap(), + }; + // Build instruction data for wrapper program + let mint_to_data = MintToCTokenData { + compressed_mint_inputs: compressed_mint_with_context.clone(), + amount, + mint_authority, + proof: rpc_result.proof, + }; + let wrapper_instruction_data = [vec![1u8], mint_to_data.try_to_vec().unwrap()].concat(); + + // Build wrapper instruction with compressed token program as first account + let compressed_token_program_id = + Pubkey::new_from_array(light_ctoken_interface::COMPRESSED_TOKEN_PROGRAM_ID); + + let mut wrapper_accounts = vec![AccountMeta::new_readonly( + compressed_token_program_id, + false, + )]; + let account_metas = MintActionMetaConfig::new( + payer.pubkey(), + mint_authority, + compressed_mint_account.tree_info.tree, + compressed_mint_account.tree_info.queue, + compressed_mint_account.tree_info.queue, + ) + .with_ctoken_accounts(vec![ctoken_account.pubkey()]) + .to_account_metas(); + wrapper_accounts.extend(account_metas); + + let instruction = Instruction { + program_id: ID, + accounts: wrapper_accounts, + data: wrapper_instruction_data, + }; + + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); } - .invoke()?; - Ok(()) + // Verify tokens were minted to the ctoken account + let ctoken_account_data = rpc + .get_account(ctoken_account.pubkey()) + .await + .unwrap() + .unwrap(); + + // Parse the account data to verify balance + use light_ctoken_interface::state::CToken; + let account_state = CToken::deserialize(&mut &ctoken_account_data.data[..]).unwrap(); + assert_eq!(account_state.amount, amount, "Token amount should match"); + assert_eq!( + account_state.mint.to_bytes(), + mint_pda.to_bytes(), + "Mint should match" + ); + assert_eq!( + account_state.owner.to_bytes(), + owner.to_bytes(), + "Owner should match" + ); } -/// Handler for minting tokens with PDA mint authority (invoke_signed) +/// Test minting tokens with PDA mint authority using MintToCTokenInfos::invoke_signed() /// -/// Account order: -/// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: light_system_program -/// - accounts[2]: authority (PDA mint_authority) -/// - accounts[3]: fee_payer -/// - accounts[4]: cpi_authority_pda -/// - accounts[5]: registered_program_pda -/// - accounts[6]: account_compression_authority -/// - accounts[7]: account_compression_program -/// - accounts[8]: system_program -/// - accounts[9]: output_queue -/// - accounts[10]: state_tree -/// - accounts[11]: input_queue -/// - accounts[12..]: ctoken_accounts (destination accounts) -pub fn process_mint_to_ctoken_invoke_signed( - accounts: &[AccountInfo], - data: MintToCTokenData, -) -> Result<(), ProgramError> { - if accounts.len() < 13 { - return Err(ProgramError::NotEnoughAccountKeys); +/// This test uses the wrapper program to: +/// 1. Create a compressed mint with PDA authority (discriminator 14 - CreateCmintWithPdaAuthority) +/// 2. Mint tokens using PDA authority (discriminator 13 - MintToCtokenInvokeSigned) +#[tokio::test] +async fn test_mint_to_ctoken_invoke_signed() { + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( + false, + Some(vec![("native_ctoken_examples", ID)]), + )) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + + // Derive both PDAs from our wrapper program + let (mint_signer_pda, _) = Pubkey::find_program_address(&[MINT_SIGNER_SEED], &ID); + let (mint_authority_pda, _) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); + + let decimals = 9u8; + let address_tree = rpc.get_address_tree_v2(); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; + + // Derive compression address using the PDA mint_signer + let compression_address = light_ctoken_sdk::ctoken::derive_compressed_mint_address( + &mint_signer_pda, + &address_tree.tree, + ); + + let mint_pda = light_ctoken_sdk::ctoken::find_spl_mint_address(&mint_signer_pda).0; + + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![light_client::indexer::AddressWithTree { + address: compression_address, + tree: address_tree.tree, + }], + None, + ) + .await + .unwrap() + .value; + + let compressed_token_program_id = + Pubkey::new_from_array(light_ctoken_interface::COMPRESSED_TOKEN_PROGRAM_ID); + let default_pubkeys = light_ctoken_sdk::utils::CTokenDefaultAccounts::default(); + + // Step 1: Create compressed mint with PDA authority using wrapper program (discriminator 14) + { + let create_cmint_data = CreateCmintData { + decimals, + address_merkle_tree_root_index: rpc_result.addresses[0].root_index, + mint_authority: mint_authority_pda, // Will be overridden by the handler + proof: rpc_result.proof.0.unwrap(), + compression_address, + mint: mint_pda, + freeze_authority: None, + extensions: None, + }; + // Discriminator 14 = CreateCmintWithPdaAuthority + let wrapper_instruction_data = + [vec![14u8], create_cmint_data.try_to_vec().unwrap()].concat(); + + // Account order for CreateCmintWithPdaAuthority: + // [0] compressed_token_program, [1] light_system_program, [2] mint_signer (PDA), + // [3] authority (PDA), [4] fee_payer, [5] cpi_authority_pda, [6] registered_program_pda, + // [7] account_compression_authority, [8] account_compression_program, [9] system_program, + // [10] output_queue, [11] address_tree + let wrapper_accounts = vec![ + AccountMeta::new_readonly(compressed_token_program_id, false), + AccountMeta::new_readonly(default_pubkeys.light_system_program, false), + AccountMeta::new_readonly(mint_signer_pda, false), // PDA - program signs + AccountMeta::new(mint_authority_pda, false), // writable PDA - program signs + AccountMeta::new(payer.pubkey(), true), // fee_payer + AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false), + AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false), + AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false), + AccountMeta::new_readonly(default_pubkeys.account_compression_program, false), + AccountMeta::new_readonly(default_pubkeys.system_program, false), + AccountMeta::new(output_queue, false), + AccountMeta::new(address_tree.tree, false), + ]; + + let create_mint_ix = Instruction { + program_id: ID, + accounts: wrapper_accounts, + data: wrapper_instruction_data, + }; + + rpc.create_and_send_transaction(&[create_mint_ix], &payer.pubkey(), &[&payer]) + .await + .unwrap(); } - // Derive the PDA for the mint authority - let (pda, bump) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); + let ctoken_account = Keypair::new(); + let owner = payer.pubkey(); - // Verify the authority account is the PDA - if &pda != accounts[2].key { - return Err(ProgramError::InvalidSeeds); + // Create a ctoken account to mint tokens to via wrapper program + { + let create_token_account_data = CreateTokenAccountData { + owner, + pre_pay_num_epochs: 2, + lamports_per_write: 1, + }; + let instruction_data = + [vec![2u8], create_token_account_data.try_to_vec().unwrap()].concat(); + + use light_ctoken_sdk::ctoken::{config_pda, rent_sponsor_pda}; + let config = config_pda(); + let rent_sponsor = rent_sponsor_pda(); + + let instruction = Instruction { + program_id: ID, + accounts: vec![ + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new(ctoken_account.pubkey(), true), + AccountMeta::new_readonly(mint_pda, false), + AccountMeta::new_readonly(config, false), + AccountMeta::new_readonly(Pubkey::default(), false), // system_program + AccountMeta::new(rent_sponsor, false), + AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), + ], + data: instruction_data, + }; + + rpc.create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&payer, &ctoken_account], + ) + .await + .unwrap(); } - let params = MintToCTokenParams::new( - data.compressed_mint_inputs, - data.amount, - data.mint_authority, - data.proof, - ); + // Get the compressed mint account to build CompressedMintWithContext + let compressed_mint_account = rpc + .get_compressed_account(compression_address, None) + .await + .unwrap() + .value + .expect("Compressed mint should exist"); - let system_accounts = SystemAccountInfos { - light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[4].clone(), - registered_program_pda: accounts[5].clone(), - account_compression_authority: accounts[6].clone(), - account_compression_program: accounts[7].clone(), - system_program: accounts[8].clone(), - }; + // Deserialize the compressed mint data + use light_ctoken_interface::state::CompressedMint; + let compressed_mint = + CompressedMint::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) + .unwrap(); - let ctoken_accounts: Vec = accounts[12..].to_vec(); - - let account_infos = MintToCTokenInfos { - authority: accounts[2].clone(), - payer: accounts[3].clone(), - state_tree: accounts[10].clone(), - input_queue: accounts[11].clone(), - output_queue: accounts[9].clone(), - ctoken_accounts, - system_accounts, - cpi_context: None, - cpi_context_account: None, - params, - }; + let amount = 1_000_000_000u64; // 1 token with 9 decimals - let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; - account_infos.invoke_signed(&[signer_seeds])?; + // Mint ctokens with PDA authority via invoke_signed + { + // Get validity proof for the mint operation + let rpc_result = rpc + .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) + .await + .unwrap() + .value; + + // Build CompressedMintWithContext from the compressed account + let compressed_mint_with_context = + light_ctoken_interface::instructions::mint_action::CompressedMintWithContext { + address: compression_address, + leaf_index: compressed_mint_account.leaf_index, + prove_by_index: true, + root_index: rpc_result.accounts[0] + .root_index + .root_index() + .unwrap_or_default(), + mint: compressed_mint.try_into().unwrap(), + }; + + // Build instruction data for wrapper program + let mint_to_data = MintToCTokenData { + compressed_mint_inputs: compressed_mint_with_context.clone(), + amount, + mint_authority: mint_authority_pda, + proof: rpc_result.proof, + }; + // Discriminator 13 = MintToCtokenInvokeSigned + let wrapper_instruction_data = [vec![13u8], mint_to_data.try_to_vec().unwrap()].concat(); + + // Build accounts manually since SDK marks authority as signer, but we need it as non-signer + // for invoke_signed (the wrapper program signs via CPI) + let compressed_token_program_id = + Pubkey::new_from_array(light_ctoken_interface::COMPRESSED_TOKEN_PROGRAM_ID); + let default_pubkeys = light_ctoken_sdk::utils::CTokenDefaultAccounts::default(); + + let wrapper_accounts = vec![ + AccountMeta::new_readonly(compressed_token_program_id, false), + AccountMeta::new_readonly(default_pubkeys.light_system_program, false), + // authority NOT marked as signer - program will sign via invoke_signed + AccountMeta::new_readonly(mint_authority_pda, false), + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false), + AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false), + AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false), + AccountMeta::new_readonly(default_pubkeys.account_compression_program, false), + AccountMeta::new_readonly(default_pubkeys.system_program, false), + AccountMeta::new(compressed_mint_account.tree_info.queue, false), // output_queue + AccountMeta::new(compressed_mint_account.tree_info.tree, false), // state_tree + AccountMeta::new(compressed_mint_account.tree_info.queue, false), // input_queue + AccountMeta::new(ctoken_account.pubkey(), false), // ctoken_account + ]; + let instruction = Instruction { + program_id: ID, + accounts: wrapper_accounts, + data: wrapper_instruction_data, + }; + + // Note: only payer signs, the mint_authority PDA is signed by the program via invoke_signed + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + } - Ok(()) + // Verify tokens were minted to the ctoken account + let ctoken_account_data = rpc + .get_account(ctoken_account.pubkey()) + .await + .unwrap() + .unwrap(); + + // Parse the account data to verify balance + use light_ctoken_interface::state::CToken; + let account_state = CToken::deserialize(&mut &ctoken_account_data.data[..]).unwrap(); + assert_eq!(account_state.amount, amount, "Token amount should match"); + assert_eq!( + account_state.mint.to_bytes(), + mint_pda.to_bytes(), + "Mint should match" + ); + assert_eq!( + account_state.owner.to_bytes(), + owner.to_bytes(), + "Owner should match" + ); } ``` diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx index 0ce164e9..d8833b9d 100644 --- a/compressed-token-program/ctoken/close-ctoken-account.mdx +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -249,7 +249,7 @@ Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob ```rust expandable -use light_compressed_token_sdk::ctoken::CloseAccountInfos; +use light_ctoken_sdk::ctoken::CloseAccountInfos; use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use crate::{ID, TOKEN_ACCOUNT_SEED}; @@ -301,7 +301,7 @@ pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<( // Derive the PDA for the authority let (pda, bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); - // Verify the authority account is the PDA + // Verify the authority account is the PDA we expect if &pda != accounts[3].key { return Err(ProgramError::InvalidSeeds); } diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index 6fe1132f..c1e6ee03 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -12,10 +12,10 @@ import CTokenClientPrerequisites from '/snippets/ctoken-guides/ctoken-client-pre import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-config.mdx'; 1. Associated c-Token accounts (c-ATA) are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. -2. The address for c-ATAs is deterministically derived with the owner’s address, c-Token program ID, and mint address. +2. The address for c-ATAs is deterministically derived with the owner's address, c-Token program ID, and mint address. 3. c-ATAs are compressible with a default rent config. - 1. At account creation, you pay ~22,208 lamports for 24h of rent
    and compression incentive (the rent-exemption is sponsored by the protocol) - 2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 766 lamports when the account's rent is below 3h. + 1. At account creation, you pay ~17,208 lamports for 24h of rent
    and compression incentive (the rent-exemption is sponsored by the protocol) + 2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 776 lamports when the account's rent is below 3h. @@ -31,7 +31,7 @@ import ClientCustomRentConfig from '/snippets/ctoken-guides/client-custom-rent-c 2. Derive the address from mint and owner pubkey. 3. Build the instruction with `CreateAssociatedTokenAccount`. It automatically includes the default rent config: ```rust -use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccount; +use light_ctoken_sdk::ctoken::CreateAssociatedTokenAccount; let instruction = CreateAssociatedTokenAccount::new( payer.pubkey(), @@ -60,11 +60,11 @@ let instruction = CreateAssociatedTokenAccount::new( use borsh::BorshDeserialize; use light_client::indexer::{AddressWithTree, Indexer}; use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::{ +use light_ctoken_sdk::ctoken::{ derive_ctoken_ata, CreateAssociatedTokenAccount, CreateCMint, CreateCMintParams, }; -use light_ctoken_types::state::CToken; +use light_ctoken_interface::state::CToken; use light_program_test::{LightProgramTest, ProgramTestConfig}; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; @@ -117,13 +117,13 @@ pub async fn create_compressed_mint( let output_queue = rpc.get_random_state_tree_info().unwrap().queue; // Derive compression address - let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + let compression_address = light_ctoken_sdk::ctoken::derive_compressed_mint_address( &mint_signer.pubkey(), &address_tree.tree, ); let mint_pda = - light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + light_ctoken_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; // Get validity proof for the address let rpc_result = rpc @@ -202,7 +202,7 @@ The c-ATA address is derived from `[owner, ctoken_program_id, mint]`. Unlike c-T ```rust -use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccountInfos; +use light_ctoken_sdk::ctoken::CreateAssociatedTokenAccountInfos; CreateAssociatedTokenAccountInfos { owner: owner.clone(), @@ -221,7 +221,7 @@ CreateAssociatedTokenAccountInfos { ```rust -use light_compressed_token_sdk::ctoken::CreateAssociatedTokenAccountInfos; +use light_ctoken_sdk::ctoken::CreateAssociatedTokenAccountInfos; let signer_seeds: &[&[u8]] = &[ATA_SEED, &[bump]]; CreateAssociatedTokenAccountInfos { @@ -251,22 +251,20 @@ Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob ```rust expandable use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::{ - CompressibleParamsInfos, CreateAssociatedTokenAccountInfos, -}; +use light_ctoken_sdk::ctoken::{CompressibleParamsInfos, CreateAssociatedTokenAccountInfos}; use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use crate::{ATA_SEED, ID}; -/// Instruction data for create ATA (owner/mint as accounts) +/// Instruction data for create ATA V2 (owner/mint as accounts) #[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CreateAtaData { +pub struct CreateAta2Data { pub bump: u8, pub pre_pay_num_epochs: u8, pub lamports_per_write: u32, } -/// Handler for creating ATA (invoke) +/// Handler for creating ATA using V2 variant (invoke) /// /// Account order: /// - accounts[0]: owner (readonly) @@ -276,17 +274,15 @@ pub struct CreateAtaData { /// - accounts[4]: system_program /// - accounts[5]: compressible_config /// - accounts[6]: rent_sponsor (writable) -pub fn process_create_ata_invoke( +pub fn process_create_ata2_invoke( accounts: &[AccountInfo], - data: CreateAtaData, + data: CreateAta2Data, ) -> Result<(), ProgramError> { if accounts.len() < 7 { return Err(ProgramError::NotEnoughAccountKeys); } let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, accounts[5].clone(), accounts[6].clone(), accounts[4].clone(), @@ -307,7 +303,7 @@ pub fn process_create_ata_invoke( Ok(()) } -/// Handler for creating ATA with PDA ownership (invoke_signed) +/// Handler for creating ATA using V2 variant with PDA ownership (invoke_signed) /// /// Account order: /// - accounts[0]: owner (PDA, readonly) @@ -317,9 +313,9 @@ pub fn process_create_ata_invoke( /// - accounts[4]: system_program /// - accounts[5]: compressible_config /// - accounts[6]: rent_sponsor (writable) -pub fn process_create_ata_invoke_signed( +pub fn process_create_ata2_invoke_signed( accounts: &[AccountInfo], - data: CreateAtaData, + data: CreateAta2Data, ) -> Result<(), ProgramError> { if accounts.len() < 7 { return Err(ProgramError::NotEnoughAccountKeys); @@ -334,8 +330,6 @@ pub fn process_create_ata_invoke_signed( } let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, accounts[5].clone(), accounts[6].clone(), accounts[4].clone(), diff --git a/compressed-token-program/ctoken/create-ctoken.mdx b/compressed-token-program/ctoken/create-ctoken.mdx index 6743be63..26815991 100644 --- a/compressed-token-program/ctoken/create-ctoken.mdx +++ b/compressed-token-program/ctoken/create-ctoken.mdx @@ -18,8 +18,8 @@ import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-c 1. c-Token accounts are Solana accounts that hold token balances of cMints, SPL mints, or Token 2022 mints. 2. c-Token accounts are compressible with a default rent config. - 1. At account creation, you pay ~22,208 lamports for 24h of rent
    and compression incentive (the rent-exemption is sponsored by the protocol) - 2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 766 lamports when the account's rent is below 3h. + 1. At account creation, you pay ~17,208 lamports for 24h of rent
    and compression incentive (the rent-exemption is sponsored by the protocol) + 2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 776 lamports when the account's rent is below 3h. @@ -35,7 +35,7 @@ import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-c 1. The example creates a test cMint. You can use existing cMints, SPL or Token 2022 mints as well. 2. Build the instruction with `CreateCTokenAccount`. It automatically includes the default rent config. ```rust -use light_compressed_token_sdk::ctoken::{CreateCTokenAccount}; +use light_ctoken_sdk::ctoken::{CreateCTokenAccount}; let instruction = CreateCTokenAccount::new( payer.pubkey(), @@ -63,12 +63,12 @@ let instruction = CreateCTokenAccount::new( ```rust use borsh::BorshDeserialize; use light_client::rpc::Rpc; -use light_compressed_token_sdk::ctoken::CreateCTokenAccount; -use light_ctoken_types::state::CToken; +use light_ctoken_sdk::ctoken::CreateCTokenAccount; +use light_ctoken_interface::state::CToken; use light_program_test::{LightProgramTest, ProgramTestConfig}; use solana_sdk::{signature::Keypair, signer::Signer}; use light_client::indexer::{AddressWithTree, Indexer}; -use light_compressed_token_sdk::ctoken::{CreateCMint, CreateCMintParams}; +use light_ctoken_sdk::ctoken::{CreateCMint, CreateCMintParams}; use solana_sdk::{pubkey::Pubkey}; @@ -122,13 +122,13 @@ pub async fn create_compressed_mint( let output_queue = rpc.get_random_state_tree_info().unwrap().queue; // Derive compression address - let compression_address = light_compressed_token_sdk::ctoken::derive_compressed_mint_address( + let compression_address = light_ctoken_sdk::ctoken::derive_compressed_mint_address( &mint_signer.pubkey(), &address_tree.tree, ); let mint_pda = - light_compressed_token_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; + light_ctoken_sdk::ctoken::find_spl_mint_address(&mint_signer.pubkey()).0; // Get validity proof for the address let rpc_result = rpc @@ -205,7 +205,7 @@ Find [a full code example at the end](#full-code-example). ```rust -use light_compressed_token_sdk::ctoken::CreateCTokenAccountInfos; +use light_ctoken_sdk::ctoken::CreateCTokenAccountInfos; CreateCTokenAccountInfos { payer: payer.clone(), @@ -222,7 +222,7 @@ CreateCTokenAccountInfos { ```rust -use light_compressed_token_sdk::ctoken::CreateCTokenAccountInfos; +use light_ctoken_sdk::ctoken::CreateCTokenAccountInfos; let account_infos = CreateCTokenAccountInfos { payer: payer.clone(), @@ -250,7 +250,7 @@ Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob ```rust expandable use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; +use light_ctoken_sdk::ctoken::{CompressibleParamsInfos, CreateCTokenAccountInfos}; use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use crate::{ID, TOKEN_ACCOUNT_SEED}; @@ -286,8 +286,6 @@ pub fn process_create_token_account_invoke( // Build the compressible params using constructor let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, accounts[3].clone(), accounts[5].clone(), accounts[4].clone(), @@ -333,8 +331,6 @@ pub fn process_create_token_account_invoke_signed( // Build the compressible params using constructor let compressible_params = CompressibleParamsInfos::new( - data.pre_pay_num_epochs, - data.lamports_per_write, accounts[3].clone(), accounts[5].clone(), accounts[4].clone(), diff --git a/compressed-token-program/ctoken/transfer-interface.mdx b/compressed-token-program/ctoken/transfer-interface.mdx index d7657d63..904101aa 100644 --- a/compressed-token-program/ctoken/transfer-interface.mdx +++ b/compressed-token-program/ctoken/transfer-interface.mdx @@ -73,15 +73,14 @@ The example transfers SPL token -> c-Token and c-Token -> c-Token: ```rust use borsh::BorshDeserialize; use light_client::rpc::Rpc; -use light_compressed_token_sdk::{ +use light_ctoken_sdk::{ ctoken::{ derive_ctoken_ata, CreateAssociatedTokenAccount, TransferCtoken, TransferSplToCtoken, }, token_pool::find_token_pool_pda_with_index, }; -use light_compressed_token_types::CPI_AUTHORITY_PDA; -use light_ctoken_types::state::CToken; +use light_ctoken_interface::state::CToken; use light_program_test::{LightProgramTest, ProgramTestConfig}; use light_test_utils::spl::{create_mint_helper, create_token_2022_account, mint_spl_tokens}; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; @@ -144,8 +143,6 @@ async fn test_client_transfer_spl_to_ctoken_to_ctoken() { // Step 5: Convert SPL tokens to cToken (7000 tokens) let (token_pool_pda, token_pool_pda_bump) = find_token_pool_pda_with_index(&mint, 0); - let cpi_authority_pda = Pubkey::new_from_array(CPI_AUTHORITY_PDA); - let spl_token_program = anchor_spl::token::ID; let spl_to_ctoken_instruction = TransferSplToCtoken { @@ -264,7 +261,7 @@ Define the number of c-Tokens / SPL tokens to transfer - to which SPL or c-Token account. ```rust -use light_compressed_token_sdk::ctoken::TransferInterface; +use light_ctoken_sdk::ctoken::TransferInterface; let mut transfer = TransferInterface::new( data.amount, @@ -339,7 +336,7 @@ Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob ```rust expandable use borsh::{BorshDeserialize, BorshSerialize}; -use light_compressed_token_sdk::ctoken::TransferInterface; +use light_ctoken_sdk::ctoken::TransferInterface; use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use crate::ID; @@ -351,25 +348,25 @@ pub const TRANSFER_INTERFACE_AUTHORITY_SEED: &[u8] = b"transfer_interface_author #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct TransferInterfaceData { pub amount: u64, - /// Required for SPL<->c-Token transfers, None for c-Token->c-Token + /// Required for SPL<->CToken transfers, None for CToken->CToken pub token_pool_pda_bump: Option, } /// Handler for TransferInterface (invoke) /// /// This unified interface automatically detects account types and routes to: -/// - c-Token -> c-Token transfer -/// - c-Token -> SPL transfer -/// - SPL -> c-Token transfer +/// - CToken -> CToken transfer +/// - CToken -> SPL transfer +/// - SPL -> CToken transfer /// /// Account order: /// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or c-Token) -/// - accounts[2]: destination_account (SPL or c-Token) +/// - accounts[1]: source_account (SPL or CToken) +/// - accounts[2]: destination_account (SPL or CToken) /// - accounts[3]: authority (signer) /// - accounts[4]: payer (signer) /// - accounts[5]: compressed_token_program_authority -/// For SPL interface (required for SPL<->c-Token): +/// For SPL bridge (optional, required for SPL<->CToken): /// - accounts[6]: mint /// - accounts[7]: token_pool_pda /// - accounts[8]: spl_token_program @@ -390,7 +387,7 @@ pub fn process_transfer_interface_invoke( accounts[5].clone(), // compressed_token_program_authority ); - // Add SPL interface config if provided + // Add SPL bridge config if provided if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { transfer = transfer.with_spl_interface( Some(accounts[6].clone()), // mint @@ -411,12 +408,12 @@ pub fn process_transfer_interface_invoke( /// /// Account order: /// - accounts[0]: compressed_token_program (for CPI) -/// - accounts[1]: source_account (SPL or c-Token) -/// - accounts[2]: destination_account (SPL or c-Token) +/// - accounts[1]: source_account (SPL or CToken) +/// - accounts[2]: destination_account (SPL or CToken) /// - accounts[3]: authority (PDA, not signer - program signs) /// - accounts[4]: payer (signer) /// - accounts[5]: compressed_token_program_authority -/// For SPL interface (required for SPL<->c-Token): +/// For SPL bridge (optional, required for SPL<->CToken): /// - accounts[6]: mint /// - accounts[7]: token_pool_pda /// - accounts[8]: spl_token_program @@ -432,7 +429,7 @@ pub fn process_transfer_interface_invoke_signed( let (authority_pda, authority_bump) = Pubkey::find_program_address(&[TRANSFER_INTERFACE_AUTHORITY_SEED], &ID); - // Verify the authority account is the PDA + // Verify the authority account is the PDA we expect if &authority_pda != accounts[3].key { return Err(ProgramError::InvalidSeeds); } @@ -446,7 +443,7 @@ pub fn process_transfer_interface_invoke_signed( accounts[5].clone(), // compressed_token_program_authority ); - // Add SPL interface config if provided + // Add SPL bridge config if provided if accounts.len() >= 9 && data.token_pool_pda_bump.is_some() { transfer = transfer.with_spl_interface( Some(accounts[6].clone()), // mint diff --git a/landing.mdx b/landing.mdx index 6af9f77f..e63b4118 100644 --- a/landing.mdx +++ b/landing.mdx @@ -11,7 +11,7 @@ description: ZK Compression is a Solana account primitive that lets you create t | Creation Cost | Solana | ZK Compression | Use Case | |:---------------------|:-------------------|:----------------|:--------------------| | **Mint Account** | ~1,500,600 lamports | 15,000 lamports | Mints with Metadata | -| **Token Account** | ~2,039,280 lamports | 22,288 lamports | Tokens | +| **Token Account** | ~2,039,280 lamports | 17,208 lamports | Tokens | | **100-byte PDA** | ~1,600,000 lamports | 15,000 lamports | App State | diff --git a/snippets/compressible-default-rent-config.mdx b/snippets/compressible-default-rent-config.mdx index 6c574713..72b18de4 100644 --- a/snippets/compressible-default-rent-config.mdx +++ b/snippets/compressible-default-rent-config.mdx @@ -1,3 +1,3 @@ ### Default Rent Config -1. At account creation, you pay ~22,208 lamports for 24h of rent
    and compression incentive (the rent-exemption is sponsored by the protocol) -2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 766 lamports when the account's rent is below 3h. +1. At account creation, you pay ~17,208 lamports for 24h of rent
    and compression incentive (the rent-exemption is sponsored by the protocol) +2. Transfers keep the account funded with rent for 3h via top-ups. The transaction payer tops up 776 lamports when the account's rent is below 3h. diff --git a/snippets/compressible-rent-explained.mdx b/snippets/compressible-rent-explained.mdx index 145e23f8..17aebab1 100644 --- a/snippets/compressible-rent-explained.mdx +++ b/snippets/compressible-rent-explained.mdx @@ -19,8 +19,8 @@ This way rent is only paid when accounts are used: Account Creation - Total: 22,208 lamports
    - 11,208 lamports
    + Total: 17,208 lamports
    + 6,208 lamports
    11,000 lamports (compression incentive)
    Transaction payer @@ -28,15 +28,15 @@ This way rent is only paid when accounts are used: Top ups
    (when rent < 3h) - 766 lamports
    + 776 lamports
    Transaction payer Funds 3h rent Load Account
    (when account inactive) - Total: 22,208 lamports
    - 11,208 lamports
    + Total: 17,208 lamports
    + 6,208 lamports
    11,000 lamports (compression incentive)
    Transaction payer diff --git a/snippets/jsx/compressible-rent-calculator.jsx b/snippets/jsx/compressible-rent-calculator.jsx index f05601c5..9dd81f05 100644 --- a/snippets/jsx/compressible-rent-calculator.jsx +++ b/snippets/jsx/compressible-rent-calculator.jsx @@ -1,6 +1,6 @@ export const CompressibleRentCalculator = () => { const [hours, setHours] = useState(24); - const [lamportsPerWrite, setLamportsPerWrite] = useState(766); + const [lamportsPerWrite, setLamportsPerWrite] = useState(776); const [showCustomHours, setShowCustomHours] = useState(false); const [showCustomLamports, setShowCustomLamports] = useState(false); const [showFormula, setShowFormula] = useState(false); @@ -10,7 +10,6 @@ export const CompressibleRentCalculator = () => { const LAMPORTS_PER_BYTE_PER_EPOCH = 1; const MINUTES_PER_EPOCH = 90; const COMPRESSION_INCENTIVE = 11000; - const TX_COST = 5000; const LAMPORTS_PER_SOL = 1_000_000_000; const HOURS_MAX = 36; @@ -19,7 +18,7 @@ export const CompressibleRentCalculator = () => { const numEpochs = Math.ceil((hours * 60) / MINUTES_PER_EPOCH); const rentPerEpoch = BASE_RENT + (DATA_LEN * LAMPORTS_PER_BYTE_PER_EPOCH); const totalPrepaidRent = rentPerEpoch * numEpochs; - const totalCreationCost = totalPrepaidRent + COMPRESSION_INCENTIVE + TX_COST; + const totalCreationCost = totalPrepaidRent + COMPRESSION_INCENTIVE; const handleHoursChange = (value) => { const num = Math.max(3, Math.min(168, Number.parseInt(value) || 3)); @@ -32,7 +31,7 @@ export const CompressibleRentCalculator = () => { }; const hoursPresets = [24]; - const lamportsPresets = [766]; + const lamportsPresets = [776]; const SliderMarkers = ({ max, step }) => { const marks = []; @@ -123,7 +122,7 @@ export const CompressibleRentCalculator = () => { : 'bg-black/[0.015] dark:bg-white/5 border-black/[0.04] dark:border-white/20 text-zinc-600 dark:text-white/70 hover:bg-black/[0.03]' }`} > - {l === 766 ? 'Default' : l.toLocaleString()} + {l === 776 ? 'Default' : l.toLocaleString()} ))} {showCustomLamports ? ( @@ -198,7 +197,7 @@ export const CompressibleRentCalculator = () => { {showFormula && (
    Total cost for {DATA_LEN}-byte c-Token account:
    - total_creation_cost = prepaid_rent + compression_incentive + tx_cost

    + total_creation_cost = prepaid_rent + compression_incentive

    rent_per_epoch = base_rent + (data_len × lamports_per_byte_per_epoch)
    rent_per_epoch = {BASE_RENT} + ({DATA_LEN} × {LAMPORTS_PER_BYTE_PER_EPOCH}) = {rentPerEpoch} lamports
    compression_incentive = {COMPRESSION_INCENTIVE.toLocaleString()} lamports diff --git a/snippets/jsx/ctoken-vs-spl-calculator.jsx b/snippets/jsx/ctoken-vs-spl-calculator.jsx index dac0f5c0..f2636b60 100644 --- a/snippets/jsx/ctoken-vs-spl-calculator.jsx +++ b/snippets/jsx/ctoken-vs-spl-calculator.jsx @@ -5,7 +5,7 @@ export const CTokenVsSplCalculator = () => { const ACCOUNT_STORAGE_OVERHEAD = 128; const LAMPORTS_PER_BYTE = 6960; const DATA_LEN = 165; // SPL token account size - const CTOKEN_DEFAULT_CREATION_COST = 22208; // Default rent config: 11,000 rent + 11,000 compression + 208 base + const CTOKEN_DEFAULT_CREATION_COST = 17208; // Default rent config: 6,208 prepaid rent (24h) + 11,000 compression incentive const LAMPORTS_PER_SOL = 1_000_000_000; const ACCOUNTS_MAX = 1000000; From f0da3f98e5b33c97a1717fb7d08d2890ccb28c8b Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sat, 6 Dec 2025 17:49:19 +0000 Subject: [PATCH 087/143] Refactor c-Mint and mint-ctoken code examples for consistency - Rename cmint_params to params in create-cmint.mdx - Rename mint_params to params in mint-ctokens.mdx - Replace test code with handler examples in mint-ctokens.mdx - Add detailed inline documentation for invoke and invoke_signed patterns --- .../cmint/create-cmint.mdx | 12 +- .../cmint/mint-ctokens.mdx | 550 +++++------------- 2 files changed, 156 insertions(+), 406 deletions(-) diff --git a/compressed-token-program/cmint/create-cmint.mdx b/compressed-token-program/cmint/create-cmint.mdx index 3e2408a1..b996fd36 100644 --- a/compressed-token-program/cmint/create-cmint.mdx +++ b/compressed-token-program/cmint/create-cmint.mdx @@ -203,7 +203,7 @@ Set `decimals`, `mint_authority`, `freeze_authority`, and pass the `token_metada ```rust use light_ctoken_sdk::ctoken::CreateCMintParams; -let cmint_params = CreateCMintParams { +let params = CreateCMintParams { decimals: data.decimals, address_merkle_tree_root_index: data.address_merkle_tree_root_index, mint_authority: data.mint_authority, @@ -211,7 +211,7 @@ let cmint_params = CreateCMintParams { compression_address: data.compression_address, mint: data.mint, freeze_authority: data.freeze_authority, - extensions: data.token_metadata, + extensions: data.extensions, }; ``` @@ -251,7 +251,7 @@ let system_accounts = SystemAccountInfos { ### Build Account Infos and CPI the c-Token Program 1. Pass the required accounts -2. Include `cmint_params` and `system_accounts` from the previous steps +2. Include `params` and `system_accounts` from the previous steps 3. Use `invoke` or `invoke_signed`: * When `mint_signer` is an external keypair, use `invoke`. * When `mint_signer` is a PDA, use `invoke_signed` with its seeds. @@ -273,7 +273,7 @@ CreateCMintAccountInfos { system_accounts, cpi_context: None, cpi_context_account: None, - cmint_params, + params, } .invoke()?; ``` @@ -293,7 +293,7 @@ let account_infos = CreateCMintAccountInfos { system_accounts, cpi_context: None, cpi_context_account: None, - cmint_params, + params, }; let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]]; @@ -315,7 +315,7 @@ let account_infos = CreateCMintAccountInfos { system_accounts, cpi_context: None, cpi_context_account: None, - cmint_params, + params, }; let mint_signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_signer_bump]]; diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index 7f0e99be..1de184e0 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -250,7 +250,7 @@ The client passes a validity proof that @@ -310,7 +310,7 @@ MintToCTokenInfos { system_accounts, cpi_context: None, cpi_context_account: None, - mint_params, + params, } .invoke()?; ``` @@ -331,7 +331,7 @@ let account_infos = MintToCTokenInfos { system_accounts, cpi_context: None, cpi_context_account: None, - mint_params, + params, }; let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; @@ -355,422 +355,172 @@ Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob ```rust expandable -// Tests for MintToCTokenInfos (MintToCtoken instruction) - -mod shared; - use borsh::{BorshDeserialize, BorshSerialize}; -use light_client::{indexer::Indexer, rpc::Rpc}; -use light_ctoken_sdk::{ - compressed_token::mint_action::MintActionMetaConfig, ctoken::CTOKEN_PROGRAM_ID, -}; -use light_program_test::{LightProgramTest, ProgramTestConfig}; -use native_ctoken_examples::{ - CreateCmintData, CreateTokenAccountData, MintToCTokenData, ID, MINT_AUTHORITY_SEED, - MINT_SIGNER_SEED, -}; -use shared::setup_create_compressed_mint; -use solana_sdk::{ - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - signature::Keypair, - signer::Signer, -}; - -/// Test minting tokens to a ctoken account using MintToCTokenInfos::invoke() -#[tokio::test] -async fn test_mint_to_ctoken() { - let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( - false, - Some(vec![("native_ctoken_examples", ID)]), - )) - .await - .unwrap(); - - let payer = rpc.get_payer().insecure_clone(); - let mint_authority = payer.pubkey(); - - // Setup: Create compressed mint directly (not via wrapper program) - let (mint_pda, compression_address, _) = - setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; +use light_ctoken_interface::instructions::mint_action::CompressedMintWithContext; +use light_ctoken_sdk::ctoken::{MintToCTokenInfos, MintToCTokenParams, SystemAccountInfos}; +use light_sdk::instruction::ValidityProof; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + +use crate::ID; + +/// PDA seed for mint authority in invoke_signed variant +pub const MINT_AUTHORITY_SEED: &[u8] = b"mint_authority"; + +/// Instruction data for mint_to_ctoken operations +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct MintToCTokenData { + pub compressed_mint_inputs: CompressedMintWithContext, + pub amount: u64, + pub mint_authority: Pubkey, + pub proof: ValidityProof, +} - let ctoken_account = Keypair::new(); - let owner = payer.pubkey(); - // Create a ctoken account to mint tokens to via wrapper program - { - let create_token_account_data = CreateTokenAccountData { - owner, - pre_pay_num_epochs: 2, - lamports_per_write: 1, - }; - let instruction_data = - [vec![2u8], create_token_account_data.try_to_vec().unwrap()].concat(); - - use light_ctoken_sdk::ctoken::{config_pda, rent_sponsor_pda}; - let config = config_pda(); - let rent_sponsor = rent_sponsor_pda(); - - let instruction = Instruction { - program_id: ID, - accounts: vec![ - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new(ctoken_account.pubkey(), true), - AccountMeta::new_readonly(mint_pda, false), - AccountMeta::new_readonly(config, false), - AccountMeta::new_readonly(Pubkey::default(), false), // system_program - AccountMeta::new(rent_sponsor, false), - AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), // token_program - ], - data: instruction_data, - }; - - rpc.create_and_send_transaction( - &[instruction], - &payer.pubkey(), - &[&payer, &ctoken_account], - ) - .await - .unwrap(); +/// Handler for minting tokens to compressed token accounts +/// +/// Uses the MintToCTokenInfos builder pattern. This demonstrates how to: +/// 1. Build MintToCTokenParams using the constructor +/// 2. Build MintToCTokenInfos with accounts and params +/// 3. Call invoke() which handles instruction building and CPI +/// +/// Account order (all accounts from SDK-generated instruction): +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: authority (mint_authority) +/// - accounts[3]: fee_payer +/// - accounts[4]: cpi_authority_pda +/// - accounts[5]: registered_program_pda +/// - accounts[6]: account_compression_authority +/// - accounts[7]: account_compression_program +/// - accounts[8]: system_program +/// - accounts[9]: output_queue +/// - accounts[10]: state_tree +/// - accounts[11]: input_queue +/// - accounts[12..]: ctoken_accounts (variable length - destination accounts) +pub fn process_mint_to_ctoken( + accounts: &[AccountInfo], + data: MintToCTokenData, +) -> Result<(), ProgramError> { + if accounts.len() < 13 { + return Err(ProgramError::NotEnoughAccountKeys); } - // Get the compressed mint account to build CompressedMintWithContext - let compressed_mint_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - // Deserialize the compressed mint data - use light_ctoken_interface::state::CompressedMint; - let compressed_mint = - CompressedMint::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) - .unwrap(); + // Build params using the constructor + let params = MintToCTokenParams::new( + data.compressed_mint_inputs, + data.amount, + data.mint_authority, + data.proof, + ); - let amount = 1_000_000_000u64; // 1 token with 9 decimals + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[4].clone(), + registered_program_pda: accounts[5].clone(), + account_compression_authority: accounts[6].clone(), + account_compression_program: accounts[7].clone(), + system_program: accounts[8].clone(), + }; - // Mint ctokens with test program. - { - // Get validity proof for the mint operation - let rpc_result = rpc - .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) - .await - .unwrap() - .value; - - // Build CompressedMintWithContext from the compressed account - let compressed_mint_with_context = - light_ctoken_interface::instructions::mint_action::CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_mint_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), // Will be updated with validity proof - mint: compressed_mint.try_into().unwrap(), - }; - // Build instruction data for wrapper program - let mint_to_data = MintToCTokenData { - compressed_mint_inputs: compressed_mint_with_context.clone(), - amount, - mint_authority, - proof: rpc_result.proof, - }; - let wrapper_instruction_data = [vec![1u8], mint_to_data.try_to_vec().unwrap()].concat(); - - // Build wrapper instruction with compressed token program as first account - let compressed_token_program_id = - Pubkey::new_from_array(light_ctoken_interface::COMPRESSED_TOKEN_PROGRAM_ID); - - let mut wrapper_accounts = vec![AccountMeta::new_readonly( - compressed_token_program_id, - false, - )]; - let account_metas = MintActionMetaConfig::new( - payer.pubkey(), - mint_authority, - compressed_mint_account.tree_info.tree, - compressed_mint_account.tree_info.queue, - compressed_mint_account.tree_info.queue, - ) - .with_ctoken_accounts(vec![ctoken_account.pubkey()]) - .to_account_metas(); - wrapper_accounts.extend(account_metas); - - let instruction = Instruction { - program_id: ID, - accounts: wrapper_accounts, - data: wrapper_instruction_data, - }; - - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) - .await - .unwrap(); + // Collect ctoken accounts from remaining accounts (index 12 onwards) + let ctoken_accounts: Vec = accounts[12..].to_vec(); + + // Build the account infos struct and invoke + // SDK account order: output_queue (9), tree (10), input_queue (11), ctoken_accounts (12+) + // In this case, payer == authority (accounts[3]) + MintToCTokenInfos { + authority: accounts[2].clone(), // authority from SDK accounts + payer: accounts[3].clone(), // fee_payer from SDK accounts + state_tree: accounts[10].clone(), // tree at index 10 + input_queue: accounts[11].clone(), // input_queue at index 11 + output_queue: accounts[9].clone(), // output_queue at index 9 + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, } + .invoke()?; - // Verify tokens were minted to the ctoken account - let ctoken_account_data = rpc - .get_account(ctoken_account.pubkey()) - .await - .unwrap() - .unwrap(); - - // Parse the account data to verify balance - use light_ctoken_interface::state::CToken; - let account_state = CToken::deserialize(&mut &ctoken_account_data.data[..]).unwrap(); - assert_eq!(account_state.amount, amount, "Token amount should match"); - assert_eq!( - account_state.mint.to_bytes(), - mint_pda.to_bytes(), - "Mint should match" - ); - assert_eq!( - account_state.owner.to_bytes(), - owner.to_bytes(), - "Owner should match" - ); + Ok(()) } -/// Test minting tokens with PDA mint authority using MintToCTokenInfos::invoke_signed() +/// Handler for minting tokens with PDA mint authority (invoke_signed) /// -/// This test uses the wrapper program to: -/// 1. Create a compressed mint with PDA authority (discriminator 14 - CreateCmintWithPdaAuthority) -/// 2. Mint tokens using PDA authority (discriminator 13 - MintToCtokenInvokeSigned) -#[tokio::test] -async fn test_mint_to_ctoken_invoke_signed() { - let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( - false, - Some(vec![("native_ctoken_examples", ID)]), - )) - .await - .unwrap(); - - let payer = rpc.get_payer().insecure_clone(); - - // Derive both PDAs from our wrapper program - let (mint_signer_pda, _) = Pubkey::find_program_address(&[MINT_SIGNER_SEED], &ID); - let (mint_authority_pda, _) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); - - let decimals = 9u8; - let address_tree = rpc.get_address_tree_v2(); - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Derive compression address using the PDA mint_signer - let compression_address = light_ctoken_sdk::ctoken::derive_compressed_mint_address( - &mint_signer_pda, - &address_tree.tree, - ); - - let mint_pda = light_ctoken_sdk::ctoken::find_spl_mint_address(&mint_signer_pda).0; - - let rpc_result = rpc - .get_validity_proof( - vec![], - vec![light_client::indexer::AddressWithTree { - address: compression_address, - tree: address_tree.tree, - }], - None, - ) - .await - .unwrap() - .value; - - let compressed_token_program_id = - Pubkey::new_from_array(light_ctoken_interface::COMPRESSED_TOKEN_PROGRAM_ID); - let default_pubkeys = light_ctoken_sdk::utils::CTokenDefaultAccounts::default(); - - // Step 1: Create compressed mint with PDA authority using wrapper program (discriminator 14) - { - let create_cmint_data = CreateCmintData { - decimals, - address_merkle_tree_root_index: rpc_result.addresses[0].root_index, - mint_authority: mint_authority_pda, // Will be overridden by the handler - proof: rpc_result.proof.0.unwrap(), - compression_address, - mint: mint_pda, - freeze_authority: None, - extensions: None, - }; - // Discriminator 14 = CreateCmintWithPdaAuthority - let wrapper_instruction_data = - [vec![14u8], create_cmint_data.try_to_vec().unwrap()].concat(); - - // Account order for CreateCmintWithPdaAuthority: - // [0] compressed_token_program, [1] light_system_program, [2] mint_signer (PDA), - // [3] authority (PDA), [4] fee_payer, [5] cpi_authority_pda, [6] registered_program_pda, - // [7] account_compression_authority, [8] account_compression_program, [9] system_program, - // [10] output_queue, [11] address_tree - let wrapper_accounts = vec![ - AccountMeta::new_readonly(compressed_token_program_id, false), - AccountMeta::new_readonly(default_pubkeys.light_system_program, false), - AccountMeta::new_readonly(mint_signer_pda, false), // PDA - program signs - AccountMeta::new(mint_authority_pda, false), // writable PDA - program signs - AccountMeta::new(payer.pubkey(), true), // fee_payer - AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false), - AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_program, false), - AccountMeta::new_readonly(default_pubkeys.system_program, false), - AccountMeta::new(output_queue, false), - AccountMeta::new(address_tree.tree, false), - ]; - - let create_mint_ix = Instruction { - program_id: ID, - accounts: wrapper_accounts, - data: wrapper_instruction_data, - }; - - rpc.create_and_send_transaction(&[create_mint_ix], &payer.pubkey(), &[&payer]) - .await - .unwrap(); +/// Uses the MintToCTokenInfos builder pattern with invoke_signed. +/// The mint authority is a PDA derived from this program. +/// +/// Account order (all accounts from SDK-generated instruction): +/// - accounts[0]: compressed_token_program (for CPI) +/// - accounts[1]: light_system_program +/// - accounts[2]: authority (PDA mint_authority, not signer - program signs) +/// - accounts[3]: fee_payer +/// - accounts[4]: cpi_authority_pda +/// - accounts[5]: registered_program_pda +/// - accounts[6]: account_compression_authority +/// - accounts[7]: account_compression_program +/// - accounts[8]: system_program +/// - accounts[9]: output_queue +/// - accounts[10]: state_tree +/// - accounts[11]: input_queue +/// - accounts[12..]: ctoken_accounts (variable length - destination accounts) +pub fn process_mint_to_ctoken_invoke_signed( + accounts: &[AccountInfo], + data: MintToCTokenData, +) -> Result<(), ProgramError> { + if accounts.len() < 13 { + return Err(ProgramError::NotEnoughAccountKeys); } - let ctoken_account = Keypair::new(); - let owner = payer.pubkey(); + // Derive the PDA for the mint authority + let (pda, bump) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); - // Create a ctoken account to mint tokens to via wrapper program - { - let create_token_account_data = CreateTokenAccountData { - owner, - pre_pay_num_epochs: 2, - lamports_per_write: 1, - }; - let instruction_data = - [vec![2u8], create_token_account_data.try_to_vec().unwrap()].concat(); - - use light_ctoken_sdk::ctoken::{config_pda, rent_sponsor_pda}; - let config = config_pda(); - let rent_sponsor = rent_sponsor_pda(); - - let instruction = Instruction { - program_id: ID, - accounts: vec![ - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new(ctoken_account.pubkey(), true), - AccountMeta::new_readonly(mint_pda, false), - AccountMeta::new_readonly(config, false), - AccountMeta::new_readonly(Pubkey::default(), false), // system_program - AccountMeta::new(rent_sponsor, false), - AccountMeta::new_readonly(CTOKEN_PROGRAM_ID, false), - ], - data: instruction_data, - }; - - rpc.create_and_send_transaction( - &[instruction], - &payer.pubkey(), - &[&payer, &ctoken_account], - ) - .await - .unwrap(); + // Verify the authority account is the PDA we expect + if &pda != accounts[2].key { + return Err(ProgramError::InvalidSeeds); } - // Get the compressed mint account to build CompressedMintWithContext - let compressed_mint_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - // Deserialize the compressed mint data - use light_ctoken_interface::state::CompressedMint; - let compressed_mint = - CompressedMint::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) - .unwrap(); + // Build params using the constructor + let params = MintToCTokenParams::new( + data.compressed_mint_inputs, + data.amount, + data.mint_authority, + data.proof, + ); - let amount = 1_000_000_000u64; // 1 token with 9 decimals + // Build system accounts struct + let system_accounts = SystemAccountInfos { + light_system_program: accounts[1].clone(), + cpi_authority_pda: accounts[4].clone(), + registered_program_pda: accounts[5].clone(), + account_compression_authority: accounts[6].clone(), + account_compression_program: accounts[7].clone(), + system_program: accounts[8].clone(), + }; - // Mint ctokens with PDA authority via invoke_signed - { - // Get validity proof for the mint operation - let rpc_result = rpc - .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) - .await - .unwrap() - .value; - - // Build CompressedMintWithContext from the compressed account - let compressed_mint_with_context = - light_ctoken_interface::instructions::mint_action::CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_mint_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: compressed_mint.try_into().unwrap(), - }; - - // Build instruction data for wrapper program - let mint_to_data = MintToCTokenData { - compressed_mint_inputs: compressed_mint_with_context.clone(), - amount, - mint_authority: mint_authority_pda, - proof: rpc_result.proof, - }; - // Discriminator 13 = MintToCtokenInvokeSigned - let wrapper_instruction_data = [vec![13u8], mint_to_data.try_to_vec().unwrap()].concat(); - - // Build accounts manually since SDK marks authority as signer, but we need it as non-signer - // for invoke_signed (the wrapper program signs via CPI) - let compressed_token_program_id = - Pubkey::new_from_array(light_ctoken_interface::COMPRESSED_TOKEN_PROGRAM_ID); - let default_pubkeys = light_ctoken_sdk::utils::CTokenDefaultAccounts::default(); - - let wrapper_accounts = vec![ - AccountMeta::new_readonly(compressed_token_program_id, false), - AccountMeta::new_readonly(default_pubkeys.light_system_program, false), - // authority NOT marked as signer - program will sign via invoke_signed - AccountMeta::new_readonly(mint_authority_pda, false), - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false), - AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_program, false), - AccountMeta::new_readonly(default_pubkeys.system_program, false), - AccountMeta::new(compressed_mint_account.tree_info.queue, false), // output_queue - AccountMeta::new(compressed_mint_account.tree_info.tree, false), // state_tree - AccountMeta::new(compressed_mint_account.tree_info.queue, false), // input_queue - AccountMeta::new(ctoken_account.pubkey(), false), // ctoken_account - ]; - let instruction = Instruction { - program_id: ID, - accounts: wrapper_accounts, - data: wrapper_instruction_data, - }; - - // Note: only payer signs, the mint_authority PDA is signed by the program via invoke_signed - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - } + // Collect ctoken accounts from remaining accounts (index 12 onwards) + let ctoken_accounts: Vec = accounts[12..].to_vec(); + + // Build the account infos struct + // authority is the PDA (accounts[2]) + let account_infos = MintToCTokenInfos { + authority: accounts[2].clone(), // authority PDA + payer: accounts[3].clone(), // fee_payer from SDK accounts + state_tree: accounts[10].clone(), // tree at index 10 + input_queue: accounts[11].clone(), // input_queue at index 11 + output_queue: accounts[9].clone(), // output_queue at index 9 + ctoken_accounts, + system_accounts, + cpi_context: None, + cpi_context_account: None, + params, + }; - // Verify tokens were minted to the ctoken account - let ctoken_account_data = rpc - .get_account(ctoken_account.pubkey()) - .await - .unwrap() - .unwrap(); + // Invoke with PDA signing + let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; + account_infos.invoke_signed(&[signer_seeds])?; - // Parse the account data to verify balance - use light_ctoken_interface::state::CToken; - let account_state = CToken::deserialize(&mut &ctoken_account_data.data[..]).unwrap(); - assert_eq!(account_state.amount, amount, "Token amount should match"); - assert_eq!( - account_state.mint.to_bytes(), - mint_pda.to_bytes(), - "Mint should match" - ); - assert_eq!( - account_state.owner.to_bytes(), - owner.to_bytes(), - "Owner should match" - ); + Ok(()) } ``` From 9bd4c1ffb5099ecafa59874cc2c255b806e6f142 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sat, 6 Dec 2025 21:54:43 +0000 Subject: [PATCH 088/143] Refactor c-Token docs: standardize param names, improve navigation, add CI workflow --- .github/workflows/sync-handlers.yml | 33 +++++++++++ compressed-token-program/README.mdx | 6 +- .../cmint/mint-ctokens.mdx | 14 +---- .../ctoken/close-ctoken-account.mdx | 4 +- .../ctoken/create-cata.mdx | 2 +- .../ctoken/create-ctoken.mdx | 2 +- .../ctoken/transfer-interface.mdx | 4 +- scripts/sync-docs.sh | 56 +++++++++++++++++++ 8 files changed, 99 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/sync-handlers.yml create mode 100755 scripts/sync-docs.sh diff --git a/.github/workflows/sync-handlers.yml b/.github/workflows/sync-handlers.yml new file mode 100644 index 00000000..a0aeffb0 --- /dev/null +++ b/.github/workflows/sync-handlers.yml @@ -0,0 +1,33 @@ +name: Sync Handler Code + +on: + repository_dispatch: + types: [sync-handlers] + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - name: Checkout docs repo + uses: actions/checkout@v4 + + - name: Checkout light-protocol + uses: actions/checkout@v4 + with: + repository: Lightprotocol/light-protocol + path: light-protocol + ref: ${{ github.event.client_payload.commit }} + + - name: Sync handlers to docs + run: | + chmod +x ./scripts/sync-docs.sh + ./scripts/sync-docs.sh + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + branch: sync-handlers-${{ github.event.client_payload.commit }} + title: "Sync handler code from light-protocol release" + body: | + Auto-generated from light-protocol commit: ${{ github.event.client_payload.commit }} + commit-message: "sync: update Full Code Examples from light-protocol" diff --git a/compressed-token-program/README.mdx b/compressed-token-program/README.mdx index 7848f014..dd5c2013 100644 --- a/compressed-token-program/README.mdx +++ b/compressed-token-program/README.mdx @@ -40,7 +40,7 @@ import CMintGuidesTable from '/snippets/overview-tables/cmint-guides-table.mdx'; -If you simply want to distribute tokens, please refer to [this page](/compressed-tokens). +If you simply want to **distribute tokens**, please refer to [this page](/compressed-tokens). #### Rent Config for c-Tokens @@ -92,9 +92,9 @@ ADD TABLE OF github repository # Next Steps \ No newline at end of file diff --git a/compressed-token-program/cmint/mint-ctokens.mdx b/compressed-token-program/cmint/mint-ctokens.mdx index 1de184e0..644009ad 100644 --- a/compressed-token-program/cmint/mint-ctokens.mdx +++ b/compressed-token-program/cmint/mint-ctokens.mdx @@ -528,23 +528,11 @@ pub fn process_mint_to_ctoken_invoke_signed( # Next Steps - - Mint c-Tokens - - - Transfer Interface c-Token - \ No newline at end of file diff --git a/compressed-token-program/ctoken/close-ctoken-account.mdx b/compressed-token-program/ctoken/close-ctoken-account.mdx index d8833b9d..066b57ba 100644 --- a/compressed-token-program/ctoken/close-ctoken-account.mdx +++ b/compressed-token-program/ctoken/close-ctoken-account.mdx @@ -332,9 +332,9 @@ pub fn process_close_account_invoke_signed(accounts: &[AccountInfo]) -> Result<( # Next Steps diff --git a/compressed-token-program/ctoken/create-cata.mdx b/compressed-token-program/ctoken/create-cata.mdx index c1e6ee03..7ad828de 100644 --- a/compressed-token-program/ctoken/create-cata.mdx +++ b/compressed-token-program/ctoken/create-cata.mdx @@ -357,7 +357,7 @@ pub fn process_create_ata2_invoke_signed( # Next Steps # Next Steps diff --git a/scripts/sync-docs.sh b/scripts/sync-docs.sh new file mode 100755 index 00000000..12112bea --- /dev/null +++ b/scripts/sync-docs.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -e + +# Mapping of source files to docs files +declare -A FILE_MAP=( + ["create_cmint.rs"]="compressed-token-program/cmint/create-cmint.mdx" + ["mint_to_ctoken.rs"]="compressed-token-program/cmint/mint-ctokens.mdx" + ["create_token_account.rs"]="compressed-token-program/ctoken/create-ctoken.mdx" + ["create_ata.rs"]="compressed-token-program/ctoken/create-cata.mdx" + ["close.rs"]="compressed-token-program/ctoken/close-ctoken-account.mdx" + ["transfer_interface.rs"]="compressed-token-program/ctoken/transfer-interface.mdx" +) + +SOURCE_DIR="light-protocol/sdk-tests/sdk-ctoken-test/src" + +for filename in "${!FILE_MAP[@]}"; do + docs_file="${FILE_MAP[$filename]}" + source_file="$SOURCE_DIR/$filename" + + if [[ -f "$source_file" && -f "$docs_file" ]]; then + echo "Syncing $filename → $docs_file" + + # Read the source code + source_code=$(cat "$source_file") + + # Use awk to replace content between ```rust expandable and ``` after "# Full Code Example" + awk -v new_code="$source_code" ' + /^# Full Code Example/ { in_section = 1 } + in_section && /^```rust expandable/ { + print + print new_code + # Skip until closing ``` + while ((getline line) > 0) { + if (line ~ /^```$/) { + print line + in_section = 0 + break + } + } + next + } + { print } + ' "$docs_file" > "$docs_file.tmp" && mv "$docs_file.tmp" "$docs_file" + + echo "✓ Updated $docs_file" + else + if [[ ! -f "$source_file" ]]; then + echo "⚠ Source file not found: $source_file" + fi + if [[ ! -f "$docs_file" ]]; then + echo "⚠ Docs file not found: $docs_file" + fi + fi +done + +echo "Done syncing handler code to docs" From 78dbd765964732b36d70591c0a2ebd0715cd8240 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sat, 6 Dec 2025 22:56:35 +0000 Subject: [PATCH 089/143] Add rent lifecycle visualizer and update c-Token overview navigation --- compressed-token-program/README.mdx | 3 + snippets/jsx/rent-lifecycle-visualizer.jsx | 426 +++++++++++++++++++++ 2 files changed, 429 insertions(+) create mode 100644 snippets/jsx/rent-lifecycle-visualizer.jsx diff --git a/compressed-token-program/README.mdx b/compressed-token-program/README.mdx index dd5c2013..64c3a91f 100644 --- a/compressed-token-program/README.mdx +++ b/compressed-token-program/README.mdx @@ -10,6 +10,7 @@ import SetupEnvironment from '/snippets/setup/setup-environment-tabs.mdx'; import InstallDependencies from '/snippets/setup/install-dependencies-codegroup.mdx'; import { CTokenVsSplCalculator } from '/snippets/jsx/ctoken-vs-spl-calculator.jsx'; import { CompressibleRentCalculator } from '/snippets/jsx/compressible-rent-calculator.jsx'; +import { RentLifecycleVisualizer } from '/snippets/jsx/rent-lifecycle-visualizer.jsx'; import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; import CompressibleDefaultRentConfig from '/snippets/compressible-default-rent-config.mdx'; import CTokenGuidesTable from '/snippets/overview-tables/ctoken-guides-table.mdx'; @@ -44,7 +45,9 @@ If you simply want to **distribute tokens**, please refer to [this page](/compre #### Rent Config for c-Tokens + + Learn the core concepts to the [c-Token program here](/learn/c-token-program). diff --git a/snippets/jsx/rent-lifecycle-visualizer.jsx b/snippets/jsx/rent-lifecycle-visualizer.jsx new file mode 100644 index 00000000..ed798f10 --- /dev/null +++ b/snippets/jsx/rent-lifecycle-visualizer.jsx @@ -0,0 +1,426 @@ +export const RentLifecycleVisualizer = () => { + const [time, setTime] = useState(0); + const [lamports, setLamports] = useState(0); + const [isRunning, setIsRunning] = useState(true); + const [lastTopupTime, setLastTopupTime] = useState(0); + const [phase, setPhase] = useState('uninitialized'); + const [isHighlighted, setIsHighlighted] = useState(false); + const [activeLines, setActiveLines] = useState([]); + const [isButtonPressed, setIsButtonPressed] = useState(false); + + // Constants from rent config + const RENT_PER_EPOCH = 388; + const INITIAL_LAMPORTS = 17208; + const TOPUP_LAMPORTS = 776; + // 388 lamports per second (decreased every 1s tick) + const LAMPORTS_PER_SECOND = RENT_PER_EPOCH; + + // Colors + const GREY = { r: 161, g: 161, b: 170 }; + const RED = { r: 227, g: 89, b: 48 }; + const BLUE = { r: 120, g: 140, b: 180 }; // Subtle grey-blue + + // 7 transaction lines from edges to center (positioned around the visualization) + // Each line has start point (edge) and ends at center (50, 50) + const txLines = [ + { id: 0, x1: 5, y1: 20, x2: 50, y2: 50 }, // top-left + { id: 1, x1: 95, y1: 15, x2: 50, y2: 50 }, // top-right + { id: 2, x1: 0, y1: 50, x2: 50, y2: 50 }, // left + { id: 3, x1: 100, y1: 55, x2: 50, y2: 50 }, // right + { id: 4, x1: 10, y1: 85, x2: 50, y2: 50 }, // bottom-left + { id: 5, x1: 90, y1: 90, x2: 50, y2: 50 }, // bottom-right + { id: 6, x1: 50, y1: 0, x2: 50, y2: 50 }, // top + ]; + + const interpolateColor = (c1, c2, t) => { + const clamp = (v) => Math.max(0, Math.min(255, Math.round(v))); + return { + r: clamp(c1.r + (c2.r - c1.r) * t), + g: clamp(c1.g + (c2.g - c1.g) * t), + b: clamp(c1.b + (c2.b - c1.b) * t), + }; + }; + + const colorToRgba = (c, alpha = 1) => `rgba(${c.r}, ${c.g}, ${c.b}, ${alpha})`; + + const triggerHighlight = () => { + setIsHighlighted(true); + setTimeout(() => setIsHighlighted(false), 500); + }; + + const triggerTransaction = (lineIndex) => { + setActiveLines((prev) => [...prev, { id: lineIndex, startTime: Date.now() }]); + setTimeout(() => { + setActiveLines((prev) => prev.filter((l) => l.id !== lineIndex)); + }, 500); + }; + + // Cycle through lines for transactions + const txLineIndexRef = useRef(0); + const getNextLineIndex = () => { + const index = txLineIndexRef.current; + txLineIndexRef.current = (txLineIndexRef.current + 1) % txLines.length; + return index; + }; + + const getAccountColor = () => { + if (phase === 'uninitialized') return GREY; + + const timeSinceTopup = time - lastTopupTime; + + // Stay red for 0.8s, then fade directly to blue over 3s + if (timeSinceTopup < 0.8) { + return RED; + } else { + // Red directly to blue (stays at blue once transition completes) + const t = Math.min(1, (timeSinceTopup - 0.8) / 3); + return interpolateColor(RED, BLUE, t); + } + }; + + const handleTopup = () => { + setLamports((l) => l + TOPUP_LAMPORTS); + setLastTopupTime(time); + if (phase === 'uninitialized' || phase === 'cold') { + setPhase('hot'); + } + triggerHighlight(); + triggerTransaction(getNextLineIndex()); + setIsButtonPressed(true); + setTimeout(() => setIsButtonPressed(false), 200); + }; + + const handleReset = () => { + setTime(0); + setLamports(0); + setPhase('uninitialized'); + setLastTopupTime(0); + setIsRunning(true); + setActiveLines([]); + txLineIndexRef.current = 0; + }; + + // Track last second for 1-second lamport decreases + const lastSecondRef = useRef(0); + + useEffect(() => { + if (!isRunning) return; + + const interval = setInterval(() => { + setTime((t) => { + const newTime = t + 0.1; + const currentSecond = Math.floor(newTime); + + // Initialize at t=0 (instant) + if (t === 0 && newTime > 0) { + setLamports(INITIAL_LAMPORTS); + setLastTopupTime(0); + setPhase('hot'); + triggerHighlight(); + triggerTransaction(getNextLineIndex()); + lastSecondRef.current = 0; + } + + // Auto top-ups (faster cycle) + if (newTime >= 2 && t < 2) { + setLamports((l) => l + TOPUP_LAMPORTS); + setLastTopupTime(2); + triggerHighlight(); + triggerTransaction(getNextLineIndex()); + } + if (newTime >= 2.5 && t < 2.5) { + setLamports((l) => l + TOPUP_LAMPORTS); + setLastTopupTime(2.5); + triggerHighlight(); + triggerTransaction(getNextLineIndex()); + } + if (newTime >= 3 && t < 3) { + setLamports((l) => l + TOPUP_LAMPORTS); + setLastTopupTime(3); + triggerHighlight(); + triggerTransaction(getNextLineIndex()); + } + if (newTime >= 3.3 && t < 3.3) { + setLamports((l) => l + TOPUP_LAMPORTS); + setLastTopupTime(3.3); + triggerHighlight(); + triggerTransaction(getNextLineIndex()); + } + + // First cold at ~5.5s + if (newTime >= 5.5 && t < 5.5) { + setPhase('cold'); + setLamports(0); + } + + // Re-initialize (Load) at 6s + if (newTime >= 6 && t < 6) { + setLamports(INITIAL_LAMPORTS); + setLastTopupTime(6); + setPhase('hot'); + triggerHighlight(); + triggerTransaction(getNextLineIndex()); + } + + // Second cold at ~8.5s + if (newTime >= 8.5 && t < 8.5) { + setPhase('cold'); + setLamports(0); + } + + // Loop at 10s + if (newTime >= 10) { + setPhase('uninitialized'); + setLamports(0); + setLastTopupTime(0); + txLineIndexRef.current = 0; + lastSecondRef.current = 0; + return 0; + } + + // Decrease lamports every 1 second when hot + if (phase === 'hot' && currentSecond > lastSecondRef.current) { + setLamports((l) => Math.max(0, l - LAMPORTS_PER_SECOND)); + lastSecondRef.current = currentSecond; + } + + return newTime; + }); + }, 100); + + return () => clearInterval(interval); + }, [isRunning, phase]); + + const displayHours = Math.round(time); + const accountColor = getAccountColor(); + + // Diamond dots pattern + const generateDiamondDots = () => { + const dots = []; + const size = 5; + const centerSize = 4; + + for (let row = -size; row <= size; row++) { + const width = size - Math.abs(row); + for (let col = -width; col <= width; col++) { + const distFromCenter = Math.max(Math.abs(row), Math.abs(col)); + const dotSize = Math.max(1, centerSize - distFromCenter * 0.6); + const opacity = Math.max(0.2, 1 - distFromCenter * 0.15); + + dots.push({ + x: 50 + col * 6, + y: 50 + row * 6, + size: dotSize, + opacity, + }); + } + } + return dots; + }; + + const diamondDots = generateDiamondDots(); + + const isLineActive = (lineId) => activeLines.some((l) => l.id === lineId); + + return ( +
    + {/* CSS for animations */} + + + {/* Main visualization area */} +
    + {/* Transaction lines SVG layer */} + + {txLines.map((line) => { + const active = isLineActive(line.id); + return ( + + {/* Line */} + + {/* Bobble that travels along the line when active */} + {active && ( + + )} + + ); + })} + + + {/* Timeline with fading edges */} +
    + {/* Continuously scrolling tick marks */} +
    +
    + {Array.from({ length: 40 }).map((_, i) => ( +
    + ))} +
    +
    + + {/* Center line */} +
    +
    + + {/* Diamond Account - centered */} +
    { + const timeSinceTopup = time - lastTopupTime; + if (timeSinceTopup < 1.5) { + // Hot glow fading out + const intensity = Math.max(0, 1 - timeSinceTopup / 1.5); + return `drop-shadow(0 0 ${20 * intensity + 5}px rgba(227, 89, 48, ${0.7 * intensity})) drop-shadow(0 0 ${8 * intensity + 2}px rgba(255, 150, 50, ${0.8 * intensity}))`; + } + return 'none'; + })(), + transition: 'filter 0.3s ease', + }} + > + + {diamondDots.map((dot, i) => ( + + ))} + +
    +
    + + {/* Counters */} +
    + {/* Left: Time at 45% with padding to push right */} +
    +
    + {displayHours}h +
    +
    + Time +
    +
    + + {/* Right: Rent Balance at 55% */} +
    +
    + + {lamports.toLocaleString()} + + lamports +
    +
    + Rent Balance +
    +
    +
    + + {/* Buttons */} +
    +
    + +
    +
    + +
    +
    +
    + ); +}; From cd71b1fe58f3a298cbf6be1ae80b7e531ab6b547 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sat, 6 Dec 2025 23:07:52 +0000 Subject: [PATCH 090/143] Improve rent visualizer: add arrow animation and enhance button styling --- snippets/jsx/rent-lifecycle-visualizer.jsx | 81 +++++++++++++++++++--- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/snippets/jsx/rent-lifecycle-visualizer.jsx b/snippets/jsx/rent-lifecycle-visualizer.jsx index ed798f10..16793dba 100644 --- a/snippets/jsx/rent-lifecycle-visualizer.jsx +++ b/snippets/jsx/rent-lifecycle-visualizer.jsx @@ -5,6 +5,7 @@ export const RentLifecycleVisualizer = () => { const [lastTopupTime, setLastTopupTime] = useState(0); const [phase, setPhase] = useState('uninitialized'); const [isHighlighted, setIsHighlighted] = useState(false); + const [activeArrows, setActiveArrows] = useState([]); const [activeLines, setActiveLines] = useState([]); const [isButtonPressed, setIsButtonPressed] = useState(false); @@ -43,9 +44,18 @@ export const RentLifecycleVisualizer = () => { const colorToRgba = (c, alpha = 1) => `rgba(${c.r}, ${c.g}, ${c.b}, ${alpha})`; + const arrowIdRef = useRef(0); + const triggerHighlight = () => { setIsHighlighted(true); setTimeout(() => setIsHighlighted(false), 500); + + // Add a new arrow with unique ID + const arrowId = arrowIdRef.current++; + setActiveArrows((prev) => [...prev, arrowId]); + setTimeout(() => { + setActiveArrows((prev) => prev.filter((id) => id !== arrowId)); + }, 500); }; const triggerTransaction = (lineIndex) => { @@ -97,7 +107,9 @@ export const RentLifecycleVisualizer = () => { setLastTopupTime(0); setIsRunning(true); setActiveLines([]); + setActiveArrows([]); txLineIndexRef.current = 0; + arrowIdRef.current = 0; }; // Track last second for 1-second lamport decreases @@ -247,11 +259,55 @@ export const RentLifecycleVisualizer = () => { } @keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); font-weight: 500; } - 50% { opacity: 0.85; transform: scale(1.06); font-weight: 700; } + 50% { opacity: 0.8; transform: scale(1.12); font-weight: 700; } } .topup-pulse { animation: pulse 2s ease-in-out infinite; } + .btn-interactive { + position: relative; + overflow: hidden; + background: rgba(120, 140, 180, 0.08); + border-color: rgba(120, 140, 180, 0.25); + color: rgb(90, 110, 150); + } + .dark .btn-interactive { + background: rgba(120, 140, 180, 0.12); + border-color: rgba(120, 140, 180, 0.3); + color: rgba(180, 200, 230, 0.9); + } + .btn-interactive::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(120, 140, 180, 0.15), transparent); + transition: left 0.5s ease; + } + .btn-interactive:hover::before { + left: 100%; + } + .btn-interactive:hover { + background: rgba(120, 140, 180, 0.15); + border-color: rgba(120, 140, 180, 0.4); + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(120, 140, 180, 0.2); + } + .dark .btn-interactive:hover { + background: rgba(120, 140, 180, 0.2); + box-shadow: 0 4px 12px rgba(120, 140, 180, 0.15); + } + @keyframes arrowUp { + 0% { opacity: 0; transform: translateY(4px); } + 20% { opacity: 1; } + 80% { opacity: 1; } + 100% { opacity: 0; transform: translateY(-8px); } + } + .arrow-up { + animation: arrowUp 0.5s ease-out forwards; + } `} {/* Main visualization area */} @@ -366,6 +422,17 @@ export const RentLifecycleVisualizer = () => { {/* Right: Rent Balance at 55% */}
    +
    + {activeArrows.map((arrowId) => ( + + ↑ + + ))} +
    {
    -
    +
    {/* Right: Rent Balance at 55% */} -
    +
    + {/* Arrows container - positioned absolutely so it doesn't move with lamports */} +
    + {activeArrows.map((arrowId) => ( + + ↑ + + ))} + {/* Flying arrows from button */} + {flyingArrows.map((id) => ( + + ↑ + + ))} +
    -
    - {activeArrows.map((arrowId) => ( - - ↑ - - ))} -
    { fontWeight: isHighlighted ? 700 : 500, transformOrigin: 'center', transform: isHighlighted ? 'scale(1.05)' : 'scale(1)', + fontVariantNumeric: 'tabular-nums', + minWidth: '5.5rem', + textAlign: 'right', }} > {lamports.toLocaleString()} @@ -447,8 +491,8 @@ export const RentLifecycleVisualizer = () => { lamports
    @@ -458,29 +502,30 @@ export const RentLifecycleVisualizer = () => {
    - {/* Buttons */} -
    + {/* Buttons row: Back to Start under Time, Top Up centered under Rent Balance */} +
    -
    +
    + {/* Top Up button centered under Rent Balance */}
    From 1be822f5f45ad3212a8fd1d69240b34230f74d89 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 7 Dec 2025 00:02:00 +0000 Subject: [PATCH 092/143] Refactor rent visualizer: epoch-based rent decay and realistic lifecycle --- snippets/jsx/rent-lifecycle-visualizer.jsx | 203 +++++++++++---------- 1 file changed, 108 insertions(+), 95 deletions(-) diff --git a/snippets/jsx/rent-lifecycle-visualizer.jsx b/snippets/jsx/rent-lifecycle-visualizer.jsx index c8422896..1d269a55 100644 --- a/snippets/jsx/rent-lifecycle-visualizer.jsx +++ b/snippets/jsx/rent-lifecycle-visualizer.jsx @@ -2,7 +2,6 @@ export const RentLifecycleVisualizer = () => { const [time, setTime] = useState(0); const [lamports, setLamports] = useState(0); const [isRunning, setIsRunning] = useState(true); - const [lastTopupTime, setLastTopupTime] = useState(0); const [phase, setPhase] = useState('uninitialized'); const [isHighlighted, setIsHighlighted] = useState(false); const [activeArrows, setActiveArrows] = useState([]); @@ -11,11 +10,12 @@ export const RentLifecycleVisualizer = () => { const [flyingArrows, setFlyingArrows] = useState([]); // Constants from rent config - const RENT_PER_EPOCH = 388; - const INITIAL_LAMPORTS = 17208; - const TOPUP_LAMPORTS = 776; - // 388 lamports per second (decreased every 1s tick) - const LAMPORTS_PER_SECOND = RENT_PER_EPOCH; + const RENT_PER_EPOCH = 388; // 388 lamports per epoch (1.5h) + const INITIAL_RENT = 6208; // 24h of rent (16 epochs × 388) + const TOPUP_LAMPORTS = 776; // 3h worth (2 epochs) + const TOPUP_THRESHOLD = 776; // Top up when below 3h of rent + const COLD_THRESHOLD = 388; // Cold when below 1 epoch of rent + // Time scale: 0.5s per epoch → 16 epochs = 8s to deplete, 12s total cycle // Colors const GREY = { r: 161, g: 161, b: 170 }; @@ -85,38 +85,71 @@ export const RentLifecycleVisualizer = () => { const getAccountColor = () => { if (phase === 'uninitialized') return GREY; + if (phase === 'cold') return BLUE; - const timeSinceTopup = time - lastTopupTime; - - // Stay red for 0.8s, then fade directly to blue over 3s - if (timeSinceTopup < 0.8) { + // Color based on lamports threshold + if (lamports > TOPUP_THRESHOLD) { + // Above 776: stay red (hot) return RED; - } else { - // Red directly to blue (stays at blue once transition completes) - const t = Math.min(1, (timeSinceTopup - 0.8) / 3); + } else if (lamports > COLD_THRESHOLD) { + // Between 776 and 388: fade red → blue + const t = 1 - (lamports - COLD_THRESHOLD) / (TOPUP_THRESHOLD - COLD_THRESHOLD); return interpolateColor(RED, BLUE, t); + } else { + // Below 388: cold (blue) + return BLUE; } }; const handleTopup = () => { - // If balance is 0, initialize with full amount; otherwise add top-up amount - setLamports((l) => l === 0 ? INITIAL_LAMPORTS : l + TOPUP_LAMPORTS); - setLastTopupTime(time); - if (phase === 'uninitialized' || phase === 'cold') { - setPhase('hot'); - } - triggerHighlight(); + // Always trigger a transaction triggerTransaction(getNextLineIndex()); - triggerFlyingArrow(); setIsButtonPressed(true); setTimeout(() => setIsButtonPressed(false), 200); + + // If cold/uninitialized, re-initialize with full amount + if (phase === 'uninitialized' || phase === 'cold' || lamports === 0) { + setLamports(INITIAL_RENT); + setPhase('hot'); + triggerHighlight(); + triggerFlyingArrow(); + return; + } + + // Only top up if below threshold (3h = 776 lamports) + if (lamports < TOPUP_THRESHOLD) { + setLamports((l) => l + TOPUP_LAMPORTS); + triggerHighlight(); + triggerFlyingArrow(); + } + // Otherwise just the transaction happens (no rent top-up needed) }; + // Track epoch ticks (every 0.5s = 1.5h = 1 epoch) + const lastEpochRef = useRef(0); + // 30-second transaction schedule with varied patterns for good DX + const txTimesRef = useRef([ + // Phase 1: Burst of activity (0-5s) - shows active account + 0.5, 1, 1.5, 2, 2.5, 3, 4, 5, + // Phase 2: Top-ups as rent depletes (~7-10s) + 7.6, 8.7, 9.7, + // Phase 3: Account goes cold ~11s, reinit at 11.3s + 11.3, + // Phase 4: Second cycle activity (12-16s) + 12.5, 13.5, 14.5, 15.5, + // Phase 5: Top-ups again (~17-19s) + 17.5, 18.5, 19.5, + // Phase 6: Goes cold ~21s, reinit at 21.5s + 21.5, + // Phase 7: Final burst (22-26s) + 22.5, 23.5, 24.5, 25.5, + // Phase 8: Let it drain and go cold before loop (26-30s) + ]); + const handleReset = () => { setTime(0); setLamports(0); setPhase('uninitialized'); - setLastTopupTime(0); setIsRunning(true); setActiveLines([]); setActiveArrows([]); @@ -124,92 +157,76 @@ export const RentLifecycleVisualizer = () => { txLineIndexRef.current = 0; arrowIdRef.current = 0; flyingArrowIdRef.current = 0; + lastEpochRef.current = 0; }; - // Track last second for 1-second lamport decreases - const lastSecondRef = useRef(0); - useEffect(() => { if (!isRunning) return; const interval = setInterval(() => { setTime((t) => { const newTime = t + 0.1; - const currentSecond = Math.floor(newTime); + // 0.5s per epoch: epoch = floor(time / 0.5) = floor(time * 2) + const currentEpoch = Math.floor(newTime * 2); // Initialize at t=0 (instant) if (t === 0 && newTime > 0) { - setLamports(INITIAL_LAMPORTS); - setLastTopupTime(0); + setLamports(INITIAL_RENT); setPhase('hot'); triggerHighlight(); triggerTransaction(getNextLineIndex()); - lastSecondRef.current = 0; - } - - // Auto top-ups (faster cycle) - if (newTime >= 2 && t < 2) { - setLamports((l) => l + TOPUP_LAMPORTS); - setLastTopupTime(2); - triggerHighlight(); - triggerTransaction(getNextLineIndex()); - } - if (newTime >= 2.5 && t < 2.5) { - setLamports((l) => l + TOPUP_LAMPORTS); - setLastTopupTime(2.5); - triggerHighlight(); - triggerTransaction(getNextLineIndex()); - } - if (newTime >= 3 && t < 3) { - setLamports((l) => l + TOPUP_LAMPORTS); - setLastTopupTime(3); - triggerHighlight(); - triggerTransaction(getNextLineIndex()); - } - if (newTime >= 3.3 && t < 3.3) { - setLamports((l) => l + TOPUP_LAMPORTS); - setLastTopupTime(3.3); - triggerHighlight(); - triggerTransaction(getNextLineIndex()); - } - - // First cold at ~5.5s - if (newTime >= 5.5 && t < 5.5) { - setPhase('cold'); - setLamports(0); + lastEpochRef.current = 0; } - // Re-initialize (Load) at 6s - if (newTime >= 6 && t < 6) { - setLamports(INITIAL_LAMPORTS); - setLastTopupTime(6); - setPhase('hot'); - triggerHighlight(); - triggerTransaction(getNextLineIndex()); - } + // Check for scheduled transactions + txTimesRef.current.forEach((txTime) => { + if (newTime >= txTime && t < txTime) { + // Transaction happens - show line animation + triggerTransaction(getNextLineIndex()); + + // Handle based on current state + if (phase === 'cold') { + // Reinitialize from cold state + setLamports(INITIAL_RENT); + setPhase('hot'); + triggerHighlight(); + triggerFlyingArrow(); + } else if (phase === 'hot') { + // Only top up if below threshold (3h = 776 lamports = 2 epochs) + setLamports((currentLamports) => { + if (currentLamports > 0 && currentLamports < TOPUP_THRESHOLD) { + triggerHighlight(); + triggerFlyingArrow(); + return currentLamports + TOPUP_LAMPORTS; + } + return currentLamports; + }); + } + } + }); - // Second cold at ~8.5s - if (newTime >= 8.5 && t < 8.5) { - setPhase('cold'); - setLamports(0); + // Decrease lamports every epoch (0.5s = 1.5h) when hot + if (phase === 'hot' && currentEpoch > lastEpochRef.current && newTime > 0.1) { + setLamports((l) => { + const newLamports = Math.max(0, l - RENT_PER_EPOCH); + // Go cold when lamports below cold threshold + if (newLamports < COLD_THRESHOLD) { + setPhase('cold'); + } + return newLamports; + }); + lastEpochRef.current = currentEpoch; } - // Loop at 10s - if (newTime >= 10) { + // Loop at 30s + if (newTime >= 30) { setPhase('uninitialized'); setLamports(0); - setLastTopupTime(0); txLineIndexRef.current = 0; - lastSecondRef.current = 0; + lastEpochRef.current = 0; return 0; } - // Decrease lamports every 1 second when hot - if (phase === 'hot' && currentSecond > lastSecondRef.current) { - setLamports((l) => Math.max(0, l - LAMPORTS_PER_SECOND)); - lastSecondRef.current = currentSecond; - } - return newTime; }); }, 100); @@ -217,7 +234,9 @@ export const RentLifecycleVisualizer = () => { return () => clearInterval(interval); }, [isRunning, phase]); - const displayHours = Math.round(time); + // 0.5 second = 1 epoch = 1.5 hours, so 1 second = 3 hours + // Round to nearest 3h for cleaner display + const displayHours = Math.round(time * 3 / 3) * 3; const accountColor = getAccountColor(); // Diamond dots pattern @@ -402,16 +421,10 @@ export const RentLifecycleVisualizer = () => { left: '50%', top: '50%', transform: 'translate(-50%, -50%)', - filter: (() => { - const timeSinceTopup = time - lastTopupTime; - if (timeSinceTopup < 1.5) { - // Hot glow fading out - const intensity = Math.max(0, 1 - timeSinceTopup / 1.5); - return `drop-shadow(0 0 ${20 * intensity + 5}px rgba(227, 89, 48, ${0.7 * intensity})) drop-shadow(0 0 ${8 * intensity + 2}px rgba(255, 150, 50, ${0.8 * intensity}))`; - } - return 'none'; - })(), - transition: 'filter 0.3s ease', + filter: activeLines.length > 0 + ? 'drop-shadow(0 0 25px rgba(227, 89, 48, 0.7)) drop-shadow(0 0 10px rgba(255, 150, 50, 0.8))' + : 'none', + transition: 'filter 0.15s ease', }} > @@ -525,7 +538,7 @@ export const RentLifecycleVisualizer = () => { transform: isButtonPressed ? 'scale(1.15)' : 'scale(1)', }} > - Press for Top Up + Send Tx
    From a619b8e8e62d7a77a9cfefd991161419914dcebd Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 7 Dec 2025 00:16:31 +0000 Subject: [PATCH 093/143] Improve rent visualizer: smooth tick decay and threshold formatting --- snippets/jsx/rent-lifecycle-visualizer.jsx | 119 ++++++++++++--------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/snippets/jsx/rent-lifecycle-visualizer.jsx b/snippets/jsx/rent-lifecycle-visualizer.jsx index 1d269a55..d79d5e45 100644 --- a/snippets/jsx/rent-lifecycle-visualizer.jsx +++ b/snippets/jsx/rent-lifecycle-visualizer.jsx @@ -10,12 +10,13 @@ export const RentLifecycleVisualizer = () => { const [flyingArrows, setFlyingArrows] = useState([]); // Constants from rent config - const RENT_PER_EPOCH = 388; // 388 lamports per epoch (1.5h) + const LAMPORTS_PER_TICK = 77.6; // 388 per epoch / 5 ticks = smooth decrement + const LAMPORTS_PER_TICK_SLOW = 25; // Slower in critical zone (776-388) for visibility const INITIAL_RENT = 6208; // 24h of rent (16 epochs × 388) const TOPUP_LAMPORTS = 776; // 3h worth (2 epochs) const TOPUP_THRESHOLD = 776; // Top up when below 3h of rent const COLD_THRESHOLD = 388; // Cold when below 1 epoch of rent - // Time scale: 0.5s per epoch → 16 epochs = 8s to deplete, 12s total cycle + // Time scale: 0.5s per epoch → 16 epochs = 8s to deplete, 30s total cycle // Colors const GREY = { r: 161, g: 161, b: 170 }; @@ -45,6 +46,24 @@ export const RentLifecycleVisualizer = () => { const colorToRgba = (c, alpha = 1) => `rgba(${c.r}, ${c.g}, ${c.b}, ${alpha})`; + // Format lamports: ~rounded above 1000, then 776 → 388 as discrete steps, then ticks + const formatLamports = (l) => { + if (l > 1000) { + const rounded = Math.round(l / 500) * 500; + return `~${rounded.toLocaleString()}`; + } + if (l > 776) { + return '~1,000'; // approaching threshold + } + if (l > 388) { + return '776'; // at top-up threshold + } + if (l > 0) { + return '388'; // at cold threshold + } + return '0'; + }; + const arrowIdRef = useRef(0); const flyingArrowIdRef = useRef(0); @@ -125,25 +144,25 @@ export const RentLifecycleVisualizer = () => { // Otherwise just the transaction happens (no rent top-up needed) }; - // Track epoch ticks (every 0.5s = 1.5h = 1 epoch) - const lastEpochRef = useRef(0); - // 30-second transaction schedule with varied patterns for good DX + // 30-second transaction schedule with realistic spam patterns const txTimesRef = useRef([ - // Phase 1: Burst of activity (0-5s) - shows active account - 0.5, 1, 1.5, 2, 2.5, 3, 4, 5, - // Phase 2: Top-ups as rent depletes (~7-10s) - 7.6, 8.7, 9.7, - // Phase 3: Account goes cold ~11s, reinit at 11.3s - 11.3, - // Phase 4: Second cycle activity (12-16s) - 12.5, 13.5, 14.5, 15.5, - // Phase 5: Top-ups again (~17-19s) - 17.5, 18.5, 19.5, - // Phase 6: Goes cold ~21s, reinit at 21.5s - 21.5, - // Phase 7: Final burst (22-26s) - 22.5, 23.5, 24.5, 25.5, - // Phase 8: Let it drain and go cold before loop (26-30s) + // Phase 1: Heavy burst at start (0-5s) - shows active account + 0.3, 0.6, 0.9, 1.2, 1.6, 2, 2.4, 2.8, 3.2, 3.7, 4.2, 4.8, + // Phase 2: Continued activity (5-7s) + 5.3, 5.9, 6.4, + // Phase 3: Top-ups as rent depletes (~7-10s) + 7.2, 7.8, 8.4, 9, 9.6, + // Phase 4: Goes cold, reinit + 10.8, + // Phase 5: Second cycle spam (11-15s) + 11.3, 11.8, 12.3, 12.9, 13.4, 14, 14.6, 15.2, + // Phase 6: Top-ups again (~16-18s) + 16, 16.7, 17.3, 18, + // Phase 7: Goes cold, reinit + 19.5, + // Phase 8: Final burst (20-25s) + 20, 20.5, 21, 21.6, 22.2, 22.8, 23.5, 24.2, 25, + // Phase 9: Let it drain and go cold before loop (25-30s) ]); const handleReset = () => { @@ -157,7 +176,6 @@ export const RentLifecycleVisualizer = () => { txLineIndexRef.current = 0; arrowIdRef.current = 0; flyingArrowIdRef.current = 0; - lastEpochRef.current = 0; }; useEffect(() => { @@ -166,8 +184,6 @@ export const RentLifecycleVisualizer = () => { const interval = setInterval(() => { setTime((t) => { const newTime = t + 0.1; - // 0.5s per epoch: epoch = floor(time / 0.5) = floor(time * 2) - const currentEpoch = Math.floor(newTime * 2); // Initialize at t=0 (instant) if (t === 0 && newTime > 0) { @@ -175,7 +191,6 @@ export const RentLifecycleVisualizer = () => { setPhase('hot'); triggerHighlight(); triggerTransaction(getNextLineIndex()); - lastEpochRef.current = 0; } // Check for scheduled transactions @@ -205,17 +220,18 @@ export const RentLifecycleVisualizer = () => { } }); - // Decrease lamports every epoch (0.5s = 1.5h) when hot - if (phase === 'hot' && currentEpoch > lastEpochRef.current && newTime > 0.1) { + // Smooth lamport decrement every tick (100ms) when hot + // Slower in critical zone (below 1000) so users can see the 776→388 transition + if (phase === 'hot' && newTime > 0.1) { setLamports((l) => { - const newLamports = Math.max(0, l - RENT_PER_EPOCH); + const tickAmount = l < 1000 ? LAMPORTS_PER_TICK_SLOW : LAMPORTS_PER_TICK; + const newLamports = Math.max(0, l - tickAmount); // Go cold when lamports below cold threshold if (newLamports < COLD_THRESHOLD) { setPhase('cold'); } return newLamports; }); - lastEpochRef.current = currentEpoch; } // Loop at 30s @@ -223,7 +239,6 @@ export const RentLifecycleVisualizer = () => { setPhase('uninitialized'); setLamports(0); txLineIndexRef.current = 0; - lastEpochRef.current = 0; return 0; } @@ -300,37 +315,35 @@ export const RentLifecycleVisualizer = () => { .btn-interactive { position: relative; overflow: hidden; - background: rgba(120, 140, 180, 0.08); - border-color: rgba(120, 140, 180, 0.25); + background: rgba(120, 140, 180, 0.06); + border: 1px solid rgba(120, 140, 180, 0.2); + border-bottom-color: rgba(0, 0, 0, 0.08); + box-shadow: 0 1px 2px rgba(0,0,0,0.05); color: rgb(0, 0, 0); } .dark .btn-interactive { - background: rgba(120, 140, 180, 0.12); - border-color: rgba(120, 140, 180, 0.3); + background: rgba(120, 140, 180, 0.1); + border: 1px solid rgba(120, 140, 180, 0.25); + border-bottom-color: rgba(0, 0, 0, 0.2); + box-shadow: 0 1px 2px rgba(0,0,0,0.1); color: rgb(255, 255, 255); } - .btn-interactive::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(120, 140, 180, 0.15), transparent); - transition: left 0.5s ease; - } - .btn-interactive:hover::before { - left: 100%; - } .btn-interactive:hover { - background: rgba(120, 140, 180, 0.15); - border-color: rgba(120, 140, 180, 0.4); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(120, 140, 180, 0.2); + background: rgba(120, 140, 180, 0.1); + box-shadow: 0 2px 4px rgba(0,0,0,0.08); } .dark .btn-interactive:hover { - background: rgba(120, 140, 180, 0.2); - box-shadow: 0 4px 12px rgba(120, 140, 180, 0.15); + background: rgba(120, 140, 180, 0.15); + box-shadow: 0 2px 4px rgba(0,0,0,0.15); + } + .btn-interactive:active { + background: rgba(120, 140, 180, 0.12); + box-shadow: inset 0 1px 2px rgba(0,0,0,0.1); + transform: translateY(0.5px); + } + .dark .btn-interactive:active { + background: rgba(120, 140, 180, 0.18); + box-shadow: inset 0 1px 2px rgba(0,0,0,0.2); } @keyframes arrowUp { 0% { opacity: 0; transform: translateY(calc(-50% + 4px)); } @@ -499,7 +512,7 @@ export const RentLifecycleVisualizer = () => { textAlign: 'right', }} > - {lamports.toLocaleString()} + {formatLamports(lamports)} Date: Sun, 7 Dec 2025 00:35:31 +0000 Subject: [PATCH 094/143] Add floating amount indicators to rent visualizer transactions --- snippets/jsx/rent-lifecycle-visualizer.jsx | 75 ++++++++++++++++------ 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/snippets/jsx/rent-lifecycle-visualizer.jsx b/snippets/jsx/rent-lifecycle-visualizer.jsx index d79d5e45..40e42864 100644 --- a/snippets/jsx/rent-lifecycle-visualizer.jsx +++ b/snippets/jsx/rent-lifecycle-visualizer.jsx @@ -8,6 +8,7 @@ export const RentLifecycleVisualizer = () => { const [activeLines, setActiveLines] = useState([]); const [isButtonPressed, setIsButtonPressed] = useState(false); const [flyingArrows, setFlyingArrows] = useState([]); + const [floatingAmounts, setFloatingAmounts] = useState([]); // Constants from rent config const LAMPORTS_PER_TICK = 77.6; // 388 per epoch / 5 ticks = smooth decrement @@ -46,33 +47,38 @@ export const RentLifecycleVisualizer = () => { const colorToRgba = (c, alpha = 1) => `rgba(${c.r}, ${c.g}, ${c.b}, ${alpha})`; - // Format lamports: ~rounded above 1000, then 776 → 388 as discrete steps, then ticks + // Format lamports: ~rounded above 2000, then discrete steps matching rent calculations + // 1,552 = 4 epochs, 776 = 2 epochs (top-up), 388 = 1 epoch (cold) const formatLamports = (l) => { - if (l > 1000) { + if (l > 2000) { const rounded = Math.round(l / 500) * 500; return `~${rounded.toLocaleString()}`; } - if (l > 776) { - return '~1,000'; // approaching threshold - } - if (l > 388) { - return '776'; // at top-up threshold - } - if (l > 0) { - return '388'; // at cold threshold - } + if (l > 1552) return '~2,000'; + if (l > 776) return '1,552'; // 4 epochs + if (l > 388) return '776'; // 2 epochs (top-up threshold) + if (l > 0) return '388'; // 1 epoch (cold threshold) return '0'; }; const arrowIdRef = useRef(0); const flyingArrowIdRef = useRef(0); + const floatingAmountIdRef = useRef(0); - const triggerFlyingArrow = () => { + const triggerFlyingArrow = (amount, lineIndex) => { const id = flyingArrowIdRef.current++; setFlyingArrows((prev) => [...prev, id]); setTimeout(() => { setFlyingArrows((prev) => prev.filter((a) => a !== id)); }, 600); + + // Also show floating amount at the source of the transaction line + const amountId = floatingAmountIdRef.current++; + const line = txLines[lineIndex] || txLines[0]; + setFloatingAmounts((prev) => [...prev, { id: amountId, amount, x: line.x1, y: line.y1 }]); + setTimeout(() => { + setFloatingAmounts((prev) => prev.filter((a) => a.id !== amountId)); + }, 800); }; const triggerHighlight = () => { @@ -96,9 +102,11 @@ export const RentLifecycleVisualizer = () => { // Cycle through lines for transactions const txLineIndexRef = useRef(0); + const lastLineIndexRef = useRef(0); const getNextLineIndex = () => { const index = txLineIndexRef.current; txLineIndexRef.current = (txLineIndexRef.current + 1) % txLines.length; + lastLineIndexRef.current = index; return index; }; @@ -131,7 +139,7 @@ export const RentLifecycleVisualizer = () => { setLamports(INITIAL_RENT); setPhase('hot'); triggerHighlight(); - triggerFlyingArrow(); + triggerFlyingArrow(INITIAL_RENT, lastLineIndexRef.current); return; } @@ -139,7 +147,7 @@ export const RentLifecycleVisualizer = () => { if (lamports < TOPUP_THRESHOLD) { setLamports((l) => l + TOPUP_LAMPORTS); triggerHighlight(); - triggerFlyingArrow(); + triggerFlyingArrow(TOPUP_LAMPORTS, lastLineIndexRef.current); } // Otherwise just the transaction happens (no rent top-up needed) }; @@ -150,14 +158,14 @@ export const RentLifecycleVisualizer = () => { 0.3, 0.6, 0.9, 1.2, 1.6, 2, 2.4, 2.8, 3.2, 3.7, 4.2, 4.8, // Phase 2: Continued activity (5-7s) 5.3, 5.9, 6.4, - // Phase 3: Top-ups as rent depletes (~7-10s) - 7.2, 7.8, 8.4, 9, 9.6, + // Phase 3: Top-ups as rent depletes (~7-9s) - 3 top-ups + 7.2, 8, 8.8, // Phase 4: Goes cold, reinit 10.8, // Phase 5: Second cycle spam (11-15s) 11.3, 11.8, 12.3, 12.9, 13.4, 14, 14.6, 15.2, - // Phase 6: Top-ups again (~16-18s) - 16, 16.7, 17.3, 18, + // Phase 6: Top-ups again (~16-17s) - 2 top-ups + 16, 17, // Phase 7: Goes cold, reinit 19.5, // Phase 8: Final burst (20-25s) @@ -205,13 +213,13 @@ export const RentLifecycleVisualizer = () => { setLamports(INITIAL_RENT); setPhase('hot'); triggerHighlight(); - triggerFlyingArrow(); + triggerFlyingArrow(INITIAL_RENT, lastLineIndexRef.current); } else if (phase === 'hot') { // Only top up if below threshold (3h = 776 lamports = 2 epochs) setLamports((currentLamports) => { if (currentLamports > 0 && currentLamports < TOPUP_THRESHOLD) { triggerHighlight(); - triggerFlyingArrow(); + triggerFlyingArrow(TOPUP_LAMPORTS, lastLineIndexRef.current); return currentLamports + TOPUP_LAMPORTS; } return currentLamports; @@ -362,6 +370,14 @@ export const RentLifecycleVisualizer = () => { .arrow-fly-up { animation: arrowFlyUp 0.4s ease-out forwards; } + @keyframes amountFlyUp { + 0% { opacity: 1; transform: translateY(0); } + 70% { opacity: 1; } + 100% { opacity: 0; transform: translateY(-20px); } + } + .amount-fly-up { + animation: amountFlyUp 0.8s ease-out forwards; + } `} {/* Main visualization area */} @@ -400,6 +416,25 @@ export const RentLifecycleVisualizer = () => { ); })} + {/* Floating amounts at transaction source */} + {floatingAmounts.map(({ id, amount, x, y }) => ( + + +{amount.toLocaleString()} + + ))} {/* Timeline with fading edges */} From c307e8f67e3376937e28b2fc919088a0083ec090 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 7 Dec 2025 00:38:44 +0000 Subject: [PATCH 095/143] Add timeline gap behind diamond in rent visualizer --- snippets/jsx/rent-lifecycle-visualizer.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/snippets/jsx/rent-lifecycle-visualizer.jsx b/snippets/jsx/rent-lifecycle-visualizer.jsx index 40e42864..a087be31 100644 --- a/snippets/jsx/rent-lifecycle-visualizer.jsx +++ b/snippets/jsx/rent-lifecycle-visualizer.jsx @@ -437,12 +437,12 @@ export const RentLifecycleVisualizer = () => { ))} - {/* Timeline with fading edges */} + {/* Timeline with fading edges and gap behind diamond */}
    {/* Continuously scrolling tick marks */} From 68167ba8108c48395d74d4a55b06e51c1cf74b05 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sun, 7 Dec 2025 01:04:50 +0000 Subject: [PATCH 096/143] Replace time counter with hour labels on scrolling timeline --- snippets/jsx/rent-lifecycle-visualizer.jsx | 102 +++++++++------------ 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/snippets/jsx/rent-lifecycle-visualizer.jsx b/snippets/jsx/rent-lifecycle-visualizer.jsx index a087be31..b3d1c484 100644 --- a/snippets/jsx/rent-lifecycle-visualizer.jsx +++ b/snippets/jsx/rent-lifecycle-visualizer.jsx @@ -1,5 +1,5 @@ export const RentLifecycleVisualizer = () => { - const [time, setTime] = useState(0); + const [, setTime] = useState(0); const [lamports, setLamports] = useState(0); const [isRunning, setIsRunning] = useState(true); const [phase, setPhase] = useState('uninitialized'); @@ -242,8 +242,8 @@ export const RentLifecycleVisualizer = () => { }); } - // Loop at 30s - if (newTime >= 30) { + // Loop at 33s (extra 3s for cold fade) + if (newTime >= 33) { setPhase('uninitialized'); setLamports(0); txLineIndexRef.current = 0; @@ -257,9 +257,6 @@ export const RentLifecycleVisualizer = () => { return () => clearInterval(interval); }, [isRunning, phase]); - // 0.5 second = 1 epoch = 1.5 hours, so 1 second = 3 hours - // Round to nearest 3h for cleaner display - const displayHours = Math.round(time * 3 / 3) * 3; const accountColor = getAccountColor(); // Diamond dots pattern @@ -299,10 +296,10 @@ export const RentLifecycleVisualizer = () => { +
    Create accounts without paying upfront rent. - + Execution and data availability on Solana. From da2f8bdf62c654b00002dbde8de1939f8d1fdaea Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 8 Dec 2025 20:25:59 +0000 Subject: [PATCH 122/143] Move FAQ to ctoken-faq, add auto-demo to CodeCompare --- {snippets => c-token-program}/ctoken-faq.mdx | 82 +++++++++------ c-token-program/faq.mdx | 8 -- docs.json | 2 +- snippets/jsx/code-compare.jsx | 104 ++++++++++++------- 4 files changed, 120 insertions(+), 76 deletions(-) rename {snippets => c-token-program}/ctoken-faq.mdx (60%) delete mode 100644 c-token-program/faq.mdx diff --git a/snippets/ctoken-faq.mdx b/c-token-program/ctoken-faq.mdx similarity index 60% rename from snippets/ctoken-faq.mdx rename to c-token-program/ctoken-faq.mdx index dd115c1d..caefebb2 100644 --- a/snippets/ctoken-faq.mdx +++ b/c-token-program/ctoken-faq.mdx @@ -1,40 +1,48 @@ +--- +title: FAQ +description: Frequently asked questions about c-Token. +--- import { CodeCompare } from '/snippets/jsx/code-compare.jsx'; +import CompressibleRentExplained from '/snippets/compressible-rent-explained.mdx'; +import { RentLifecycleVisualizer } from '/snippets/jsx/rent-lifecycle-visualizer.jsx'; -#### What is c-Token? -c-Token is like SPL token stored more efficiently to reduce account creation cost. +#### What is c-token? +c-token is like SPL token stored more efficiently to reduce account creation cost. Users receive and -#### Is c-Token live on mainnet & audited? -c-Token is currently deployed on devnet and undergoing security audits. +| Creation Cost | SPL | c-Token | +|:---------------------|:------------------|:----------------------| +| **Token Account** | ~2,000,000 lamports | ~**17,000** lamports | + + +#### Is c-token live on mainnet & audited? +c-token is currently deployed on devnet and undergoing security audits.
    ETA: Q1 2026. #### Do I need to change my client code significantly? -No. The c-Token SDK follows similar patterns to SPL. +No. The c-token SDK follows similar patterns to SPL. ---- - - @@ -42,6 +50,20 @@ Yes. c-Token accounts can hold tokens from c-Mints, SPL mints, or Token 2022 min SPL tokens can be converted to c-Tokens and back. + + +Rent is paid over time when accounts are accessed via a custom rent config instead of the full rent-exemption upfront: + + + + + + + +The account is automatically compressed. +Your tokens are preserved as a compressed token account (rent-free). The account decompresses automatically when you interact with it again. + + Extensions are under development. Additional extensions can be requested. @@ -64,30 +86,18 @@ Coming soon: - ConfidentialMintBurn - - - - **c-Mint**: Compressed account representing a token (like SPL mint). Rent-free. - **c-Token**: Solana account that holds token balances of c-mints, SPL or Token 22 mints. Uses sponsored rent. - **Compressed token**: Compressed account storing token data. Rent-free, for storage and distribution. - -The account is automatically compressed. -Your tokens are preserved as a compressed token account (rent-free). The account decompresses automatically when you interact with it again. - - - -DEXs and other protocols need to integrate with the c-Token program. c-Token accounts follow SPL Token account layout for compatibility, but protocol integration is required. - - Standard Solana RPC works for c-Token accounts (Solana accounts). Compressed token balances require the ZK Compression RPC. -Yes. SPL tokens can be deposited into c-Token accounts and withdrawn back to SPL token accounts. +Yes. SPL tokens can be deposited into c-Token accounts and withdrawn back to SPL token accounts via the `transferInterface` method. @@ -96,3 +106,13 @@ Yes. SPL tokens can be deposited into c-Token accounts and withdrawn back to SPL + +### Get Started + + + Dedicated guides for specific use cases. + + + Recipes for c-Mints and c-Tokens. + + \ No newline at end of file diff --git a/c-token-program/faq.mdx b/c-token-program/faq.mdx deleted file mode 100644 index 2d95dd1c..00000000 --- a/c-token-program/faq.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: FAQ -description: Frequently asked questions about c-Token. ---- - -import CtokenFaq from '/snippets/ctoken-faq.mdx'; - - diff --git a/docs.json b/docs.json index abf844bd..1eb1da8e 100644 --- a/docs.json +++ b/docs.json @@ -30,7 +30,7 @@ "group": "c-Token (Beta)", "pages": [ "c-token-program/README", - "c-token-program/faq", + "c-token-program/ctoken-faq", { "group": "Cookbook", "pages": [ diff --git a/snippets/jsx/code-compare.jsx b/snippets/jsx/code-compare.jsx index 90d45e7c..d1ed59f0 100644 --- a/snippets/jsx/code-compare.jsx +++ b/snippets/jsx/code-compare.jsx @@ -85,14 +85,55 @@ export const CodeCompare = ({ } }; - // Wiggle animation keyframes - const wiggleKeyframes = ` - @keyframes wiggle { - 0%, 100% { transform: translateX(0); } - 25% { transform: translateX(-3px); } - 75% { transform: translateX(3px); } - } - `; + // Auto-demo animation - slides the actual slider to show functionality + useEffect(() => { + if (hasInteracted) return; + + let animationId; + let startTime; + const duration = 2000; // 2s per cycle + const startPos = initialPosition; + const endPos = 20; // Slide slightly to hint at functionality + + const animate = (timestamp) => { + if (!startTime) startTime = timestamp; + const elapsed = timestamp - startTime; + const cycle = (elapsed % (duration * 2)) / duration; // 0-2 range + + // Ease in-out: go to endPos then back to startPos + let progress; + if (cycle < 1) { + progress = cycle; // Going to endPos + } else { + progress = 2 - cycle; // Coming back to startPos + } + + // Smooth easing + const eased = progress < 0.5 + ? 2 * progress * progress + : 1 - Math.pow(-2 * progress + 2, 2) / 2; + + const newPos = startPos + (endPos - startPos) * eased; + setSliderPercent(newPos); + + // Stop after 1 full cycle (4 seconds) + if (elapsed < duration * 2) { + animationId = requestAnimationFrame(animate); + } else { + setSliderPercent(initialPosition); + } + }; + + // Start after a short delay + const timeout = setTimeout(() => { + animationId = requestAnimationFrame(animate); + }, 500); + + return () => { + clearTimeout(timeout); + if (animationId) cancelAnimationFrame(animationId); + }; + }, [hasInteracted, initialPosition]); useEffect(() => { if (isDragging) { @@ -128,33 +169,23 @@ export const CodeCompare = ({ ); return ( -
    - {/* Inject keyframes for wiggle animation */} - -
    -
    + <> +
    {/* Code container - no header, just code */}
    {/* Floating copy button */} @@ -248,6 +279,7 @@ export const CodeCompare = ({
    -
    +
    + ); }; From 1d82d3053fd2a4d8aa992dae5495cabe113155de Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 8 Dec 2025 20:32:50 +0000 Subject: [PATCH 123/143] Rename Toolkit to Toolkits in navigation --- docs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs.json b/docs.json index 1eb1da8e..08475f86 100644 --- a/docs.json +++ b/docs.json @@ -55,7 +55,7 @@ ] }, { - "group": "Toolkit", + "group": "Toolkits", "expanded": true, "pages": [ "c-token-program/c-token-toolkits/for-payments", From 302449177cb1910f6adaad2a69a78f2caeb29b9c Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Mon, 8 Dec 2025 20:38:28 +0000 Subject: [PATCH 124/143] Remove home page, update CodeCompare border colors --- docs.json | 1 - snippets/jsx/code-compare.jsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs.json b/docs.json index 08475f86..5b698c8c 100644 --- a/docs.json +++ b/docs.json @@ -20,7 +20,6 @@ { "group": "Get Started", "pages": [ - "home", "welcome", "quickstart", "learn/ai-tools-guide" diff --git a/snippets/jsx/code-compare.jsx b/snippets/jsx/code-compare.jsx index d1ed59f0..18d00b58 100644 --- a/snippets/jsx/code-compare.jsx +++ b/snippets/jsx/code-compare.jsx @@ -172,7 +172,7 @@ export const CodeCompare = ({ <>
    Date: Tue, 9 Dec 2025 00:28:10 +0000 Subject: [PATCH 125/143] Update slider styling, add FAQ separator, refine diamond fade --- c-token-program/ctoken-faq.mdx | 2 ++ snippets/jsx/code-compare.jsx | 15 ++++++++------- snippets/jsx/rent-lifecycle-visualizer.jsx | 11 +++++------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/c-token-program/ctoken-faq.mdx b/c-token-program/ctoken-faq.mdx index caefebb2..c2b0c2e0 100644 --- a/c-token-program/ctoken-faq.mdx +++ b/c-token-program/ctoken-faq.mdx @@ -43,6 +43,8 @@ const ix = CreateCTokenAccount.new( /> +--- + diff --git a/snippets/jsx/code-compare.jsx b/snippets/jsx/code-compare.jsx index 18d00b58..2f418a5b 100644 --- a/snippets/jsx/code-compare.jsx +++ b/snippets/jsx/code-compare.jsx @@ -236,12 +236,13 @@ export const CodeCompare = ({ {/* Grey line */}
    - {/* Blue shine/glow to the right */} + {/* Blue glow fading to left starting at the line */}
    @@ -253,9 +254,9 @@ export const CodeCompare = ({ width: "20px", height: "32px", borderRadius: "4px", - background: "linear-gradient(to bottom, rgba(255,255,255,0.95), rgba(191,219,254,0.9), rgba(255,255,255,0.95))", - border: "1px solid rgba(199, 210, 254, 0.8)", - boxShadow: "0 1px 3px rgba(0,0,0,0.1), 0 0 8px rgba(99,102,241,0.2)", + background: "#f8fafc", + border: "1px solid #d1d5db", + boxShadow: "0 1px 2px rgba(0,0,0,0.05)", transform: isDragging ? "scale(1.08)" : "scale(1)", }} > diff --git a/snippets/jsx/rent-lifecycle-visualizer.jsx b/snippets/jsx/rent-lifecycle-visualizer.jsx index 493b4c2c..76c67bac 100644 --- a/snippets/jsx/rent-lifecycle-visualizer.jsx +++ b/snippets/jsx/rent-lifecycle-visualizer.jsx @@ -283,8 +283,10 @@ export const RentLifecycleVisualizer = () => { const width = size - Math.abs(row); for (let col = -width; col <= width; col++) { const distFromCenter = Math.max(Math.abs(row), Math.abs(col)); - const dotSize = Math.max(1, centerSize - distFromCenter * 0.6); - const opacity = Math.max(0.2, 1 - distFromCenter * 0.15); + // Exponential fade for smooth transition to white + const fadeProgress = distFromCenter / size; // 0 at center, 1 at edge + const dotSize = Math.max(0.3, centerSize * Math.pow(1 - fadeProgress, 1.5)); + const opacity = Math.pow(1 - fadeProgress, 2.5); // Aggressive exponential fade dots.push({ x: 50 + col * 6, @@ -610,14 +612,11 @@ export const RentLifecycleVisualizer = () => {