-
Notifications
You must be signed in to change notification settings - Fork 86
feat: ts interface #2071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: ts interface #2071
Changes from all commits
0374e18
50637f0
2002872
e5c280e
bb10ec7
d1ca4dc
8025744
ea58ed1
0b77338
bc50c79
e5f623e
b627226
3c9cbf5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
Additionally, the setup imports should include brief inline comments explaining each function's purpose, or cross-reference an API reference document. Consider adding:
🤖 Prompt for AI Agents |
||
|
|
||
| --- | ||
|
|
||
| ## 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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 ( Suggested approach: Either remove these lines entirely or preface them with a comment like -console.log(ata.amount);
+// Account object retrieved; use ata.amount to access token balanceAlso applies to: 69-69, 229-230, 239-239, 249-249, 256-260 🤖 Prompt for AI Agents |
||
| ``` | ||
|
Comment on lines
+32
to
+41
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 // 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 |
||
|
|
||
| **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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Find the ctoken_for_payments.md file
find . -name "ctoken_for_payments.md" -type fRepository: Lightprotocol/light-protocol Length of output: 96 🏁 Script executed: # Search for getOrCreateAtaInterface implementation
rg "getOrCreateAtaInterface" --type ts --type js -B 3 -A 5Repository: Lightprotocol/light-protocol Length of output: 220 🏁 Script executed: # Search for createAssociatedTokenAccountInterfaceIdempotentInstruction signature
rg "createAssociatedTokenAccountInterfaceIdempotentInstruction" --type ts --type js -B 2 -A 10Repository: Lightprotocol/light-protocol Length of output: 220 🏁 Script executed: # Find unified API exports
find . -path "*/unified*" -type f -name "*.ts" -o -name "*.js" | head -20Repository: 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 -100Repository: 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 5Repository: Lightprotocol/light-protocol Length of output: 2214 🏁 Script executed: # Search for createAssociatedTokenAccountInterfaceIdempotentInstruction
rg "createAssociatedTokenAccountInterfaceIdempotentInstruction" -B 2 -A 8Repository: 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 -5Repository: 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 -20Repository: Lightprotocol/light-protocol Length of output: 54 🏁 Script executed: # Search for unified directory and API implementations
find . -type f -name "*.ts" -path "*/unified*" | head -20Repository: Lightprotocol/light-protocol Length of output: 54 🏁 Script executed: # Search for getOrCreateAtaInterface function definition
rg "export.*getOrCreateAtaInterface|function getOrCreateAtaInterface|const getOrCreateAtaInterface" -B 2 -A 15Repository: Lightprotocol/light-protocol Length of output: 54 🏁 Script executed: # Search for the return type definition
rg "interface.*AtaInterface|type.*AtaInterface" -B 2 -A 10Repository: 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 guideRepository: 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 -50Repository: 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 -40Repository: 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 -20Repository: 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:
🤖 Prompt for AI Agents |
||
|
|
||
| --- | ||
|
|
||
| ## 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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 |
||
|
|
||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 // 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 |
||
| ``` | ||
|
|
||
| Use `getSignaturesForAddressInterface(address)` if you want address-specific rather than owner-wide history. | ||
|
|
||
|
Comment on lines
+222
to
+264
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -name "ctoken_for_payments.md" -o -name "*ctoken*payments*" | head -20Repository: Lightprotocol/light-protocol Length of output: 96 🏁 Script executed: git ls-files | grep -i "ctoken.*payment\|payment.*ctoken" | head -20Repository: Lightprotocol/light-protocol Length of output: 94 🏁 Script executed: git ls-files | grep -i "docs" | grep -i "token" | head -30Repository: Lightprotocol/light-protocol Length of output: 2231 🏁 Script executed: cat -n ctoken_for_payments.md | head -280Repository: Lightprotocol/light-protocol Length of output: 7933 🏁 Script executed: rg "getAtaInterface" --type ts --type js -A 5 | head -60Repository: Lightprotocol/light-protocol Length of output: 220 🏁 Script executed: rg "getSignaturesForOwnerInterface|getSignaturesForAddressInterface" --type ts --type js -A 3 | head -80Repository: Lightprotocol/light-protocol Length of output: 220 🏁 Script executed: find . -name "unified*" -o -path "*/compressed-token/*" -name "*.ts" | grep -E "(index|types|unified)" | head -20Repository: Lightprotocol/light-protocol Length of output: 54 🏁 Script executed: find . -path "*/compressed-token*" -name "*.md" | head -20Repository: Lightprotocol/light-protocol Length of output: 987 🏁 Script executed: find . -path "*unified*" -type f | head -20Repository: Lightprotocol/light-protocol Length of output: 54 🏁 Script executed: rg "getAtaInterface" --max-count 20 | head -80Repository: Lightprotocol/light-protocol Length of output: 321 🏁 Script executed: find ./js/compressed-token -name "*.ts" -o -name "*.js" | grep -E "(index|unified)" | head -20Repository: 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 -30Repository: Lightprotocol/light-protocol Length of output: 54 🏁 Script executed: cat ./js/compressed-token/README.mdRepository: Lightprotocol/light-protocol Length of output: 4917 🏁 Script executed: rg "getSignaturesForOwnerInterface|getSignaturesForAddressInterface" -A 10 -B 2Repository: 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:
🤖 Prompt for AI Agents |
||
| --- | ||
|
|
||
| ## 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 // 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 |
||
|
|
||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
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 |
||
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.