Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .github/workflows/js-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ jobs:
done
echo "Tests passed on attempt $attempt"

- name: Run compressed-token tests with V2
- name: Run compressed-token legacy tests with V2
run: |
echo "Running compressed-token tests with retry logic (max 2 attempts)..."
echo "Running compressed-token legacy tests with retry logic (max 2 attempts)..."
attempt=1
max_attempts=2
until npx nx test @lightprotocol/compressed-token; do
Expand All @@ -95,6 +95,23 @@ jobs:
done
echo "Tests passed on attempt $attempt"

- name: Run compressed-token ctoken tests with V2
run: |
echo "Running compressed-token ctoken tests with retry logic (max 2 attempts)..."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need multiple attempts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think that was to hot fix an issue with build dependency ordering. let me try without retries

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it wasn't a hotfix it was so that the ci tests fail less often because test validator didn't start correctly
please preserve these retries.

attempt=1
max_attempts=2
cd js/compressed-token
until LIGHT_PROTOCOL_VERSION=V2 pnpm test:e2e:ctoken:all; do
attempt=$((attempt + 1))
if [ $attempt -gt $max_attempts ]; then
echo "Tests failed after $max_attempts attempts"
exit 1
fi
echo "Attempt $attempt/$max_attempts failed, retrying..."
sleep 5
done
echo "Tests passed on attempt $attempt"

- name: Run sdk-anchor-test TypeScript tests with V2
run: |
npx nx build @lightprotocol/sdk-anchor-test
Expand Down
1 change: 0 additions & 1 deletion cli/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export const PHOTON_VERSION = "0.51.2";
export const USE_PHOTON_FROM_GIT = true; // If true, will show git install command instead of crates.io.
export const PHOTON_GIT_REPO = "https://github.com/lightprotocol/photon.git";
export const PHOTON_GIT_COMMIT = "711c47b20330c6bb78feb0a2c15e8292fcd0a7b0"; // If empty, will use main branch.

export const LIGHT_PROTOCOL_PROGRAMS_DIR_ENV = "LIGHT_PROTOCOL_PROGRAMS_DIR";
export const BASE_PATH = "../../bin/";

