Skip to content

Commit 4eae20f

Browse files
author
tilo-14
committed
Expand c-token-program overview with compressible extension details
- Add comprehensive compressible extension documentation with rent mechanics - Add BaseMint struct code example in new tab alongside comparison table - Add ATA derivation code example showing seed construction - Reorder compressed token use cases: storage first, then distribution - Simplify cmint.mdx: remove duplicate core concepts, link to main overview - Update ctoken.mdx: remove redundant sections covered in main overview - Add placeholder note for token type graphic - Fix source code link to ctoken_struct.rs
1 parent 8a39c4b commit 4eae20f

File tree

3 files changed

+287
-199
lines changed

3 files changed

+287
-199
lines changed

compressed-token-program/c-token-program.mdx

Lines changed: 243 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,17 @@ The [Compressed Token Program](https://github.com/Lightprotocol/light-protocol/t
4343
<ul>
4444
<li>**Compressed account** with `TokenData` field</li>
4545
<li>**Rent-free** and SPL-compatible</li>
46-
<li>Use for **token distribution** (airdrops, payments, etc.) and **storage of inactive tokens**</li>
46+
<li>Use for **storage of inactive tokens** or **token distribution**</li>
4747
<li>cToken accounts with the **[compressible extension](#compressible) are automatically compressed/decompressed** when active/inactive.</li>
4848
</ul>
4949
</td>
5050
</tr>
5151
</tbody>
5252
</table>
5353

54+
_add graphic of all tokens here_
55+
56+
5457
<Tip>
5558
**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.<br />
5659
You can **migrate SPL token accounts to cTokens** with compressible extension for **sponsored rent-exemption** while keeping the **same interoparability**.
@@ -63,9 +66,10 @@ You can **migrate SPL token accounts to cTokens** with compressible extension fo
6366
* SPL mints can not be compressed to cMints.
6467
</Note>
6568

66-
cMints **uniquely represent a token on Solana and store its global metadata**, similar to SPL mint accounts with two core differences:
69+
cMints **uniquely represent a token on Solana and store its global metadata**, similar to SPL mint accounts with few core differences:
6770
1. cMint accounts are **rent-free**.
6871
2. Tokens created from cMints are **cTokens** (fully compatible with SPL tokens).
72+
3. Token metadata (name, symbol, URI) is stored as an extension within the compressed mint account.
6973

7074
<Tabs>
7175
<Tab title="Diagram">
@@ -99,52 +103,76 @@ The `metadata` field is used by the Compressed Token Program to store the intern
99103
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.
100104

101105
Here is how cMints and SPL mints compare:
102-
<table>
103-
<thead>
104-
<tr>
105-
<th style={{width: '10%', textAlign: 'left'}}>Field</th>
106-
<th style={{width: '45%', textAlign: 'center'}}>cMint</th>
107-
<th style={{width: '30%', textAlign: 'center'}}>SPL Mint</th>
108-
</tr>
109-
</thead>
110-
<tbody>
111-
<tr>
112-
<td>mint_authority</td>
113-
<td style={{textAlign: 'center'}}>✓</td>
114-
<td style={{textAlign: 'center'}}>✓</td>
115-
</tr>
116-
<tr>
117-
<td>supply</td>
118-
<td style={{textAlign: 'center'}}>✓</td>
119-
<td style={{textAlign: 'center'}}>✓</td>
120-
</tr>
121-
<tr>
122-
<td>decimals</td>
123-
<td style={{textAlign: 'center'}}>✓</td>
124-
<td style={{textAlign: 'center'}}>✓</td>
125-
</tr>
126-
<tr>
127-
<td>is_initialized</td>
128-
<td style={{textAlign: 'center'}}>✓</td>
129-
<td style={{textAlign: 'center'}}>✓</td>
130-
</tr>
131-
<tr>
132-
<td>freeze_authority</td>
133-
<td style={{textAlign: 'center'}}>✓</td>
134-
<td style={{textAlign: 'center'}}>✓</td>
135-
</tr>
136-
<tr>
137-
<td>cMint Data</td>
138-
<td style={{textAlign: 'center'}}>✓</td>
139-
<td style={{textAlign: 'center'}}>-</td>
140-
</tr>
141-
<tr>
142-
<td>Extensions</td>
143-
<td style={{textAlign: 'center'}}>✓</td>
144-
<td style={{textAlign: 'center'}}>via Token-2022</td>
145-
</tr>
146-
</tbody>
147-
</table>
106+
107+
<Tabs>
108+
<Tab title="Basemint vs SPL mint">
109+
<table>
110+
<thead>
111+
<tr>
112+
<th style={{width: '10%', textAlign: 'left'}}>Field</th>
113+
<th style={{width: '45%', textAlign: 'center'}}>cMint</th>
114+
<th style={{width: '30%', textAlign: 'center'}}>SPL Mint</th>
115+
</tr>
116+
</thead>
117+
<tbody>
118+
<tr>
119+
<td>mint_authority</td>
120+
<td style={{textAlign: 'center'}}>✓</td>
121+
<td style={{textAlign: 'center'}}>✓</td>
122+
</tr>
123+
<tr>
124+
<td>supply</td>
125+
<td style={{textAlign: 'center'}}>✓</td>
126+
<td style={{textAlign: 'center'}}>✓</td>
127+
</tr>
128+
<tr>
129+
<td>decimals</td>
130+
<td style={{textAlign: 'center'}}>✓</td>
131+
<td style={{textAlign: 'center'}}>✓</td>
132+
</tr>
133+
<tr>
134+
<td>is_initialized</td>
135+
<td style={{textAlign: 'center'}}>✓</td>
136+
<td style={{textAlign: 'center'}}>✓</td>
137+
</tr>
138+
<tr>
139+
<td>freeze_authority</td>
140+
<td style={{textAlign: 'center'}}>✓</td>
141+
<td style={{textAlign: 'center'}}>✓</td>
142+
</tr>
143+
<tr>
144+
<td>cMint Data</td>
145+
<td style={{textAlign: 'center'}}>✓</td>
146+
<td style={{textAlign: 'center'}}>-</td>
147+
</tr>
148+
<tr>
149+
<td>Extensions</td>
150+
<td style={{textAlign: 'center'}}>✓</td>
151+
<td style={{textAlign: 'center'}}>via Token-2022</td>
152+
</tr>
153+
</tbody>
154+
</table>
155+
</Tab>
156+
<Tab title="BaseMint Struct">
157+
```rust
158+
pub struct BaseMint {
159+
/// Optional authority used to mint new tokens. The mint authority may only
160+
/// be provided during mint creation. If no mint authority is present
161+
/// then the mint has a fixed supply and no further tokens may be
162+
/// minted.
163+
pub mint_authority: Option<Pubkey>,
164+
/// Total supply of tokens.
165+
pub supply: u64,
166+
/// Number of base 10 digits to the right of the decimal place.
167+
pub decimals: u8,
168+
/// Is initialized - for SPL compatibility
169+
pub is_initialized: bool,
170+
/// Optional authority to freeze token accounts.
171+
pub freeze_authority: Option<Pubkey>,
172+
}
173+
```
174+
</Tab>
175+
</Tabs>
148176

149177
# cToken Account
150178

@@ -157,11 +185,6 @@ A cToken account holds token balances like SPL Token accounts:
157185
* Each wallet can own multiple cToken accounts for the same cMint.
158186
* A cToken account can only have one owner and hold units of one cMint.
159187

160-
<Tip>
161-
* Use the **[compressible extension](/compressible/compressible) for sponsored rent exemption**.
162-
* cToken accounts with this extension are **automatically compressed** with no writes in 6300 slots (1.75 hours), **and decompressed** with new writes.
163-
</Tip>
164-
165188
<Tabs>
166189
<Tab title="Diagram">
167190
<Frame>
@@ -190,7 +213,7 @@ A cToken account holds token balances like SPL Token accounts:
190213
</Tabs>
191214

192215
<Info>
193-
Find the [source code of cToken here](https://github.com/Lightprotocol/light-protocol/blob/main/program-libs/ctoken-types/src/state/ctoken/ctoken.rs).
216+
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).
194217
</Info>
195218

196219
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.
@@ -253,38 +276,188 @@ Here is how cTokens and SPL tokens compare:
253276
</tbody>
254277
</table>
255278

256-
# Associated cToken Account
279+
### Compressible Extension
257280

258-
**Associated cToken** accounts follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA):
259-
* Each wallet needs its own cToken account to hold tokens from the same cMint.
260-
* It's derived from the owner's address and the cMint account's address.
261-
* The only **difference** in ATA derivation is the **program ID parameter**.
281+
Compressible accounts maintain the **functionality of Solana accounts** but with **rent-exemption sponsorship**:
282+
283+
1. The protocol funds the account's rent exemption (rent sponsor) at creation.
284+
2. The account creator top-ups the lamport balance with rent for at least two epochs (12,600 slots / 84 min)
285+
3. The transaction payer top-ups the lamports balance when the account's lamports balance is below 2 epochs.
286+
4. The account will be compressed when inactive for a period of 12,600 slots (84 min).
287+
5. When the account is written to, it's automatically decompressed.
288+
<Tip>
289+
We recommend to create cToken accounts always with the [compressible extension](/compressible/compressible) for sponsored rent exemption.
290+
</Tip>
291+
This extension makes cToken accounts 200x cheaper than SPL token accounts
292+
293+
| | cToken | SPL |
294+
|-----------|--------|-----|
295+
| allocate | 0.000011 | 0.002 |
296+
| rent for 24h | 0.000005 | - |
297+
298+
<Accordion title="Compressible vs Solana Rent">
299+
300+
### Initial Rent Top-Up
301+
The **creator of compressible accounts** tops-up the account with **rent for at least two epochs**.
302+
Two epochs for compressible accounts have a length of **12,600 slots**.
303+
304+
``` rust
305+
rent_per_epoch = base_rent + (data_len * lamports_per_byte_per_epoch)
306+
307+
// For 260-byte cToken account:
308+
// 128 + (260 * 1) = 388 lamports per epoch
309+
```
262310

263311
<table>
264312
<thead>
265313
<tr>
266-
<th style={{width: '10%', textAlign: 'left'}}>Parameter</th>
267-
<th style={{width: '45%', textAlign: 'left'}}>cToken ATA</th>
268-
<th style={{width: '30%', textAlign: 'left'}}>SPL ATA</th>
314+
<th style={{width: '25%', textAlign: 'left'}}>Component</th>
315+
<th style={{width: '25%', textAlign: 'left'}}>Amount</th>
316+
<th style={{width: '50%', textAlign: 'left'}}>Description</th>
269317
</tr>
270318
</thead>
271319
<tbody>
272320
<tr>
273-
<td>owner</td>
274-
<td colspan="2" style={{textAlign: 'center'}}>wallet address</td>
321+
<td>Prepaid rent</td>
322+
<td>388 × N epochs*</td>
323+
<td>Rent for N epochs</td>
275324
</tr>
276325
<tr>
277-
<td>program_id</td>
278-
<td>COMPRESSED_TOKEN_PROGRAM_ID</td>
279-
<td>SPL_TOKEN_PROGRAM_ID</td>
326+
<td></td>
327+
<td></td>
328+
<td></td>
329+
<td></td>
280330
</tr>
281331
<tr>
282-
<td>mint</td>
283-
<td colspan="2" style={{textAlign: 'center'}}>mint address</td>
332+
<td>Compression cost & incentive</td>
333+
<td>10,000 lamports <br /> + 1,000 lamports</td>
334+
<td>Transaction cost for compression<br />+ protocol incentive</td>
335+
</tr>
336+
<tr>
337+
<td>Transaction fee</td>
338+
<td>5,000-10,000 lamports</td>
339+
<td>Standard Solana tx fees</td>
284340
</tr>
285341
</tbody>
286342
</table>
287343

344+
\* _`N` = `num_prepaid_epochs` parameter (0 or ≥ 2, set at creation)._
345+
346+
Normally, the **creator of Solana accounts** pays the **rent exemption balance equal to 2 years of rent** proportional to account size:
347+
348+
``` rust
349+
minimum_balance = (ACCOUNT_STORAGE_OVERHEAD + data_len) * lamports_per_byte * exemption_threshold
350+
351+
/// Example 165 byte SPL token account:
352+
/// minimum_balance = (128 + 165) * 6960 = 2,039,280 lamports
353+
```
354+
<Note>
355+
* Most Solana accounts are rarely accessed after creation, but continue to lock up SOL for the account's lifetime.
356+
* The creator of Solana accounts still must pay the rent-exemption balance.
357+
* The creator of compressible accounts only needs to pay the rent for the first two epochs.
358+
</Note>
359+
360+
361+
### Top-ups per Transaction
362+
363+
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.
364+
365+
<table>
366+
<thead>
367+
<tr>
368+
<th style={{width: '30%', textAlign: 'left'}}>Account State</th>
369+
<th style={{width: '25%', textAlign: 'left'}}>Payer Cost</th>
370+
<th style={{width: '45%', textAlign: 'left'}}>Example</th>
371+
</tr>
372+
</thead>
373+
<tbody>
374+
<tr>
375+
<td>Funded for 2+ epochs</td>
376+
<td>0 lamports</td>
377+
<td>No top-up required</td>
378+
</tr>
379+
<tr>
380+
<td>Funded for less than 2 epochs</td>
381+
<td>`lamports_per_write`</td>
382+
<td>Configured as 5,000 lamports <br />→ payer pays 5,000 lamports</td>
383+
</tr>
384+
<tr>
385+
<td>Compressible<br />(ran out of rent)</td>
386+
<td>`lamports_per_write + rent_deficit`</td>
387+
<td>Configured top-up: 5,000 lamports<br />Rent deficit: 3,000 lamports<br />Total payer cost: 8,000 lamports</td>
388+
</tr>
389+
</tbody>
390+
</table>
391+
392+
<Note>
393+
Top-ups are additional to standard Solana transaction fees (typically &lt;10,000 lamports).
394+
</Note>
395+
396+
### Closing compressible accounts
397+
398+
Compressible accounts can be closed by two parties. How rent and lamports balance are distributed depends on which party closes the account.
399+
400+
1. The **Account Owner** can close the account at any time, regardless of rent status. The account is not compressed in this case.
401+
402+
<table>
403+
<thead>
404+
<tr>
405+
<th style={{width: '35%', textAlign: 'left'}}>Component and Amount</th>
406+
<th style={{width: '35%', textAlign: 'left'}}>Recipient</th>
407+
</tr>
408+
</thead>
409+
<tbody>
410+
<tr>
411+
<td>Rent exemption + completed epoch rent</td>
412+
<td>Rent sponsor</td>
413+
</tr>
414+
<tr>
415+
<td>Remaining lamports from top-up (partial epoch)</td>
416+
<td>Account Owner</td>
417+
</tr>
418+
</tbody>
419+
</table>
420+
421+
2. The **Compression Authority** can compress and close the account when it becomes compressible
422+
423+
<table>
424+
<thead>
425+
<tr>
426+
<th style={{width: '35%', textAlign: 'left'}}>Component and Amount</th>
427+
<th style={{width: '35%', textAlign: 'left'}}>Recipient</th>
428+
</tr>
429+
</thead>
430+
<tbody>
431+
<tr>
432+
<td>Rent exemption + remaining epoch rent<br /></td>
433+
<td>Rent sponsor</td>
434+
</tr>
435+
<tr>
436+
<td>Compression incentive<br />(11,000 lamports)</td>
437+
<td>Forester node</td>
438+
</tr>
439+
</tbody>
440+
</table>
441+
442+
</Accordion>
443+
444+
# Associated cToken Account
445+
446+
**Associated cToken** accounts follow the same pattern as [associated token accounts](https://docs.solana.com/developing/programming-model/associated-token-account) (ATA):
447+
* Each wallet needs its own cToken account to hold tokens from the same cMint.
448+
* It's derived with the owner's address, program ID, and mint address.
449+
* The only **difference** in ATA derivation is the **program ID parameter** used in the seeds.
450+
451+
452+
```rust
453+
let seeds = [
454+
owner.as_ref(), // Wallet address (32 bytes)
455+
program_id.as_ref(), // Compressed Token Program ID (32 bytes)
456+
mint.as_ref(), // cMint address (32 bytes)
457+
bump.as_ref(), // Bump seed (1 byte)
458+
];
459+
```
460+
288461
<Info>
289462
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).
290463
</Info>
@@ -297,7 +470,7 @@ cToken ATAs can implement the **[compressible extension](/compressible/compressi
297470

298471
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.
299472

300-
We recommend to use compressed tokens for token distribution or storage of inactive tokens.
473+
We recommend to use compressed tokens for **token distribution** or **storage of inactive tokens**.
301474
<Tip>
302475
**cToken accounts** with the **[compressible extension](/compressible/compressible) are automatically compressed** with no writes in 6300 slots (1.75 hours), and decompressed with new writes.
303476
</Tip>

0 commit comments

Comments
 (0)