Expand Down
1 change: 1 addition & 0 deletions cli/src/utils/processPhotonIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export async function startIndexer(
if (photonDatabaseUrl) {
args.push("--db-url", photonDatabaseUrl);
}

spawnBinary(INDEXER_PROCESS_NAME, args);
await waitForServers([{ port: indexerPort, path: "/getIndexerHealth" }]);
console.log("Indexer started successfully!");
Expand Down
333 changes: 333 additions & 0 deletions ctoken_for_payments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
# Using c-token for Payments

**TL;DR**: Same API patterns, 1/200th ATA creation cost. Your users get the same USDC, just stored more efficiently.

---

## Setup

```typescript
import { createRpc } from "@lightprotocol/stateless.js";

import {
getOrCreateAtaInterface,
getAtaInterface,
getAssociatedTokenAddressInterface,
transferInterface,
unwrap,
} from "@lightprotocol/compressed-token/unified";

const rpc = createRpc(RPC_ENDPOINT);
```
Comment on lines +1 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add cross-references and explain key concepts in the introduction.

The documentation lacks connection to related docs and explanation of foundational concepts. Per the codebase documentation structure, this guide should reference:

  1. The main compressed token documentation (if it exists at programs/compressed-token/program/docs/CLAUDE.md or similar)
  2. Account structure documentation for context on why unified patterns matter
  3. A brief explanation of what "1/200th ATA creation cost" means (ZK compression benefit?)

Additionally, the setup imports should include brief inline comments explaining each function's purpose, or cross-reference an API reference document.

Consider adding:

  • A "See Also" section linking to related docs (ACCOUNTS.md, main compressed-token docs, instruction reference)
  • A one-sentence explanation of the cost reduction benefit upfront
  • Brief comments in the imports block explaining what each function does
🤖 Prompt for AI Agents
In ctoken_for_payments.md around lines 1 to 21, the intro and setup lack
cross-references and explanations of key concepts; add links to the main
compressed-token docs (e.g., programs/compressed-token/program/docs/CLAUDE.md or
the actual path in the repo), the accounts/account-structure doc (ACCOUNTS.md),
and the instruction/API reference, add a one-sentence explanation of what
"1/200th ATA creation cost" means (briefly describe that compressed-token
reduces on-chain ATA creation overhead via ZK/compression techniques), and
annotate the import block with short inline comments for each function (one-line
purpose for getOrCreateAtaInterface, getAtaInterface,
getAssociatedTokenAddressInterface, transferInterface, unwrap) plus a new "See
Also" section linking the referenced docs.


---

## 1. Receive Payments

**SPL Token:**

```typescript
import { getOrCreateAssociatedTokenAccount } from "@solana/spl-token";

const ata = await getOrCreateAssociatedTokenAccount(
connection,
payer,
mint,
recipient
);
// Share ata.address with sender

console.log(ata.amount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove console.log() statements from code examples.

Per coding guidelines, code examples should NOT include print/log statements unless essential. These are demo artifacts and distract from core logic. Remove or clearly mark as optional debugging statements.

Affected lines: 40 (console.log(ata.amount)), 69 (console.log(ata.parsed.amount)), 229-230 (SPL balance), 239 (c-token balance), 249 (tx history), 256-260 (merged results).

Suggested approach: Either remove these lines entirely or preface them with a comment like // Optional: log to console to indicate they're not part of the core flow.

-console.log(ata.amount);
+// Account object retrieved; use ata.amount to access token balance

Also applies to: 69-69, 229-230, 239-239, 249-249, 256-260

🤖 Prompt for AI Agents
In ctoken_for_payments.md around lines 40, 69, 229-230, 239, 249, and 256-260,
remove the incidental console.log() statements (ata.amount, ata.parsed.amount,
SPL balance, c-token balance, tx history, and merged results) or convert them
into clearly marked optional debug lines by prefixing with a comment like "//
Optional: log to console" so examples show only core logic; ensure the examples
remain functional after removal and update surrounding text if it references
those logs.

```
Comment on lines +32 to +41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add inline comments explaining the idempotent creation pattern.

The SPL example shows getOrCreateAssociatedTokenAccount but doesn't explain what "idempotent" means or why this pattern matters. This is key to understanding why the c-token equivalent is valuable. Add a comment explaining that this function safely creates the ATA if it doesn't exist, or returns it if it already does.

// Safe account creation: creates ATA if missing, returns existing ATA if present
const ata = await getOrCreateAssociatedTokenAccount(
  connection,
  payer,
  mint,
  recipient
);
// Use ata.address to share with sender (existing or newly created)
🤖 Prompt for AI Agents
In ctoken_for_payments.md around lines 32 to 41, the example using
getOrCreateAssociatedTokenAccount lacks an inline explanation of the idempotent
creation pattern; add a concise comment before the call stating that this helper
will create the associated token account only if it doesn't exist and otherwise
return the existing account (so repeated calls are safe), and add a short
follow-up comment clarifying that ata.address can be shared with the sender
regardless of whether the account was newly created or already present.


**SPL Token (instruction-level):**

```typescript
import {
getAssociatedTokenAddressSync,
createAssociatedTokenAccountIdempotentInstruction,
} from "@solana/spl-token";

const ata = getAssociatedTokenAddressSync(mint, recipient);

const tx = new Transaction().add(
createAssociatedTokenAccountIdempotentInstruction(
payer.publicKey,
ata,
recipient,
mint
)
);
```

**c-token:**

```typescript
const ata = await getOrCreateAtaInterface(rpc, payer, mint, recipient);
// Share ata.parsed.address with sender

console.log(ata.parsed.amount);
```

**c-token (instruction-level):**

```typescript
import {
createAssociatedTokenAccountInterfaceIdempotentInstruction,
getAssociatedTokenAddressInterface,
} from "@lightprotocol/compressed-token/unified";
import { CTOKEN_PROGRAM_ID } from "@lightprotocol/stateless.js";

const ata = getAssociatedTokenAddressInterface(mint, recipient);

const tx = new Transaction().add(
createAssociatedTokenAccountInterfaceIdempotentInstruction(
payer.publicKey,
ata,
recipient,
mint,
CTOKEN_PROGRAM_ID
)
);
```
Comment on lines +29 to +92
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the ctoken_for_payments.md file
find . -name "ctoken_for_payments.md" -type f

Repository: Lightprotocol/light-protocol

Length of output: 96


🏁 Script executed:

# Search for getOrCreateAtaInterface implementation
rg "getOrCreateAtaInterface" --type ts --type js -B 3 -A 5

Repository: Lightprotocol/light-protocol

Length of output: 220


🏁 Script executed:

# Search for createAssociatedTokenAccountInterfaceIdempotentInstruction signature
rg "createAssociatedTokenAccountInterfaceIdempotentInstruction" --type ts --type js -B 2 -A 10

Repository: Lightprotocol/light-protocol

Length of output: 220


🏁 Script executed:

# Find unified API exports
find . -path "*/unified*" -type f -name "*.ts" -o -name "*.js" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 89


🏁 Script executed:

# Search for TokenAccount type definitions to understand .parsed structure
rg "\.parsed" --type ts --type js -B 2 -A 2 | head -100

Repository: Lightprotocol/light-protocol

Length of output: 220


🏁 Script executed:

# Read the ctoken_for_payments.md file to see the actual content
cat -n ctoken_for_payments.md | sed -n '25,95p'

Repository: Lightprotocol/light-protocol

Length of output: 1954


🏁 Script executed:

# Search for getOrCreateAtaInterface in all files
rg "getOrCreateAtaInterface" -B 3 -A 8 --max-count 5

Repository: Lightprotocol/light-protocol

Length of output: 2214


🏁 Script executed:

# Search for createAssociatedTokenAccountInterfaceIdempotentInstruction
rg "createAssociatedTokenAccountInterfaceIdempotentInstruction" -B 2 -A 8

Repository: Lightprotocol/light-protocol

Length of output: 1881


🏁 Script executed:

# List files in unified directory if it exists
find . -type d -name "unified" 2>/dev/null | head -5

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Check js directory structure for API definitions
find ./js -name "*.ts" -o -name "*.js" | grep -E "(unified|index)" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Search for unified directory and API implementations
find . -type f -name "*.ts" -path "*/unified*" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Search for getOrCreateAtaInterface function definition
rg "export.*getOrCreateAtaInterface|function getOrCreateAtaInterface|const getOrCreateAtaInterface" -B 2 -A 15

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Search for the return type definition
rg "interface.*AtaInterface|type.*AtaInterface" -B 2 -A 10

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Look for coding guidelines document
find . -name "*.md" -path "*guide*" -o -name "GUIDELINES*" -o -name "CODING*" | grep -i guide

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Check the actual structure returned - look for .parsed in code
rg "\.parsed\s*\." -B 3 -A 1 | head -50

Repository: Lightprotocol/light-protocol

Length of output: 680


🏁 Script executed:

# Check if there are other examples in the file that demonstrate best practices
cat -n ctoken_for_payments.md | grep -E "(console\.|import|//) " | head -40

Repository: Lightprotocol/light-protocol

Length of output: 1188


🏁 Script executed:

# Verify the file structure and see all comment examples
rg "//\s" ctoken_for_payments.md | head -20

Repository: Lightprotocol/light-protocol

Length of output: 390


Remove console.log statements and add inline comments explaining API structure differences.

Lines 40 and 69 include logging without explanatory context, violating documentation guidelines. Unlike the logging at lines 258-260 (which includes comments explaining the output), these examples should either remove the console.log or add comments explaining why the logging is essential.

Additionally, the instruction-level examples (lines 73-92) need inline comments explaining:

  • Why createAssociatedTokenAccountInterfaceIdempotentInstruction requires CTOKEN_PROGRAM_ID as the final parameter, unlike the SPL equivalent
  • The purpose of the .parsed property returned by getOrCreateAtaInterface (already referenced at line 67)
🤖 Prompt for AI Agents
In ctoken_for_payments.md around lines 29 to 92, remove the bare console.log
statements at lines ~40 and ~69 and replace them with concise inline comments
that explain the API output structure (e.g., for
getOrCreateAssociatedTokenAccount / getOrCreateAtaInterface show which field
holds the address and which holds the token amount) and mirror the style used at
lines 258-260; additionally add short inline comments in the instruction-level
example (lines ~73-92) explaining why
createAssociatedTokenAccountInterfaceIdempotentInstruction requires
CTOKEN_PROGRAM_ID as the final parameter (to target the c-token program) and
what the .parsed property returned by getOrCreateAtaInterface contains and why
callers should use ata.parsed.address / ata.parsed.amount instead of ata.address
/ ata.amount.


---

## 2. Send Payments

**SPL Token:**

```typescript
import { transfer } from "@solana/spl-token";
const sourceAta = getAssociatedTokenAddressSync(mint, owner.publicKey);
const destinationAta = getAssociatedTokenAddressSync(mint, recipient);

await transfer(
connection,
payer,
sourceAta,
destinationAta,
owner,
amount,
decimals
);
```

**SPL Token (instruction-level):**

```typescript
import {
getAssociatedTokenAddressSync,
createTransferInstruction,
} from "@solana/spl-token";

const sourceAta = getAssociatedTokenAddressSync(mint, owner.publicKey);
const destinationAta = getAssociatedTokenAddressSync(mint, recipient);

const tx = new Transaction().add(
createTransferInstruction(sourceAta, destinationAta, owner.publicKey, amount)
);
```

**c-token:**

```typescript
const sourceAta = getAssociatedTokenAddressInterface(mint, owner.publicKey);
const destinationAta = getAssociatedTokenAddressInterface(mint, recipient);

await transferInterface(
rpc,
payer,
sourceAta,
mint,
destinationAta,
owner,
amount
);
```

**c-token (instruction-level):**

```typescript
import {
createLoadAtaInstructions,
createTransferInterfaceInstruction,
getAssociatedTokenAddressInterface,
} from "@lightprotocol/compressed-token/unified";

const sourceAta = getAssociatedTokenAddressInterface(mint, owner.publicKey);
const destinationAta = getAssociatedTokenAddressInterface(mint, recipient);

const tx = new Transaction().add(
...(await createLoadAtaInstructions(
rpc,
sourceAta,
owner.publicKey,
mint,
payer.publicKey
)),
createTransferInterfaceInstruction(
sourceAta,
destinationAta,
owner.publicKey,
amount
)
);
```
Comment on lines +161 to +176
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add inline comments explaining the instruction-level ATA loading pattern.

The createLoadAtaInstructions call is not self-explanatory. Add context about why this is needed: it loads the compressed ATA data on-chain so the transfer instruction can read it. This is necessary only at instruction-level; the high-level transferInterface handles this internally.

const tx = new Transaction().add(
  // Load ATA data on-chain for the transfer instruction to consume
  ...(await createLoadAtaInstructions(
    rpc,
    sourceAta,
    owner.publicKey,
    mint,
    payer.publicKey
  )),
  // Perform the actual transfer
  createTransferInterfaceInstruction(
    sourceAta,
    destinationAta,
    owner.publicKey,
    amount
  )
);
🤖 Prompt for AI Agents
In ctoken_for_payments.md around lines 161–176 the call to
createLoadAtaInstructions is not documented and readers may not understand why
ATA data is loaded at instruction-level; add an inline comment before the spread
that explains it loads the compressed ATA data on-chain so the following
transfer instruction can read it (this is necessary only when constructing
instruction-level transfers; the high-level transferInterface handles ATA
loading internally), and add a short comment before
createTransferInterfaceInstruction indicating that the next instruction performs
the actual transfer.


To ensure your recipient's ATA exists you can prepend an idempotent creation instruction in the same atomic transaction:

**SPL Token:**

```typescript
import {
getAssociatedTokenAddressSync,
createAssociatedTokenAccountIdempotentInstruction,
} from "@solana/spl-token";

const destinationAta = getAssociatedTokenAddressSync(mint, recipient);
const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(
payer.publicKey,
destinationAta,
recipient,
mint
);

new Transaction().add(createAtaIx, transferIx);
```

**c-token:**

```typescript
import {
getAssociatedTokenAddressInterface,
createAssociatedTokenAccountInterfaceIdempotentInstruction,
} from "@lightprotocol/compressed-token/unified";
import { CTOKEN_PROGRAM_ID } from "@lightprotocol/stateless.js";

const destinationAta = getAssociatedTokenAddressInterface(mint, recipient);
const createAtaIx = createAssociatedTokenAccountInterfaceIdempotentInstruction(
payer.publicKey,
destinationAta,
recipient,
mint,
CTOKEN_PROGRAM_ID
);

new Transaction().add(createAtaIx, transferIx);
```

---

## 3. Show Balance

**SPL Token:**

```typescript
import { getAccount } from "@solana/spl-token";

const account = await getAccount(connection, ata);
console.log(account.amount);
```

**c-token:**

```typescript
const ata = getAssociatedTokenAddressInterface(mint, owner);
const account = await getAtaInterface(rpc, ata, owner, mint);

console.log(account.parsed.amount);
```

---

## 4. Transaction History

**SPL Token:**

```typescript
const signatures = await connection.getSignaturesForAddress(ata);
```

**c-token:**

```typescript
// Unified: fetches both on-chain and compressed tx signatures
const result = await rpc.getSignaturesForOwnerInterface(owner);

console.log(result.signatures); // Merged + deduplicated
console.log(result.solana); // On-chain txs only
console.log(result.compressed); // Compressed txs only
Comment on lines +256 to +260
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove console.log() output from transaction history example; add guidance on when to use owner vs. address history.

See earlier comment on console.log removal. Additionally, add context on why you might use getSignaturesForAddressInterface(address) vs. getSignaturesForOwnerInterface(owner): the former gives history for a specific ATA, the latter gives all compressed transactions for an owner across all ATAs.

// Merged + deduplicated signatures (on-chain + compressed)
const result = await rpc.getSignaturesForOwnerInterface(owner);

// Use result.signatures for all activity, result.solana for on-chain only, result.compressed for compressed only
🤖 Prompt for AI Agents
In ctoken_for_payments.md around lines 256 to 260, remove the three console.log
statements showing result.signatures/result.solana/result.compressed and replace
them with a short explanatory comment; also add a one-line guidance clarifying
when to use getSignaturesForAddressInterface(address) (to fetch history for a
specific ATA) versus getSignaturesForOwnerInterface(owner) (to fetch all
compressed transactions for an owner across all ATAs), and indicate that
result.signatures is the merged/deduplicated set while result.solana and
result.compressed are on-chain-only and compressed-only respectively.

```

Use `getSignaturesForAddressInterface(address)` if you want address-specific rather than owner-wide history.

Comment on lines +222 to +264
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "ctoken_for_payments.md" -o -name "*ctoken*payments*" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 96


🏁 Script executed:

git ls-files | grep -i "ctoken.*payment\|payment.*ctoken" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 94


🏁 Script executed:

git ls-files | grep -i "docs" | grep -i "token" | head -30

Repository: Lightprotocol/light-protocol

Length of output: 2231


🏁 Script executed:

cat -n ctoken_for_payments.md | head -280

Repository: Lightprotocol/light-protocol

Length of output: 7933


🏁 Script executed:

rg "getAtaInterface" --type ts --type js -A 5 | head -60

Repository: Lightprotocol/light-protocol

Length of output: 220


🏁 Script executed:

rg "getSignaturesForOwnerInterface|getSignaturesForAddressInterface" --type ts --type js -A 3 | head -80

Repository: Lightprotocol/light-protocol

Length of output: 220


🏁 Script executed:

find . -name "unified*" -o -path "*/compressed-token/*" -name "*.ts" | grep -E "(index|types|unified)" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

find . -path "*/compressed-token*" -name "*.md" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 987


🏁 Script executed:

find . -path "*unified*" -type f | head -20

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

rg "getAtaInterface" --max-count 20 | head -80

Repository: Lightprotocol/light-protocol

Length of output: 321


🏁 Script executed:

find ./js/compressed-token -name "*.ts" -o -name "*.js" | grep -E "(index|unified)" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

ls -la ./js/compressed-token/

Repository: Lightprotocol/light-protocol

Length of output: 304


🏁 Script executed:

find ./js/compressed-token -type f -name "*.ts" | head -30

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

cat ./js/compressed-token/README.md

Repository: Lightprotocol/light-protocol

Length of output: 4917


🏁 Script executed:

rg "getSignaturesForOwnerInterface|getSignaturesForAddressInterface" -A 10 -B 2

Repository: Lightprotocol/light-protocol

Length of output: 1771


Remove or replace console.log statements; document API signatures and behavior.

The balance and transaction history sections have documentation gaps:

  1. Lines 230, 239, 258–260: Remove the three console.log statements in the transaction history example (lines 258–260) or replace all console.log calls with inline comments. Per guidelines, logging statements are not essential here—replace with comments like // result.signatures: array of merged signatures to show structure without the output calls.

  2. Line 237: Add inline comments documenting getAtaInterface() return type and behavior:

    const account = await getAtaInterface(rpc, ata, owner, mint);
    // Returns: { parsed: { amount: bigint, ... } }
    // Fetches current token balance for the ATA
  3. Line 256: Clarify that getSignaturesForOwnerInterface() is a method on the rpc object (from createRpc()), not an imported function. Add a comment: // rpc method: fetches merged on-chain and compressed signatures

  4. Line 263: Add a one-line example or explanation for getSignaturesForAddressInterface(address)—when/why to use it instead of getSignaturesForOwnerInterface().

🤖 Prompt for AI Agents
In ctoken_for_payments.md around lines 222 to 264, remove the three console.log
statements and replace them with inline comments describing the returned
structures, add a brief inline comment after the getAtaInterface call explaining
its return shape and that it fetches the current ATA token balance (e.g.
parsed.amount is bigint), clarify at line ~256 that
getSignaturesForOwnerInterface is an rpc instance method (from createRpc()) by
adding a comment like “rpc method: fetches merged on-chain and compressed
signatures”, and add a one-line note at ~263 explaining when to use
getSignaturesForAddressInterface(address) vs
getSignaturesForOwnerInterface(owner) (address-specific ATA history vs
owner-wide merged history).

---

## 5. Unwrap to SPL

When users need vanilla SPL tokens (eg., for CEX off-ramp):

**c-token -> SPL ATA:**

```typescript
import { getAssociatedTokenAddressSync } from "@solana/spl-token";

// SPL ATA must exist
const splAta = getAssociatedTokenAddressSync(mint, owner.publicKey);

await unwrap(rpc, payer, owner, mint, splAta, amount);
```

**c-token (instruction-level):**

```typescript
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import {
createLoadAtaInstructions,
createUnwrapInstruction,
getAssociatedTokenAddressInterface,
} from "@lightprotocol/compressed-token/unified";
import { getSplInterfaceInfos } from "@lightprotocol/compressed-token";

const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey);
const splAta = getAssociatedTokenAddressSync(mint, owner.publicKey);

const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint);
const splInterfaceInfo = splInterfaceInfos.find((i) => i.isInitialized);
Comment on lines +296 to +297
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add inline comments explaining the getSplInterfaceInfos lookup pattern.

The pattern of fetching splInterfaceInfos and finding the initialized one is opaque. Explain: SPL interface infos represent the underlying SPL token pool backing this c-token mint. Only one will be initialized; we fetch it to pass to the unwrap instruction so it knows which SPL account to transfer to.

// Fetch SPL interface info for this mint (required for unwrap)
const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint);
// Use the initialized SPL interface info (there will be only one)
const splInterfaceInfo = splInterfaceInfos.find((i) => i.isInitialized);
🤖 Prompt for AI Agents
In ctoken_for_payments.md around lines 296 to 297, add brief inline comments
explaining the getSplInterfaceInfos lookup pattern: note that
getSplInterfaceInfos returns SPL interface info objects representing underlying
SPL token pools for the c-token mint, only one of which will be initialized for
this mint, and that we select the initialized entry to pass into the unwrap
instruction so it knows which SPL account to transfer to; update the two lines
to include those explanatory comments directly above or on the same line.


const tx = new Transaction().add(
...(await createLoadAtaInstructions(
rpc,
ctokenAta,
owner.publicKey,
mint,
payer.publicKey
)),
createUnwrapInstruction(
ctokenAta,
splAta,
owner.publicKey,
mint,
amount,
splInterfaceInfo
)
);
```

---

## Quick Reference

| Operation | SPL Token | c-token (unified) |
| -------------- | ------------------------------------- | -------------------------------------- |
| Get/Create ATA | `getOrCreateAssociatedTokenAccount()` | `getOrCreateAtaInterface()` |
| Derive ATA | `getAssociatedTokenAddress()` | `getAssociatedTokenAddressInterface()` |
| Transfer | `transferChecked()` | `transferInterface()` |
| Get Balance | `getAccount()` | `getAtaInterface()` |
| Tx History | `getSignaturesForAddress()` | `rpc.getSignaturesForOwnerInterface()` |
| Exit to SPL | N/A | `unwrap()` |

---

Need help with integration? Reach out: [support@lightprotocol.com](mailto:support@lightprotocol.com)
Comment on lines +1 to +333
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Overall: Strong foundation, but needs API documentation, cross-references, and clearer explanations of c-token-specific patterns.

This guide does a great job side-by-side comparing SPL and c-token workflows. However, three categories of work remain:

  1. Cross-reference missing docs (lines 1–21): Link to main compressed-token documentation, ACCOUNTS.md, and related instruction references. Per codebase guidelines, documentation should have a main entry point with an instruction index.

  2. API documentation gaps: Functions like getAtaInterface(), getOrCreateAtaInterface(), transferInterface(), and getSplInterfaceInfos() are used but not documented. Add return types, error conditions, and purpose to the guide or link to an API reference.

  3. Explain c-token specifics (lines 45–176, 283–316): Developers migrating from SPL need to understand:

    • Why c-token examples require CTOKEN_PROGRAM_ID but SPL doesn't
    • What .parsed means on account objects
    • Why createLoadAtaInstructions() is needed (zk state loading)
    • Why splInterfaceInfo is required for unwrap (and what to do if it's not found)

These are the blocking issues. The other comments above address specific fixes. Once addressed, this will be an excellent quick-start guide for developers.

🤖 Prompt for AI Agents
In ctoken_for_payments.md (start lines 1–21, 45–176, 283–316) add the missing
cross-references and detailed API/docs: at the top (lines 1–21) insert links to
the main compressed-token documentation, ACCOUNTS.md, and an instruction
reference/index per repo docs; for the API gaps, add short API doc blocks or
links for getAtaInterface(), getOrCreateAtaInterface(), transferInterface(),
createLoadAtaInstructions(), getSplInterfaceInfos(), unwrap() that list return
types, possible errors/edge cases, and intended usage (or point to the canonical
API reference); in the c-token explanation sections (lines 45–176 and 283–316)
add concise notes explaining why CTOKEN_PROGRAM_ID is required, what the .parsed
field contains, why createLoadAtaInstructions() is necessary (zk state/load
behavior), and why splInterfaceInfo is needed for unwrap (and what to do if none
found, e.g., error handling or fallback), ensuring each explanation is a couple
of sentences and includes links to deeper docs where appropriate.

Loading
Loading