diff --git a/.gitignore b/.gitignore index b75b7cf2..96b18227 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,7 @@ docs-audit.md docs-audit.txt docs-anchor.json product-docs.json -tabs-docs.json \ No newline at end of file +tabs-docs.json + +# Local helper scripts with hardcoded paths +scripts/copy-payments-snippets.sh \ No newline at end of file diff --git a/README.md b/README.md index 7b0350d7..3f35c311 100644 --- a/README.md +++ b/README.md @@ -8,5 +8,6 @@ Resources for developers to learn and build: Find more resources here: - Documentation: [zkcompression.com](https://www.zkcompression.com). +- Index for LLMs and Agents: [llms.txt](https://www.zkcompression.com/llms.txt) - [Examples](https://github.com/Lightprotocol/examples-light-token) - [Agent Skills](https://github.com/Lightprotocol/skills/tree/main) diff --git a/ai-tools/agent-skills.mdx b/ai-tools/agent-skills.mdx index 2efb1dbb..09744ba3 100644 --- a/ai-tools/agent-skills.mdx +++ b/ai-tools/agent-skills.mdx @@ -36,15 +36,15 @@ npx skills add Lightprotocol/skills | Use case | Skill | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -| Build rent-free Solana programs with Light SDK (Anchor or Pinocchio). Includes router integration. | [light-sdk](https://github.com/Lightprotocol/skills/tree/main/skills/light-sdk) | -| Use Light Token client SDKs (TypeScript and Rust) for mints, ATAs, transfers | [light-token-client](https://github.com/Lightprotocol/skills/tree/main/skills/light-token-client) | -| Stream account state via Laserstream gRPC | [data-streaming](https://github.com/Lightprotocol/skills/tree/main/skills/data-streaming) | -| Wallets and payment flows with light-token. Includes privy, wallet adapter, mobile wallet adapter signing. Optional nullifier to prevent your onchain instruction from being executed more than once. | [payments-and-wallets](https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets) | -| Airdrops, DePIN, token distribution | [token-distribution](https://github.com/Lightprotocol/skills/tree/main/skills/token-distribution) | -| Anti-double-spend nullifiers for Privacy-preserving ZK programs | [zk-nullifier](https://github.com/Lightprotocol/skills/tree/main/skills/zk-nullifier) | -| Testing programs and clients on localnet, devnet, mainnet | [testing](https://github.com/Lightprotocol/skills/tree/main/skills/testing) | -| For per-user state, DePIN nodes, and infrequently accessed app state with compressed PDAs | [solana-compression](https://github.com/Lightprotocol/skills/tree/main/skills/solana-compression) | -| Help with Debugging and Questions via DeepWiki MCP | [ask-mcp](https://github.com/Lightprotocol/skills/tree/main/skills/ask-mcp) | +| For stablecoin payment flows and wallet integrations on Solana. | [payments](https://github.com/Lightprotocol/skills/tree/main/skills/payments) | +| For client development with Light Token APIs on Solana. | [light-token-client](https://github.com/Lightprotocol/skills/tree/main/skills/light-token-client) | +| For Solana program development with tokens and PDAs. | [light-sdk](https://github.com/Lightprotocol/skills/tree/main/skills/light-sdk) | +| For data pipelines, aggregators, or indexers, real-time account state streaming on Solana with light account hot/cold lifecycle tracking | [data-streaming](https://github.com/Lightprotocol/skills/tree/main/skills/data-streaming) | +| For token distribution on Solana 5000x cheaper than SPL (rewards, airdrops, depins, ...) | [token-distribution](https://github.com/Lightprotocol/skills/tree/main/skills/token-distribution) | +| For custom ZK Solana programs and privacy-preserving applications to prevent double spending | [zk-nullifier](https://github.com/Lightprotocol/skills/tree/main/skills/zk-nullifier) | +| For program development on Solana with infrequently accessed state, such as per-user state, DePIN registrations, ... | [solana-compression](https://github.com/Lightprotocol/skills/tree/main/skills/solana-compression) | +| For testing with Light Protocol programs and clients on localnet, devnet, and mainnet validation | [testing](https://github.com/Lightprotocol/skills/tree/main/skills/testing) | +| For questions about compressed accounts, Light SDK, Solana development, Claude Code features, or agent skills | [ask-mcp](https://github.com/Lightprotocol/skills/tree/main/skills/ask-mcp) | > View all skills here: https://github.com/Lightprotocol/skills. diff --git a/api-reference/solana-to-light-comparison.mdx b/api-reference/solana-to-light-comparison.mdx index 4b50c61f..72bc9127 100644 --- a/api-reference/solana-to-light-comparison.mdx +++ b/api-reference/solana-to-light-comparison.mdx @@ -108,7 +108,7 @@ See [JSON RPC Methods](/api-reference/json-rpc-methods/overview). Fetch the parsed state of a light token account, including hot and cold balances. > [Guide](/light-token/payments/integration-guide) | -> [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/get-balance.ts) +> [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-balance.ts) @@ -126,7 +126,7 @@ console.log(account.amount); Fetch merged and deduplicated transaction history across on-chain and compressed transactions. > [Guide](/light-token/payments/integration-guide) | -> [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/get-history.ts) +> [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-history.ts) @@ -140,7 +140,7 @@ const signatures = await connection.getSignaturesForAddress(ata); ## Client -Both `@lightprotocol/compressed-token` (TypeScript) and `light_token_client` (Rust) match the SPL Token API. +Both `@lightprotocol/compressed-token` (TypeScript) and `light_token_client` (Rust) are a superset of the SPL Token API. Every SPL operation has a 1:1 equivalent. The SDKs add unified balance queries (`getAtaInterface`), automatic load/decompress for cold state, wrap/unwrap for SPL and Token 2022 interop, and cross-program dispatch via `Interface` methods like `transferInterface` that auto-detect the token program. ### Create mint @@ -650,8 +650,8 @@ For payment flows, `createTransferInterfaceInstructions` returns transferring in one call: > [Guide](/light-token/payments/integration-guide) | -> [Example (instruction)](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/send-instruction.ts) | -> [Example (action)](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/send-action.ts) +> [Example (instruction)](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-instruction.ts) | +> [Example (action)](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-action.ts) ```typescript import { Transaction } from "@solana/web3.js"; @@ -895,6 +895,48 @@ let ix = approve( +### Delegated transfer + +Transfer tokens as an approved delegate. The delegate is the transaction authority; the owner's signature is not required. + +> [Guide](/light-token/payments/spend-permissions) + + + + +```typescript title="Light" +import { transferInterface } from "@lightprotocol/compressed-token/unified"; + +const tx = await transferInterface( + rpc, + payer, + sourceAta, + mint, + recipient, + delegate, + amount, + undefined, + undefined, + { owner: owner.publicKey } +); +``` +```typescript title="SPL" +import { transfer } from "@solana/spl-token"; + +// delegate signs instead of owner +const tx = await transfer( + connection, + payer, + sourceAta, + destinationAta, + delegate, + amount +); +``` + + + + ### Revoke delegate Remove all delegate permissions. @@ -1123,7 +1165,7 @@ invoke(&ix, &[mint, rent_sysvar])?; Create a rent-free associated token account via CPI. > [Guide](/light-token/cookbook/create-ata) | -> [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-ata/src/lib.rs) | +> [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-associated-token-account/src/lib.rs) | > [Source](https://docs.rs/light-token) @@ -1455,7 +1497,7 @@ token_metadata_initialize( ### Create associated token account > [Guide](/light-token/cookbook/create-ata) | -> [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-ata) | +> [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-associated-token-account) | > [Source](https://docs.rs/light-token) diff --git a/cspell.json b/cspell.json index 314c5494..95d9c9d7 100644 --- a/cspell.json +++ b/cspell.json @@ -197,7 +197,11 @@ "clippy", "Raydium", "raydium", - "USDG" + "USDG", + "Gamal", + "blockhashes", + "supermajority", + "sqds" ], "ignorePaths": [ "node_modules", diff --git a/docs.json b/docs.json index 4558a8e6..76cf1635 100644 --- a/docs.json +++ b/docs.json @@ -33,19 +33,31 @@ ] }, { - "group": "Light Token Program", + "group": "Light Token APIs", "pages": [ "light-token/welcome", { "group": "Cookbook", "pages": [ - "light-token/cookbook/create-mint", + { + "group": "Mint Accounts", + "pages": [ + "light-token/cookbook/create-mint", + "light-token/cookbook/add-interface-pda" + ] + }, "light-token/cookbook/create-ata", "light-token/cookbook/create-token-account", "light-token/cookbook/mint-to", "light-token/cookbook/transfer-interface", "light-token/cookbook/transfer-checked", - "light-token/cookbook/approve-revoke", + { + "group": "Delegates", + "pages": [ + "light-token/cookbook/approve-revoke", + "light-token/cookbook/transfer-delegated" + ] + }, "light-token/cookbook/freeze-thaw", "light-token/cookbook/wrap-unwrap", "light-token/cookbook/load-ata", @@ -53,11 +65,36 @@ "light-token/cookbook/burn" ] }, + "light-token/extensions/overview", { "group": "For Stablecoin Payments", "pages": [ "light-token/payments/overview", - "light-token/payments/integration-guide" + { + "group": "Send Payments", + "pages": [ + "light-token/payments/basic-payment", + "light-token/payments/batch-payments", + "light-token/payments/payment-with-memo" + ] + }, + "light-token/payments/receive-payments", + { + "group": "Verify Payments", + "pages": [ + "light-token/payments/verify-payments", + "light-token/payments/verify-recipient-address" + ] + }, + "light-token/payments/wrap-unwrap", + { + "group": "Advanced", + "pages": [ + "light-token/payments/spend-permissions", + "pda/compressed-pdas/nullifier-pda" + ] + }, + "light-token/payments/production-readiness" ] }, { @@ -66,7 +103,7 @@ "light-token/wallets/overview", "light-token/wallets/privy", "light-token/wallets/wallet-adapter", - "light-token/wallets/sponsor-top-ups" + "light-token/wallets/gasless-transactions" ] }, { @@ -103,7 +140,6 @@ "pages": [ "pda/compressed-pdas/overview", "pda/compressed-pdas/guides/client-guide", - "pda/compressed-pdas/how-to-create-nullifier-pdas", { "group": "Program Guides", "expanded": true, @@ -317,6 +353,14 @@ } }, "redirects": [ + { + "source": "/light-token/payments/nullifier-pda", + "destination": "https://zkcompression.com/pda/compressed-pdas/nullifier-pda" + }, + { + "source": "/light-token/nullifier-pda", + "destination": "https://zkcompression.com/pda/compressed-pdas/nullifier-pda" + }, { "source": "/airdrop", "destination": "https://zkcompression.com/token-distribution" @@ -419,7 +463,7 @@ }, { "source": "/compressed-pdas/guides/how-to-create-nullifier-pdas", - "destination": "https://zkcompression.com/pda/compressed-pdas/how-to-create-nullifier-pdas" + "destination": "https://zkcompression.com/pda/compressed-pdas/nullifier-pda" }, { "source": "/compressed-pdas/guides/how-to-reinitialize-compressed-accounts", @@ -935,7 +979,15 @@ }, { "source": "/light-token/toolkits/sponsor-top-ups", - "destination": "https://zkcompression.com/light-token/wallets/sponsor-top-ups" + "destination": "https://zkcompression.com/light-token/wallets/gasless-transactions" + }, + { + "source": "/light-token/wallets/sponsor-top-ups", + "destination": "https://zkcompression.com/light-token/wallets/gasless-transactions" + }, + { + "source": "/light-token/payments/fee-abstraction", + "destination": "https://zkcompression.com/light-token/wallets/gasless-transactions" }, { "source": "/light-token/toolkits/for-streaming-mints", @@ -951,7 +1003,7 @@ }, { "source": "/pda/compressed-pdas/guides/how-to-create-nullifier-pdas", - "destination": "https://zkcompression.com/pda/compressed-pdas/how-to-create-nullifier-pdas" + "destination": "https://zkcompression.com/pda/compressed-pdas/nullifier-pda" } ] } diff --git a/faq.mdx b/faq.mdx index 3d639d74..5bc3bdda 100644 --- a/faq.mdx +++ b/faq.mdx @@ -5,6 +5,7 @@ keywords: ["spl token vs light token comparison", "token 2022 vs light token dif --- import { CodeCompare } from "/snippets/jsx/code-compare.jsx"; +import ExtensionsTable from "/snippets/extensions-table.mdx"; import CompressibleRentExplained from "/snippets/compressible-rent-explained.mdx"; import RentSponsorshipExplained from "/snippets/rent-sponsorship-explained.mdx"; import { RentLifecycleVisualizer } from "/snippets/jsx/rent-lifecycle-visualizer.jsx"; @@ -30,9 +31,9 @@ This is dealt with under the hood in a way that doesn’t disrupt the UX of what - + -Set the `payer` parameter to the sponsor's public key on any Light Token instruction. The sponsor pays SOL for rent top-ups while the user only signs to authorize the transfer. +Set the `payer` parameter to the sponsor's public key on any Light Token instruction. The sponsor pays SOL for rent top-ups and transaction fees while the user only signs to authorize the transfer. ```typescript const ix = createLightTokenTransferInstruction( @@ -40,13 +41,13 @@ const ix = createLightTokenTransferInstruction( recipientAta, sender.publicKey, amount, - sponsor.publicKey, // sponsor pays rent top-ups + sponsor.publicKey, // sponsor pays rent top-ups and transaction fees ); await sendAndConfirmTransaction(rpc, tx, [sponsor, sender]); ``` - + @@ -75,16 +76,15 @@ Light token is a high-performance token standard that is functionally equivalent -Yes! Light Token is deployed on devnet. You can start integrating today with our [Quickstart](/light-token/quickstart) and [Toolkits](/light-token/welcome). +Yes! Light Token is live on Solana mainnet. Start integrating with the [Quickstart](/light-token/quickstart) and [Toolkits](/light-token/welcome). -For production on mainnet, use [Compressed Tokens V1](/resources/legacy-compressed-tokens), which are live and supported by leading wallets such as Phantom and Backpack. +For token distribution use cases (airdrops, claims), see [Compressed Tokens](/resources/legacy-compressed-tokens), supported by leading wallets such as Phantom and Backpack. -No. The `light-token-sdk` methods are a superset of the SPL-token API. -Find examples below. +No. The `light-token-sdk` methods are a superset of the SPL-token API — every SPL operation (transfer, approve, revoke, freeze, thaw, burn, close) plus unified balance aggregation, automatic load/decompress, wrap/unwrap, and cross-program dispatch. See "What does 'superset of the SPL-token API' mean?" below for details. + + +The Light Token SDK covers every SPL Token operation and adds extra capabilities: + + + + + + + + + + + + + + + + + + + + + + +
CategoryWhat it does
**Cross-program dispatch**`Interface` methods (e.g., `transferInterface`) auto-detect the token program and dispatch to SPL, Token 2022, or Light Token.
**Unified balance**`getAtaInterface` returns a unified balance for a given mint, aggregating Light Token (hot + cold), SPL, and Token 2022 sources.
**Wrap / Unwrap**For interoperability with applications that don't support Light Token yet, you can wrap / unwrap SPL or Token 2022 tokens into Light Token associated token accounts and back.
+ + + Side-by-side mapping of every Light Token instruction against its SPL/Solana equivalent. + + +
+ Yes, light-token accounts can hold tokens from light, SPL, or Token 2022 mints. @@ -159,26 +192,13 @@ The account is loaded into hot account state in-flight when someone interacts wi -Extensions are under development. Additional extensions can be requested. - -Coming soon: - -- MetadataPointer -- TokenMetadata -- InterestBearingConfig -- GroupPointer -- GroupMemberPointer -- TokenGroup -- TokenGroupMember -- MintCloseAuthority -- TransferFeeConfig -- DefaultAccountState -- PermanentDelegate -- TransferHook -- Pausable -- ConfidentialTransferMint -- ConfidentialTransferFeeConfig -- ConfidentialMintBurn +Light Token supports 16 Token-2022 extensions. Some have restrictions. + + + + + +Additional extensions can be requested. diff --git a/learn/light-token-standard.mdx b/learn/light-token-standard.mdx index 437f4a4c..002f4c6b 100644 --- a/learn/light-token-standard.mdx +++ b/learn/light-token-standard.mdx @@ -1,5 +1,5 @@ --- -title: Core concepts of the Light Token program (Beta) +title: Core concepts of the Light Token program sidebarTitle: Core Concepts (Light Token Program) description: The Light Token Program is a high performance token program that reduces the cost of account creations by 200x, while being more CU efficient than SPL on hot paths. keywords: ["light token standard on solana", "token 2022 on solana", "token extensions on solana", "spl token on solana", "rent free tokens on solana"] @@ -79,7 +79,7 @@ Light-mints **uniquely represent a token on Solana and store its global metadata pub struct CompressedMint { pub base: BaseMint, pub metadata: CompressedMintMetadata, - /// Reserved bytes for T22 layout compatibility (padding to reach byte 165) + /// Reserved bytes for Token 2022 layout compatibility (padding to reach byte 165) pub reserved: [u8; 49], /// Account type discriminator at byte 165 (1 = Mint, 2 = Account) pub account_type: u8, diff --git a/light-token/cookbook/add-interface-pda.mdx b/light-token/cookbook/add-interface-pda.mdx new file mode 100644 index 00000000..6a30f727 --- /dev/null +++ b/light-token/cookbook/add-interface-pda.mdx @@ -0,0 +1,27 @@ +--- +title: Add Interface PDA to Existing SPL / Token 2022 Mints +sidebarTitle: Add Interface PDA to Existing Mints +description: Create an interface PDA for an existing SPL or Token 2022 mint for interoperability with Light Token. Does not require mint authority. +--- + +import TokenPoolActionCode from "/snippets/code-snippets/light-token/create-token-pool/action.mdx"; +import TokenPoolInstructionCode from "/snippets/code-snippets/light-token/create-token-pool/instruction.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; + + + + + + + + + + +## Related Guides + + + + + + + diff --git a/light-token/cookbook/approve-revoke.mdx b/light-token/cookbook/approve-revoke.mdx index 8141d642..466b415b 100644 --- a/light-token/cookbook/approve-revoke.mdx +++ b/light-token/cookbook/approve-revoke.mdx @@ -1,7 +1,7 @@ --- title: Approve and Revoke Delegates sidebarTitle: Approve / Revoke -description: Rust client guide to approve and revoke delegates for Light Token accounts. Includes step-by-step implementation and full code examples. +description: Guide to approve and revoke delegates for Light Token accounts. Includes step-by-step implementation and full code examples. keywords: ["approve delegate solana", "revoke delegate solana", "token delegation"] --- @@ -32,6 +32,7 @@ import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; import ApproveRevokeAiPrompt from "/snippets/ai-prompts/ts-cookbook/approve-revoke.mdx"; import RustApproveRevokeAiPrompt from "/snippets/ai-prompts/rust-cookbook/approve-revoke.mdx"; import ProgramApproveRevokeAiPrompt from "/snippets/ai-prompts/program-cookbook/approve-revoke.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. Approve grants a delegate permission to transfer up to a specified amount of tokens from your account. * Each token account can have only one delegate at a time. @@ -48,7 +49,7 @@ import ProgramApproveRevokeAiPrompt from "/snippets/ai-prompts/program-cookbook/ -`approve` grants a delegate permission to transfer up to a specified amount of tokens. +`approveInterface` grants a delegate permission to transfer up to a specified amount of tokens. -`revoke` removes all delegate permissions from a Light Token account. +`revokeInterface` removes all delegate permissions from a Light Token account. - - Find the source code: - [delegate-approve.ts](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-approve.ts) - | - [delegate-revoke.ts](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-revoke.ts) - - -### Approve a delegate +### Prerequisites - - -### Revoke a delegate +### Approve or revoke delegates + + Find the source code: + [delegate-approve.ts](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-approve.ts) + | + [delegate-revoke.ts](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-revoke.ts) + | + [delegate-approve (instruction)](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/delegate-approve.ts) + | + [delegate-revoke (instruction)](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/delegate-revoke.ts) + + + + + + + + + + + + + + + + + @@ -319,12 +337,11 @@ revoke_cpi.invoke_signed(&[signer_seeds])?; -# Next Steps +## Related Guides - + + + + + + diff --git a/light-token/cookbook/burn.mdx b/light-token/cookbook/burn.mdx index 23e16491..39a5b7b2 100644 --- a/light-token/cookbook/burn.mdx +++ b/light-token/cookbook/burn.mdx @@ -19,6 +19,7 @@ import AnchorProgramCode from "/snippets/code-snippets/light-token/burn/anchor-p import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; import RustBurnAiPrompt from "/snippets/ai-prompts/rust-cookbook/burn.mdx"; import ProgramBurnAiPrompt from "/snippets/ai-prompts/program-cookbook/burn.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. Burn permanently destroys tokens by reducing the balance in a token account. @@ -145,12 +146,11 @@ BurnCpi { -# Next Steps +## Related Guides - + + + + + + diff --git a/light-token/cookbook/close-token-account.mdx b/light-token/cookbook/close-token-account.mdx index 8e8628dc..ab7f6729 100644 --- a/light-token/cookbook/close-token-account.mdx +++ b/light-token/cookbook/close-token-account.mdx @@ -20,6 +20,7 @@ import AnchorProgramCode from "/snippets/code-snippets/light-token/close-token-a import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; import RustCloseTokenAccountAiPrompt from "/snippets/ai-prompts/rust-cookbook/close-token-account.mdx"; import ProgramCloseTokenAccountAiPrompt from "/snippets/ai-prompts/program-cookbook/close-token-account.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. Closing a Light Token account transfers remaining lamports to a destination account and the rent sponsor can reclaim sponsored rent. 2. Light token accounts can be closed by the owner. @@ -152,14 +153,11 @@ CloseAccountCpi { -# Next Steps +## Related Guides -{" "} + + + + - + diff --git a/light-token/cookbook/create-ata.mdx b/light-token/cookbook/create-ata.mdx index 125b728f..831ff425 100644 --- a/light-token/cookbook/create-ata.mdx +++ b/light-token/cookbook/create-ata.mdx @@ -32,6 +32,7 @@ import CreateAtaAiPrompt from "/snippets/ai-prompts/ts-cookbook/create-ata.mdx"; import RustCreateAtaAiPrompt from "/snippets/ai-prompts/rust-cookbook/create-ata.mdx"; import ProgramCreateAtaCpiAiPrompt from "/snippets/ai-prompts/program-cookbook/create-ata-cpi.mdx"; import ProgramCreateAtaMacrosAiPrompt from "/snippets/ai-prompts/program-cookbook/create-ata-macros.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. Associated Light Token accounts can hold token balances of light, SPL, or Token 2022 mints. 2. Light-ATAs are on-chain accounts like SPL ATA's, but the light token program sponsors the rent-exemption cost for you. @@ -337,14 +338,11 @@ pub ata: UncheckedAccount<'info>, -# Next Steps +## Related Guides -{" "} + + + + - + diff --git a/light-token/cookbook/create-mint.mdx b/light-token/cookbook/create-mint.mdx index 2cd0192b..b79f720e 100644 --- a/light-token/cookbook/create-mint.mdx +++ b/light-token/cookbook/create-mint.mdx @@ -1,7 +1,7 @@ --- title: Create Mint Account with Token Metadata -sidebarTitle: Create Mint Account -description: Program and client guides to create a mint with token metadata. For existing SPL or Token 2022 mints add an interface PDA for interoperability. Includes step-by-step implementation and full code examples. +sidebarTitle: Create Light / SPL / Token 2022 Mint Account +description: Create a Light mint with token metadata, or create SPL and Token 2022 mints with interface PDA for interoperability. keywords: ["create mint account on solana", "rent free mint on solana", "rent free token mints on solana", "spl create mint alternative"] --- @@ -33,8 +33,7 @@ import SplActionCode from "/snippets/code-snippets/light-token/create-spl-mint/a import SplInstructionCode from "/snippets/code-snippets/light-token/create-spl-mint/instruction.mdx"; import T22ActionCode from "/snippets/code-snippets/light-token/create-t22-mint/action.mdx"; import T22InstructionCode from "/snippets/code-snippets/light-token/create-t22-mint/instruction.mdx"; -import TokenPoolActionCode from "/snippets/code-snippets/light-token/create-token-pool/action.mdx"; -import TokenPoolInstructionCode from "/snippets/code-snippets/light-token/create-token-pool/instruction.mdx"; + import RustActionCode from "/snippets/code-snippets/light-token/create-mint/rust-client/action.mdx"; import RustInstructionCode from "/snippets/code-snippets/light-token/create-mint/rust-client/instruction.mdx"; import AnchorProgramCode from "/snippets/code-snippets/light-token/create-mint/anchor-program/full-example.mdx"; @@ -45,10 +44,11 @@ import CreateMintAiPrompt from "/snippets/ai-prompts/ts-cookbook/create-mint.mdx import RustCreateMintAiPrompt from "/snippets/ai-prompts/rust-cookbook/create-mint.mdx"; import ProgramCreateMintCpiAiPrompt from "/snippets/ai-prompts/program-cookbook/create-mint-cpi.mdx"; import ProgramCreateMintMacrosAiPrompt from "/snippets/ai-prompts/program-cookbook/create-mint-macros.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. Mint accounts uniquely represent a token on Solana and store its global metadata. 2. Light mints are on-chain accounts like SPL mints, but the light token program sponsors the rent-exemption cost for you. -3. Add an interface PDA to existing SPL or Token 2022 mints for interoperability with Light Token. The interface PDA holds SPL or Token 2022 tokens when wrapped to Light Token. +3. [Add an interface PDA](/light-token/cookbook/add-interface-pda) to existing SPL or Token 2022 mints for interoperability with Light Token. The interface PDA holds SPL or Token 2022 tokens when wrapped to Light Token. @@ -63,7 +63,7 @@ import ProgramCreateMintMacrosAiPrompt from "/snippets/ai-prompts/program-cookbo `createMintInterface` is a unified interface that dispatches to different mint creation paths based on programId: -- `TOKEN_PROGRAM_ID` or `TOKEN_2022_PROGRAM_ID` → delegates to SPL or T22 `createMint` +- `TOKEN_PROGRAM_ID` or `TOKEN_2022_PROGRAM_ID` → delegates to SPL or Token 2022 `createMint` - Otherwise it defaults to `LIGHT_TOKEN_PROGRAM_ID` → creates a Light Token mint You can use the same interface regardless of mint type. @@ -93,7 +93,7 @@ Compare to SPL: The `mintAuthority` must be a `Signer` for light-mints but can be just a - `PublicKey` for SPL/T22. + `PublicKey` for SPL/Token 2022. @@ -113,24 +113,7 @@ Compare to SPL: -### Add Interface PDA to SPL / Token 2022 mints - - - - -Register an interface PDA for an existing SPL mint for interoperability with Light Token. No mint authority required. - - - - - - - - - - - - +## Create SPL mint with interface PDA Pass `TOKEN_PROGRAM_ID` to create a standard SPL mint with an interface PDA in one transaction for interoperability with Light Token. @@ -143,8 +126,7 @@ Pass `TOKEN_PROGRAM_ID` to create a standard SPL mint with an interface PDA in o - - +## Create Token 2022 mint with interface PDA Pass `TOKEN_2022_PROGRAM_ID` to create a Token-2022 mint with an interface PDA in one transaction for interoperability with Light Token. @@ -157,9 +139,6 @@ Pass `TOKEN_2022_PROGRAM_ID` to create a Token-2022 mint with an interface PDA i - - - @@ -578,12 +557,11 @@ pub mint: UncheckedAccount<'info>, -# Next Steps +## Related Guides - + + + + + + diff --git a/light-token/cookbook/create-token-account.mdx b/light-token/cookbook/create-token-account.mdx index fdf7c8f6..0deab881 100644 --- a/light-token/cookbook/create-token-account.mdx +++ b/light-token/cookbook/create-token-account.mdx @@ -26,6 +26,7 @@ import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; import RustCreateTokenAccountAiPrompt from "/snippets/ai-prompts/rust-cookbook/create-token-account.mdx"; import ProgramCreateTokenAccountCpiAiPrompt from "/snippets/ai-prompts/program-cookbook/create-token-account-cpi.mdx"; import ProgramCreateTokenAccountMacrosAiPrompt from "/snippets/ai-prompts/program-cookbook/create-token-account-macros.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. Light token accounts are Solana accounts that hold token balances of light, SPL, or Token 2022 mints. 2. Light token accounts are on-chain accounts like SPL ATA's, but the light token program sponsors the rent-exemption cost for you. @@ -276,11 +277,11 @@ pub vault: UncheckedAccount<'info>, -# Next Steps - +## Related Guides + + + + + + + diff --git a/light-token/cookbook/freeze-thaw.mdx b/light-token/cookbook/freeze-thaw.mdx index 9095fb96..d5121e63 100644 --- a/light-token/cookbook/freeze-thaw.mdx +++ b/light-token/cookbook/freeze-thaw.mdx @@ -22,6 +22,7 @@ import ThawAnchorProgramCode from "/snippets/code-snippets/light-token/thaw/anch import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; import RustFreezeThawAiPrompt from "/snippets/ai-prompts/rust-cookbook/freeze-thaw.mdx"; import ProgramFreezeThawAiPrompt from "/snippets/ai-prompts/program-cookbook/freeze-thaw.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. Freeze prevents all transfers or token burns from a specific Light Token account. 2. Once frozen, the account cannot send tokens, receive tokens, or be closed until it is thawed. @@ -235,12 +236,11 @@ ThawCpi { -# Next Steps +## Related Guides - + + + + + + diff --git a/light-token/cookbook/load-ata.mdx b/light-token/cookbook/load-ata.mdx index 73fd2f19..2104091f 100644 --- a/light-token/cookbook/load-ata.mdx +++ b/light-token/cookbook/load-ata.mdx @@ -9,11 +9,12 @@ import FullSetup from "/snippets/setup/full-setup.mdx"; import ActionCode from "/snippets/code-snippets/light-token/load-ata/action.mdx"; import InstructionCode from "/snippets/code-snippets/light-token/load-ata/instruction.mdx"; import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. `loadAta` unifies tokens from multiple sources to a single ATA: * Compressed tokens (cold Light Tokens) -> Decompresses -> light ATA * SPL balance (if wrap=true) -> Wraps -> light ATA - * T22 balance (if wrap=true) -> Wraps -> light ATA + * Token 2022 balance (if wrap=true) -> Wraps -> light ATA 2. Returns `null` if there's nothing to load (idempotent) @@ -42,3 +43,12 @@ Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob + +## Related Guides + + + + + + + diff --git a/light-token/cookbook/mint-to.mdx b/light-token/cookbook/mint-to.mdx index 0aa524a3..c2ba8767 100644 --- a/light-token/cookbook/mint-to.mdx +++ b/light-token/cookbook/mint-to.mdx @@ -28,6 +28,7 @@ import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; import MintToAiPrompt from "/snippets/ai-prompts/ts-cookbook/mint-to.mdx"; import RustMintToAiPrompt from "/snippets/ai-prompts/rust-cookbook/mint-to.mdx"; import ProgramMintToAiPrompt from "/snippets/ai-prompts/program-cookbook/mint-to.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. Mint To creates new tokens of a mint and deposits them to a specified token account. @@ -212,12 +213,11 @@ MintToCpi { -# Next Steps +## Related Guides - + + + + + + diff --git a/light-token/cookbook/transfer-checked.mdx b/light-token/cookbook/transfer-checked.mdx index 4d7bac6a..254ed4b7 100644 --- a/light-token/cookbook/transfer-checked.mdx +++ b/light-token/cookbook/transfer-checked.mdx @@ -1,62 +1,33 @@ --- title: Transfer Checked sidebarTitle: Transfer Checked -description: Rust client guide to transfer Light Tokens with decimal validation. Includes step-by-step implementation and full code examples. +description: Program CPI guide for Light Token transfer with decimal validation. Includes step-by-step implementation and full code examples. keywords: ["transfer tokens solana", "checked transfer", "decimal validation"] --- ---- - -import TokenClientPrerequisites from "/snippets/light-token-guides/light-token-client-prerequisites.mdx"; -import RustActionCode from "/snippets/code-snippets/light-token/transfer-checked/rust-client/action.mdx"; -import RustInstructionCode from "/snippets/code-snippets/light-token/transfer-checked/rust-client/instruction.mdx"; import AnchorProgramCode from "/snippets/code-snippets/light-token/transfer-checked/anchor-program/full-example.mdx"; import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; -import RustTransferCheckedAiPrompt from "/snippets/ai-prompts/rust-cookbook/transfer-checked.mdx"; import ProgramTransferCheckedAiPrompt from "/snippets/ai-prompts/program-cookbook/transfer-checked.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; 1. TransferChecked validates that the decimals parameter matches the mint's decimals. -2. Use for Light→Light transfers when you need decimal verification. -3. For transfers involving SPL or Token 2022 accounts, use [Transfer Interface](/light-token/cookbook/transfer-interface) instead. +2. Use `TransferCheckedCpi` for Light-to-Light transfers in on-chain programs when you need decimal verification without interface routing. +3. For transfers involving SPL or Token 2022 accounts, use [Transfer Interface](/light-token/cookbook/transfer-interface) instead. It uses `transferChecked` under the hood. - - - - - - -### Prerequisites - + - +`transferChecked` is called automatically under the hood by [`transferInterface`](/light-token/cookbook/transfer-interface) and `createTransferInterfaceInstructions`. You do not need to call it directly. - -### Transfer with Decimal Check + - - Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-libs/token-sdk/src/instruction/transfer_checked.rs). - + - - - - - - - - +`TransferChecked` is called automatically under the hood by [`TransferInterface`](/light-token/cookbook/transfer-interface). You do not need to call it directly. - - - - - - - @@ -136,14 +107,11 @@ TransferCheckedCpi { -# Next Steps +## Related Guides -{" "} + + + + - + diff --git a/light-token/cookbook/transfer-delegated.mdx b/light-token/cookbook/transfer-delegated.mdx new file mode 100644 index 00000000..636a9da5 --- /dev/null +++ b/light-token/cookbook/transfer-delegated.mdx @@ -0,0 +1,52 @@ +--- +title: Delegated Transfer +sidebarTitle: Delegated Transfer +description: Transfer tokens as an approved delegate. The delegate signs instead of the owner, spending within the approved allowance. +keywords: ["delegated transfer solana", "delegate transfer light token", "token delegation transfer"] +--- + +import FullSetup from "/snippets/setup/full-setup.mdx"; +import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; +import DelegateTransferAction from "/snippets/code-snippets/payments/spend-permissions/delegate-transfer.mdx"; +import DelegateTransferInstruction from "/snippets/code-snippets/payments/spend-permissions/delegate-transfer-instruction.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; + +`transferInterface` with `{ owner }` transfers tokens from an associated token account as an approved delegate. The delegate is the transaction authority. Only the delegate and fee payer sign; the owner's signature is not required. + +- The recipient is a wallet address (associated token account derived and created internally) +- The amount must be within the approved allowance +- Each transfer reduces the remaining allowance + + + + + + + + +### Delegated Transfer + + + + + + + + + + + + + + + + + +## Related Guides + + + + + + + diff --git a/light-token/cookbook/transfer-interface.mdx b/light-token/cookbook/transfer-interface.mdx index 2e2a4244..885f1417 100644 --- a/light-token/cookbook/transfer-interface.mdx +++ b/light-token/cookbook/transfer-interface.mdx @@ -1,6 +1,6 @@ --- title: Transfer Interface -description: Program and client guide for transfers between Light Token and SPL token accounts. The interface detects account types and invokes the right programs. +description: Transfer tokens between Light Token, SPL and Token 2022 accounts. The interface checks decimals under the hood and detects account types to invoke the right programs. keywords: ["transfer tokens on solana", "token transfer on solana", "interface methods on solana", "spl transfer for developers"] --- @@ -26,6 +26,7 @@ import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; import TransferInterfaceAiPrompt from "/snippets/ai-prompts/ts-cookbook/transfer-interface.mdx"; import RustTransferInterfaceAiPrompt from "/snippets/ai-prompts/rust-cookbook/transfer-interface.mdx"; import ProgramTransferInterfaceAiPrompt from "/snippets/ai-prompts/program-cookbook/transfer-interface.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; @@ -39,20 +40,20 @@ import ProgramTransferInterfaceAiPrompt from "/snippets/ai-prompts/program-cookb - + - + @@ -66,7 +67,7 @@ import ProgramTransferInterfaceAiPrompt from "/snippets/ai-prompts/program-cookb -The `transferInterface` function transfers tokens between token accounts (SPL, Token 2022, or Light) in a single call. +The `transferInterface` function transfers tokens between token accounts (SPL, Token 2022, or Light) in a single call and checks decimals. Compare to SPL: @@ -108,11 +109,66 @@ Compare to SPL: +### Advanced: Explicit Destination Account + +For PDA destinations or program-owned accounts where you need to specify the exact destination token account, use `transferToAccountInterface`. This is an edge case; most transfers should use `transferInterface` with a wallet address. + + + + +```typescript +import { + transferToAccountInterface, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token"; + +// The destination must be an existing token account (not a wallet address). +await transferToAccountInterface( + rpc, + payer, + sourceAta, + mint, + destinationTokenAccount, + owner, + amount, +); +``` + + + + +```typescript +import { + createTransferToAccountInterfaceInstructions, + getMintInterface, + sliceLast, +} from "@lightprotocol/compressed-token"; + +const decimals = (await getMintInterface(rpc, mint)).mint.decimals; + +// Returns TransactionInstruction[][]. The destination must be an existing token account. +const batches = await createTransferToAccountInterfaceInstructions( + rpc, + payer.publicKey, + mint, + amount, + owner.publicKey, + destinationTokenAccount, + decimals, +); + +const { rest: loads, last: transfer } = sliceLast(batches); +``` + + + + + -Use the unified `TransferInterface` to transfer tokens between token accounts (SPL, Token 2022, or Light) in a single call. +Use the unified `TransferInterface` to transfer tokens between token accounts (SPL, Token 2022, or Light) in a single call and checks decimals. -# Next Steps +## Related Guides -{" "} + + + + - + diff --git a/light-token/cookbook/wrap-unwrap.mdx b/light-token/cookbook/wrap-unwrap.mdx index a346ccbf..def603e2 100644 --- a/light-token/cookbook/wrap-unwrap.mdx +++ b/light-token/cookbook/wrap-unwrap.mdx @@ -18,9 +18,10 @@ import TokenClientPrerequisites from "/snippets/light-token-guides/light-token-c import AgentSkillGeneric from "/snippets/setup/agent-skill-generic.mdx"; import WrapUnwrapAiPrompt from "/snippets/ai-prompts/ts-cookbook/wrap-unwrap.mdx"; import RustWrapUnwrapAiPrompt from "/snippets/ai-prompts/rust-cookbook/wrap-unwrap.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; -- **Wrap**: Move tokens from SPL/T22 account → Light Token ATA (hot balance) -- **Unwrap**: Move tokens from Light Token ATA (hot balance) → SPL/T22 account +- **Wrap**: Move tokens from SPL/Token 2022 account → Light Token ATA (hot balance) +- **Unwrap**: Move tokens from Light Token ATA (hot balance) → SPL/Token 2022 account Find the source code: @@ -152,3 +153,12 @@ import RustWrapUnwrapAiPrompt from "/snippets/ai-prompts/rust-cookbook/wrap-unwr + +## Related Guides + + + + + + + diff --git a/light-token/defi/programs-pinocchio.mdx b/light-token/defi/programs-pinocchio.mdx index f7689b42..f22b88f1 100644 --- a/light-token/defi/programs-pinocchio.mdx +++ b/light-token/defi/programs-pinocchio.mdx @@ -521,7 +521,5 @@ depending on number and type of accounts being initialized or loaded. --- -API is in Beta and subject to change. - Questions or need hands-on support? [Telegram](https://t.me/swen_light) | [email](mailto:support@lightprotocol.com) | [Discord](https://discord.com/invite/7cJ8BhAXhu) diff --git a/light-token/defi/programs.mdx b/light-token/defi/programs.mdx index 3d1d6bd1..a807d877 100644 --- a/light-token/defi/programs.mdx +++ b/light-token/defi/programs.mdx @@ -552,7 +552,5 @@ depending on number and type of accounts being initialized or loaded. --- -API is in Beta and subject to change. - Questions or need hands-on support? [Telegram](https://t.me/swen_light) | [email](mailto:support@lightprotocol.com) | [Discord](https://discord.com/invite/7cJ8BhAXhu) diff --git a/light-token/defi/routers.mdx b/light-token/defi/routers.mdx index 07ed6902..cdb7ca0c 100644 --- a/light-token/defi/routers.mdx +++ b/light-token/defi/routers.mdx @@ -309,8 +309,6 @@ but requires no streaming setup. --- -API is in Beta and subject to change. - Questions or need hands-on support? [Telegram](https://t.me/swen_light) | [email](mailto:support@lightprotocol.com) | [Discord](https://discord.com/invite/7cJ8BhAXhu) diff --git a/light-token/extensions/overview.mdx b/light-token/extensions/overview.mdx index 6f939c06..35565bdf 100644 --- a/light-token/extensions/overview.mdx +++ b/light-token/extensions/overview.mdx @@ -1,49 +1,29 @@ --- -title: Token Extensions -description: "Token extensions supported by light-token. Includes restricted extensions: supported with constraints (e.g. zero fees, nil transfer hook)." -hidden: true +title: "Token 2022 Extensions with Light Token" +sidebarTitle: "Extensions" +description: "Most Token 2022 extensions are supported by Light Token to add features through extra instructions to a token mint or token account." +keywords: ["token 2022 extensions light token", "token extensions solana", "metadata pointer light token", "transfer fee config light token", "confidential transfers light token"] --- -Light-token supports wrapping and holding balances from Token-2022 mints. The following T22 extensions are supported. Some are **restricted**: the extension is supported only when configured in a specific way (e.g. zero fees, transfer hook disabled). - -### Supported with restrictions - -| Extension | Restriction | -|-----------|-------------| -| TransferFeeConfig | Fees must be zero | -| ConfidentialTransferFeeConfig | Fees must be zero | -| DefaultAccountState | Any state allowed | -| TransferHook | `program_id` must be nil (hook disabled) | -| ConfidentialTransferMint | Initialized but not enabled | -| ConfidentialMintBurn | Initialized but not enabled | - -### Full supported extensions list - -
**SPL token -> Light Token Account****Token 2022 / SPL token -> Light Token Account**
  • Transfers SPL tokens to Light Token accounts
  • -
  • SPL tokens are locked in interface PDA
  • +
  • SPL / Token 2022 tokens are locked in interface PDA
  • Tokens are minted to Light Token account
**Light Token -> SPL Account****Light Token -> SPL / Token 2022 Account**
    -
  • Releases SPL tokens from interface PDA to SPL account
  • +
  • Releases SPL / Token 2022 tokens from interface PDA to SPL account
  • Burns tokens in source Light Token account
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Extension NameRestriction / noteslight-tokenCompressed Token
MetadataPointer-YesYes
TokenMetadata-YesYes
InterestBearingConfig-YesYes
GroupPointer-YesYes
GroupMemberPointer-YesYes
TokenGroup-YesYes
TokenGroupMember-YesYes
MintCloseAuthority-Yes-
TransferFeeConfigFees must be zeroYes-
DefaultAccountStateAny state allowedYes-
PermanentDelegate-Yes-
TransferHookprogram_id must be nilYes-
Pausable-Yes-
ConfidentialTransferMintInitialized but not enabledYes-
ConfidentialTransferFeeConfigFees must be zeroYes-
ConfidentialMintBurnInitialized but not enabledYes-
\ No newline at end of file +import SupportFooter from "/snippets/support-footer.mdx"; +import ExtensionsTable from "/snippets/extensions-table.mdx"; + +## How extensions work with Light Token + +1. **Create** a Token-2022 mint with one or more extensions +2. **[Register an interface PDA](/light-token/cookbook/add-interface-pda)** to hold balances from that mint in Light Token accounts +3. **Use** the same Light Token APIs (`transferInterface`, `wrap`, `unwrap`) as any other token + +Each extension adds specific state that's generally initialized during mint or token account creation. +When initializing either account, you can enable specific extensions simultaneously for different functionality. +Most extensions can't be added after an account is initialized. Use the Solana documentation to learn more about Token 2022 extensions. + +## Supported extensions + + + +## Not supported + +The following Token-2022 extensions are not supported by Light Token: Scaled UI Amount, Non-Transferable Tokens, Memo Transfer, Immutable Owner, CPI Guard. + + diff --git a/light-token/payments/basic-payment.mdx b/light-token/payments/basic-payment.mdx new file mode 100644 index 00000000..9c80bf99 --- /dev/null +++ b/light-token/payments/basic-payment.mdx @@ -0,0 +1,234 @@ +--- +title: "Basic Payment" +sidebarTitle: "Basic Payment" +description: "Send a single token transfer with Light Token APIs for stablecoin payments with comparison to SPL." +keywords: ["stablecoin transfer solana", "light token transfer", "send payment solana", "rent free transfer"] +--- + +import ToolkitsSetup from "/snippets/setup/toolkits-setup.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; +import AgentSkillPayments from "/snippets/setup/agent-skill-payments.mdx"; +import PaymentsAiPrompt from "/snippets/ai-prompts/toolkits/payments.mdx"; + +1. The light-token API matches the SPL-token API almost entirely, and extends their functionality to include the light token program in addition to the SPL-token and Token-2022 programs. +2. Your users use the same stablecoins, just stored more efficiently. + + + + + + + + + + + + + + + + +
SPLLight
**Transfer**createTransferInstruction()createTransferInterfaceInstructions()
+ + + + + + +## Setup + + + +Snippets below assume `rpc`, `payer`, `mint`, `owner`, `recipient`, and `amount` are defined. +See the [full examples](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments) for runnable setup. + +```typescript +import { createRpc } from "@lightprotocol/stateless.js"; + +import { + createTransferInterfaceInstructions, + transferInterface +} from "@lightprotocol/compressed-token/unified"; + +const rpc = createRpc(RPC_ENDPOINT); +``` + + +Find full runnable code examples: +[instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-instruction.ts) | +[action](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-action.ts). + + +## Send a payment + + +The SDK checks whether the sender's account has a cold balance and adds load instructions if needed. + + +**About loading**: Light Token accounts reduce account rent ~200x by auto-compressing inactive +accounts. Before any action, the SDK detects cold balances and adds +instructions to load them. This almost always fits in a single +atomic transaction with your regular transfer. APIs return `TransactionInstruction[][]` so the same +loop handles the rare multi-transaction case automatically. + + + + + +```typescript +import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; +import { createTransferInterfaceInstructions } from "@lightprotocol/compressed-token/unified"; + +// Returns TransactionInstruction[][]. +// Each inner array is one transaction. +// Almost always returns just one. +const instructions = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + amount, + owner.publicKey, + recipient +); + +for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); +} +``` + + + + +```typescript +import { + getAssociatedTokenAddressSync, + createAssociatedTokenAccountIdempotentInstruction, + createTransferInstruction, +} from "@solana/spl-token"; + +const sourceAta = getAssociatedTokenAddressSync(mint, owner.publicKey); +const destinationAta = getAssociatedTokenAddressSync(mint, recipient); + +const tx = new Transaction().add( + createAssociatedTokenAccountIdempotentInstruction( + payer.publicKey, destinationAta, recipient, mint + ), + createTransferInstruction(sourceAta, destinationAta, owner.publicKey, amount) +); +``` + + + +Your app logic may require you to create a single sign request for your user: + + + +```typescript +const transactions = instructions.map((ixs) => new Transaction().add(...ixs)); + +// One approval for all +const signed = await wallet.signAllTransactions(transactions); + +for (const tx of signed) { + await sendAndConfirmTransaction(rpc, tx); +} +``` + + + +While almost always you will have only one transfer transaction, you can +optimize sending in the rare cases where you have multiple transactions. +Parallelize the loads, confirm them, and then send the transfer instruction +after. + + + +```typescript +import { + createTransferInterfaceInstructions, + sliceLast, +} from "@lightprotocol/compressed-token/unified"; + +const instructions = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + amount, + owner.publicKey, + recipient +); +const { rest: loadInstructions, last: transferInstructions } = sliceLast(instructions); +// empty = nothing to load, will no-op. +await Promise.all( + loadInstructions.map((ixs) => { + const tx = new Transaction().add(...ixs); + tx.sign(payer, owner); + return sendAndConfirmTransaction(rpc, tx); + }) +); + +const transferTx = new Transaction().add(...transferInstructions); +transferTx.sign(payer, owner); +await sendAndConfirmTransaction(rpc, transferTx); +``` + + + + + + +```typescript +import { + getAssociatedTokenAddressInterface, + transferInterface, +} from "@lightprotocol/compressed-token/unified"; + +const sourceAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); + +// Handles loading, creates recipient associated token account, transfers. +await transferInterface(rpc, payer, sourceAta, mint, recipient, owner, amount); +``` + + + +```typescript +import { + getAssociatedTokenAddressSync, + getOrCreateAssociatedTokenAccount, + transfer, +} from "@solana/spl-token"; + +await getOrCreateAssociatedTokenAccount(connection, payer, mint, recipient); + +const sourceAta = getAssociatedTokenAddressSync(mint, owner.publicKey); +const destinationAta = getAssociatedTokenAddressSync(mint, recipient); +await transfer(connection, payer, sourceAta, destinationAta, owner, amount); +``` + + + + + + + + + + + + +## Related guides + + + + Pack multiple transfers to multiple recipients into one transaction. + + + Abstract SOL fees from the user. Sponsor top-ups and transaction fees. + + + Unify token balances and share ATA address with the sender. + + + + diff --git a/light-token/payments/batch-payments.mdx b/light-token/payments/batch-payments.mdx new file mode 100644 index 00000000..beea2a3a --- /dev/null +++ b/light-token/payments/batch-payments.mdx @@ -0,0 +1,218 @@ +--- +title: "Batch Payments" +sidebarTitle: "Batch Payments" +description: "Send payments to multiple recipients in a single transaction or sequentially." +keywords: ["batch payments solana", "multiple transfers solana", "light token batch", "multi-recipient transfer"] +--- + +import ToolkitsSetup from "/snippets/setup/toolkits-setup.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; +import AgentSkillPayments from "/snippets/setup/agent-skill-payments.mdx"; +import PaymentsAiPrompt from "/snippets/ai-prompts/toolkits/payments.mdx"; + + + + + + + + + + + + + + + + +
SPLLight
**Transfer**createTransferInstruction()createTransferInterfaceInstructions()
+ + + + + + +## Setup + + + +```typescript +import { createRpc } from "@lightprotocol/stateless.js"; + +const rpc = createRpc(RPC_ENDPOINT); +``` + + +Find full code examples: +[single transaction](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/batch-send-single-tx.ts) | +[sequential](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/batch-send.ts). + + +## Send to multiple recipients in one transaction + +Load all accounts first, then batch the transfers into a single atomic transaction. + +```typescript +import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; +import { + createLoadAtaInstructions, + createTransferInterfaceInstructions, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token/unified"; + +const recipients = [ + { address: recipient1, amount: 100 }, + { address: recipient2, amount: 200 }, + { address: recipient3, amount: 300 }, +]; + +// 1. Load sender's cold balance (if needed) +const senderAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); +const loadSenderIxs = await createLoadAtaInstructions( + rpc, senderAta, owner.publicKey, mint, payer.publicKey +); +for (const ixs of loadSenderIxs) { + await sendAndConfirmTransaction(rpc, new Transaction().add(...ixs), [payer]); +} + +// 2. Load/create each recipient's associated token account +for (const { address } of recipients) { + const recipientAta = getAssociatedTokenAddressInterface(mint, address); + const loadIxs = await createLoadAtaInstructions( + rpc, recipientAta, address, mint, payer.publicKey + ); + for (const ixs of loadIxs) { + await sendAndConfirmTransaction(rpc, new Transaction().add(...ixs), [payer]); + } +} + +// 3. All accounts are hot — batch transfers in one transaction +const COMPUTE_BUDGET_ID = "ComputeBudget111111111111111111111111111111"; +const allTransferIxs = []; +let isFirst = true; + +for (const { address, amount } of recipients) { + const ixs = await createTransferInterfaceInstructions( + rpc, payer.publicKey, mint, amount, owner.publicKey, address + ); + // Filter duplicate ComputeBudget instructions from subsequent calls + for (const ix of ixs[0]) { + if (!isFirst && ix.programId.toBase58() === COMPUTE_BUDGET_ID) continue; + allTransferIxs.push(ix); + } + isFirst = false; +} + +const batchTx = new Transaction().add(...allTransferIxs); +await sendAndConfirmTransaction(rpc, batchTx, [payer, owner]); +``` + +## Send to multiple recipients sequentially + +When you cannot pre-load accounts or want simpler code, process each recipient independently. +Cold accounts are loaded automatically. + +```typescript +const recipients = [ + { address: recipientA, amount: 100 }, + { address: recipientB, amount: 200 }, + { address: recipientC, amount: 300 }, +]; + +for (const { address, amount } of recipients) { + const instructions = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + amount, + owner.publicKey, + address + ); + + for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); + } +} +``` + +## Advanced patterns + + + +Every Light Token API that modifies state returns `TransactionInstruction[][]`. +Each inner array is one atomic transaction. Almost always a single transaction, +but the loop pattern handles the rare multi-transaction case (cold account loading) automatically. + +```typescript +const instructions = await createTransferInterfaceInstructions( + rpc, payer.publicKey, mint, amount, owner.publicKey, recipient +); + +for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); +} +``` + + + + + +When the SDK returns multiple transaction batches (load + transfer), parallelize +the loads and send the transfer last. + +```typescript +import { sliceLast } from "@lightprotocol/compressed-token/unified"; + +const instructions = await createTransferInterfaceInstructions( + rpc, payer.publicKey, mint, amount, owner.publicKey, recipient +); + +const { rest: loadInstructions, last: transferInstructions } = sliceLast(instructions); + +await Promise.all( + loadInstructions.map(async (ixs) => { + const tx = new Transaction().add(...ixs); + return sendAndConfirmTransaction(rpc, tx, [payer, owner]); + }) +); + +const transferTx = new Transaction().add(...transferInstructions); +await sendAndConfirmTransaction(rpc, transferTx, [payer, owner]); +``` + + + + + +For wallet integrations (Privy, Wallet Adapter), use a helper that handles +blockhash, signing, sending, and confirming for each batch. + +See the complete implementation in the +[Privy integration](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/sign-with-privy/react/src/hooks/signAndSendBatches.ts) and +[Wallet Adapter integration](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-wallet-adapter) examples. + + + + + + + + + +## Related guides + + + + Send a single transfer. + + + Separate the fee payer from the token owner. + + + Load cold accounts and share ATA address with the sender. + + + + \ No newline at end of file diff --git a/light-token/payments/integration-guide.mdx b/light-token/payments/integration-guide.mdx index 00553c51..b217aa36 100644 --- a/light-token/payments/integration-guide.mdx +++ b/light-token/payments/integration-guide.mdx @@ -65,7 +65,7 @@ import SupportFooter from "/snippets/support-footer.mdx"; Find full runnable code examples - [here](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets). + [here](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments). @@ -78,7 +78,7 @@ import SupportFooter from "/snippets/support-footer.mdx"; Snippets below assume `rpc`, `payer`, `mint`, `owner`, `recipient`, and `amount` are defined. -See the [full examples](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets) for runnable setup. +See the [full examples](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments) for runnable setup. ```typescript import { createRpc } from "@lightprotocol/stateless.js"; @@ -101,7 +101,7 @@ const rpc = createRpc(RPC_ENDPOINT); ## Receive Payments -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/receive.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/receive/receive.ts). Load creates the associated token account (ATA) if needed and loads any compressed state into it. Share the ATA address with the sender. @@ -188,7 +188,7 @@ const ata = await getOrCreateAssociatedTokenAccount( ## Send Payments -Find a full code example: [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/send-instruction.ts) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/send-action.ts). +Find a full code example: [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-instruction.ts) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-action.ts). @@ -332,7 +332,7 @@ await transfer(connection, payer, sourceAta, destinationAta, owner, amount); ## Show Balance -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/get-balance.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-balance.ts). ```typescript @@ -362,7 +362,7 @@ console.log(account.amount); ## Transaction History -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/get-history.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-history.ts). ```typescript @@ -388,7 +388,7 @@ const signatures = await connection.getSignaturesForAddress(ata); Wrap tokens from SPL/Token-2022 accounts to light-token ATA. -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/wrap.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/interop/wrap.ts). @@ -448,7 +448,7 @@ Unwrap moves the token balance from a light-token account to a SPL-token account Use this to compose with applications that do not yet support light-token. -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/unwrap.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/interop/unwrap.ts). @@ -502,7 +502,7 @@ await unwrap(rpc, payer, splAta, owner, mint, amount); ## Related Guides - + Abstract fees entirely so users never interact with SOL. diff --git a/light-token/payments/overview.mdx b/light-token/payments/overview.mdx index 620fce31..cdf5ae41 100644 --- a/light-token/payments/overview.mdx +++ b/light-token/payments/overview.mdx @@ -46,9 +46,8 @@ The token standard pays rent-exemption cost for you. To prevent griefing, “ren
-You can abstract fees entirely so users never interact with SOL. -- See [fee abstraction](https://solana.com/docs/payments/send-payments/payment-processing/fee-abstraction) for transaction fees. -- See [sponsor rent top-ups](/light-token/wallets/sponsor-top-ups). +You can abstract fees entirely so users never interact with SOL. See [gasless transactions](/light-token/wallets/gasless-transactions) +to sponsor rent top-ups and transaction fees. ## API Comparison @@ -62,60 +61,64 @@ For example, here is the creation of an associated token account: secondLabel="SPL" /> +## Interoperability + + +The Light Token APIs cover every SPL Token operation and adds extra capabilities: + + + + + + + + + + + + + + + + + + + + + + +
CategoryWhat it does
**Cross-program dispatch**`Interface` methods (e.g., `transferInterface`) auto-detect the token program and dispatch to SPL, Token 2022, or Light Token.
**Unified balance**`getAtaInterface` returns a unified balance for a given mint, aggregating Light Token (hot + cold), SPL, and Token 2022 sources.
**Wrap / Unwrap**For interoperability with applications that don't support Light Token yet, you can wrap / unwrap SPL or Token 2022 tokens into Light Token associated token accounts and back.
+ ## Token Extensions Token extensions introduce optional features you can add to a token mint or token account through -extra instructions. - - - -| Extension | Description | -|-----------|-------------| -| MetadataPointer | Points a mint to the account that stores its metadata. | -| TokenMetadata | Stores token name, symbol, and URI directly on the mint. | -| InterestBearingConfig | Displays a UI-adjusted balance that accrues interest over time. | -| GroupPointer | Points a mint to the account that stores group configuration. | -| GroupMemberPointer | Points a mint to the account that stores group member configuration. | -| TokenGroup | Stores group configuration directly on the mint (e.g., NFT collections). | -| TokenGroupMember | Stores group member configuration directly on the mint. | -| MintCloseAuthority | Allows a designated authority to close a mint account. | -| TransferFeeConfig | Withholds a percentage of each transfer as a fee. | -| DefaultAccountState | Sets the initial state (e.g., frozen) for newly created token accounts. | -| PermanentDelegate | Grants an authority unrestricted transfer and burn rights over all accounts for the mint. | -| TransferHook | Invokes a custom program on every transfer via CPI. | -| Pausable | Allows an authority to pause all minting, burning, and transfers. | -| ConfidentialTransferMint | Configures auditor keys for confidential (encrypted) transfers. | -| ConfidentialTransferFeeConfig | Encrypts withheld transfer fees under an auditor's public key. | -| ConfidentialMintBurn | Allows minting and burning of tokens with encrypted amounts. | +extra instructions. Light Token supports most Token 2022 extensions. - -Learn more on extensions here: [Extensions](https://solana.com/docs/tokens/extensions). - - + ## Start Integrating - - - +### Payment Flows + + + + + + + + + + + + +### Fee Abstraction + + +### Signing + + +### Go to Production + diff --git a/light-token/payments/payment-with-memo.mdx b/light-token/payments/payment-with-memo.mdx new file mode 100644 index 00000000..d543f4f1 --- /dev/null +++ b/light-token/payments/payment-with-memo.mdx @@ -0,0 +1,134 @@ +--- +title: "Payment with Memo" +sidebarTitle: "Payment with Memo" +description: "Attach invoice IDs, payment references, or notes to Light Token transfers using Solana's memo program. The memo is recorded in the transaction logs for reconciliation." +keywords: ["memo payment solana", "invoice reconciliation solana", "payment reference light token", "memo instruction"] +--- + +import ToolkitsSetup from "/snippets/setup/toolkits-setup.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; +import AgentSkillPayments from "/snippets/setup/agent-skill-payments.mdx"; +import PaymentsAiPrompt from "/snippets/ai-prompts/toolkits/payments.mdx"; + + + + + + + + + + + + + + + + +
SPLLight
**Transfer**createTransferInstruction()createTransferInterfaceInstructions()
+ + + + + + + +## How it works + +1. Create the transfer instructions with `createTransferInterfaceInstructions()`. +2. Add a memo instruction to the transfer transaction (the last batch). +3. Both instructions execute atomically in the same transaction. + +## Setup + + + +Additionally install the memo program package: + +```bash +npm install @solana/spl-memo +``` + +```typescript +import { createRpc } from "@lightprotocol/stateless.js"; + +const rpc = createRpc(RPC_ENDPOINT); +``` + +## Send a payment with memo + +```typescript +import { Transaction, sendAndConfirmTransaction, TransactionInstruction, PublicKey } from "@solana/web3.js"; +import { + createTransferInterfaceInstructions, + sliceLast, +} from "@lightprotocol/compressed-token/unified"; + +const MEMO_PROGRAM_ID = new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"); + +const instructions = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + amount, + owner.publicKey, + recipient +); + +const { rest: loadInstructions, last: transferInstructions } = sliceLast(instructions); + +// Send load transactions first (if any) +for (const ixs of loadInstructions) { + const tx = new Transaction().add(...ixs); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); +} + +// Add memo to the transfer transaction +const memoIx = new TransactionInstruction({ + keys: [], + programId: MEMO_PROGRAM_ID, + data: Buffer.from("INV-2024-001"), +}); + +const transferTx = new Transaction().add(...transferInstructions, memoIx); +await sendAndConfirmTransaction(rpc, transferTx, [payer, owner]); +``` + +## Read memo from transaction logs + +After the transaction confirms, the memo appears in the transaction logs: + +```typescript +const txDetails = await rpc.getTransaction(signature, { + maxSupportedTransactionVersion: 0, +}); + +// Look for memo program log entries +const logs = txDetails?.meta?.logMessages || []; +const memoLogs = logs.filter((log) => + log.includes("Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr") +); +console.log("Memo logs:", memoLogs); +``` + + + + + + + +## Related guides + + + + Send a single transfer without memo. + + + Query balances and transaction history. + + + Pack multiple transfers into one transaction. + + + + \ No newline at end of file diff --git a/light-token/payments/production-readiness.mdx b/light-token/payments/production-readiness.mdx new file mode 100644 index 00000000..e83c30cf --- /dev/null +++ b/light-token/payments/production-readiness.mdx @@ -0,0 +1,73 @@ +--- +title: "Production Readiness" +sidebarTitle: "Production Readiness" +description: "Non-exhaustive checklist for deploying Light Token payment flows to production, including RPC infrastructure, error handling, and security." +keywords: ["production readiness solana", "light token production", "photon indexer", "mainnet deployment solana"] +--- + +import SupportFooter from "/snippets/support-footer.mdx"; +import AgentSkillPayments from "/snippets/setup/agent-skill-payments.mdx"; +import PaymentsAiPrompt from "/snippets/ai-prompts/toolkits/payments.mdx"; + + + + + + +## RPC infrastructure + +Light Token requires a Photon-compatible RPC endpoint for cold account lookups and balance queries. + +| Provider | Photon support | Notes | +|:---------|:---------------|:------| +| [Helius](https://helius.dev) | Yes | Recommended. Maintains the Photon indexer. | +| [Triton](https://triton.one) | Yes | Alternative provider. | + +## Transaction landing + +Light Token transactions follow standard Solana patterns for landing: + +- **Priority fees**: Use `ComputeBudgetProgram.setComputeUnitPrice()` to increase transaction priority. See [Solana priority fees](https://solana.com/docs/core/fees). +- **Compute units**: Set appropriate compute unit limits with `ComputeBudgetProgram.setComputeUnitLimit()`. +- **Retry logic**: Implement retries with fresh blockhashes for failed transactions. + +## Confirmation levels + +| Level | When to use | +|:------|:------------| +| `processed` | Fastest. Use for UI updates. Not guaranteed to finalize. | +| `confirmed` | Recommended for most payment flows. Confirmed by supermajority of validators. | +| `finalized` | Highest certainty. Use for high-value transfers or irreversible actions. | + +## Error handling + +Handle both standard Solana errors and Light Token-specific scenarios: + +- **[Cold account](/light-token/payments/receive-payments) load failures**: Retry the load if the indexer returns stale data. +- **Associated token account creation**: Idempotent — safe to retry. +- **Rent top-up failures**: Ensure the fee payer has sufficient SOL balance. See [gasless transactions](/light-token/wallets/gasless-transactions) for cost breakdown. +- **Transaction size limits**: If `TransactionInstruction[][]` returns [multiple batches](/light-token/payments/basic-payment), process them sequentially. + +## Fee sponsorship + +Set your application as the fee payer so users never interact with SOL: + +1. **Rent top-ups and transaction fees**: Set `payer` parameter on Light Token instructions. See [gasless transactions](/light-token/wallets/gasless-transactions). +2. **Rent sponsor funding**: Ensure the rent sponsor PDA is funded. See the [cost breakdown](/light-token/wallets/gasless-transactions) for required amounts. + +## Pre-launch checklist + +- [ ] Photon-compatible RPC endpoint configured and tested +- [ ] Fee payer wallet funded with sufficient SOL +- [ ] Error handling covers load failures, tx size limits, and retries +- [ ] Confirmation level appropriate for your use case +- [ ] [Address verification](/light-token/payments/verify-recipient-address) implemented +- [ ] [Wrap/unwrap](/light-token/payments/wrap-unwrap) flows tested for SPL / Token 2022 interoperability (if applicable) + + + + + + + + \ No newline at end of file diff --git a/light-token/payments/receive-payments.mdx b/light-token/payments/receive-payments.mdx new file mode 100644 index 00000000..35e9db10 --- /dev/null +++ b/light-token/payments/receive-payments.mdx @@ -0,0 +1,148 @@ +--- +title: "Receive Payments" +sidebarTitle: "Receive Payments" +description: "Prepare to receive token payments by loading cold accounts and sharing your associated token account address." +keywords: ["receive stablecoin solana", "light token receive", "load ata solana", "cold account loading"] +--- + +import ToolkitsSetup from "/snippets/setup/toolkits-setup.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; +import AgentSkillPayments from "/snippets/setup/agent-skill-payments.mdx"; +import PaymentsAiPrompt from "/snippets/ai-prompts/toolkits/payments.mdx"; + +1. Share the associated token account address with the sender. +2. We recommend to prepend a load instruction before. +Load creates the associated token account if needed and loads any cold balance into it. + + +**About loading**: Light Token accounts reduce account rent ~200x by auto-compressing inactive +accounts. Before any action, the SDK detects cold balances and adds +instructions to load them. This almost always fits in a single +atomic transaction with your regular transfer. APIs return `TransactionInstruction[][]` so the same +loop handles the rare multi-transaction case automatically. + + + + + + + + + +## Setup + + + +```typescript +import { createRpc } from "@lightprotocol/stateless.js"; + +const rpc = createRpc(RPC_ENDPOINT); +``` + +## Derive the associated token account address + +Share this address with the sender so they know where to send tokens. + +```typescript +import { getAssociatedTokenAddressInterface } from "@lightprotocol/compressed-token/unified"; + +const ata = getAssociatedTokenAddressInterface(mint, recipient); +``` + +## Load the associated token account + +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/receive/receive.ts). + + + + + +```typescript +import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; +import { + createLoadAtaInstructions, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token/unified"; + +const ata = getAssociatedTokenAddressInterface(mint, recipient); + +// Returns TransactionInstruction[][]. +// Each inner array is one transaction. +// Almost always returns just one. +const instructions = await createLoadAtaInstructions( + rpc, + ata, + recipient, + mint, + payer.publicKey +); + +for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + await sendAndConfirmTransaction(rpc, tx, [payer]); +} +``` + + + + +```typescript +import { + loadAta, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token/unified"; + +const ata = getAssociatedTokenAddressInterface(mint, recipient); + +const sig = await loadAta(rpc, ata, recipient, mint, payer); +if (sig) console.log("Loaded:", sig); +``` + + + + + + +```typescript +import { + createAssociatedTokenAccountInstruction, + getAssociatedTokenAddressSync, + getOrCreateAssociatedTokenAccount, +} from "@solana/spl-token"; + +const ata = getAssociatedTokenAddressSync(mint, recipient); + +// Instruction: +const tx = new Transaction().add( + createAssociatedTokenAccountInstruction(payer.publicKey, ata, recipient, mint) +); + +// Action: +const ata = await getOrCreateAssociatedTokenAccount( + connection, payer, mint, recipient +); +``` + + + + + + + + + +## Related guides + + + + Check balances and transaction history. + + + Abstract SOL fees so users never hold SOL. + + + Send a single token transfer. + + + + \ No newline at end of file diff --git a/light-token/payments/spend-permissions.mdx b/light-token/payments/spend-permissions.mdx new file mode 100644 index 00000000..34d2dc23 --- /dev/null +++ b/light-token/payments/spend-permissions.mdx @@ -0,0 +1,194 @@ +--- +title: "Spend Permissions via Delegation" +sidebarTitle: "Spend Permissions" +description: "Delegate token spending to a third party with an amount cap. The delegate can transfer tokens on behalf of the owner up to the approved amount, without the owner signing each transaction." +keywords: ["delegate tokens solana", "token delegation light token", "spend permissions solana", "recurring payments solana"] +--- + +import ToolkitsSetup from "/snippets/setup/toolkits-setup.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; +import AgentSkillPayments from "/snippets/setup/agent-skill-payments.mdx"; +import PaymentsAiPrompt from "/snippets/ai-prompts/toolkits/payments.mdx"; + +--- + +Delegation with Light Token works similar to SPL. +When you approve a delegate, you're authorizing a specific account to transfer tokens on your behalf: + +- **Owner retains custody**: You still own the tokens and can transfer or revoke at any time. Delegation is non-custodial. +- **Capped spending**: The delegate can spend tokens up to the limit, but cannot access or drain the account beyond the approved amount. +- **Single delegate per account**: Each token account can only have one active delegate. The owner can revoke at any time. +- **New approval replaces old**: Approving a new delegate automatically revokes the previous one + + + + + + + + + + + + + + + + + + + + + + + + + + +
SPLLight
**Approve**approve()approveInterface()
**Delegated Transfer**transfer() (delegate signs)transferInterface({ owner })
**Revoke**revoke()revokeInterface()
+ + + + + + +## Use cases + +| Use case | How delegation helps | +|:---------|:--------------------| +| **Subscriptions** | Approve a monthly cap. The service provider transfers the fee each period. | +| **Recurring payments** | Approve a spending limit. The payment processor draws funds as needed. | +| **Managed spending** | A parent or admin approves a cap for a sub-account. | +| **Agent wallets** | An AI agent operates within a delegated spending limit. | + +## Setup + + + +```typescript +import { createRpc } from "@lightprotocol/stateless.js"; + +const rpc = createRpc(RPC_ENDPOINT); +``` + +## Approve a delegate + +Grant a delegate permission to spend up to a capped amount: + +```typescript +import { approveInterface } from "@lightprotocol/compressed-token/unified"; + +const tx = await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, // who gets permission + 500_000, // amount cap + owner // token owner (signs) +); + +console.log("Approved:", tx); +``` + +## Check delegation status + +Check the delegation status of an account: + +```typescript +import { getAtaInterface } from "@lightprotocol/compressed-token"; + +const account = await getAtaInterface(rpc, senderAta, owner.publicKey, mint); + +console.log("Delegate:", account.parsed.delegate?.toBase58() ?? "none"); +console.log("Delegated amount:", account.parsed.delegatedAmount.toString()); +``` + +## Transfer as Delegate + +Once approved, the delegate can transfer tokens on behalf of the owner. The delegate is the transaction authority. Only the delegate and fee payer sign; the owner's signature is not required. + +`transferInterface` takes a recipient wallet address and creates the recipient's associated token account internally. Pass `{ owner }` to transfer as a delegate instead of the owner. + +```typescript +import { transferInterface } from "@lightprotocol/compressed-token/unified"; + +const tx = await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, // recipient wallet (associated token account created internally) + delegate, // delegate authority (signer) + 200_000, // must be within approved cap + undefined, + undefined, + { owner: owner.publicKey } // owner (does not sign) +); + +console.log("Delegated transfer:", tx); +``` + + + +`createTransferInterfaceInstructions` returns `TransactionInstruction[][]` for manual transaction control. Pass `owner` to transfer as a delegate. + +```typescript +import { + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { createTransferInterfaceInstructions } from "@lightprotocol/compressed-token/unified"; + +const instructions = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + 200_000, + delegate.publicKey, + recipient.publicKey, + 9, // decimals + { owner: owner.publicKey } +); + +for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + await sendAndConfirmTransaction(rpc, tx, [payer, delegate]); +} +``` + + + +## Revoke a delegate + +Remove spending permission: + +```typescript +import { revokeInterface } from "@lightprotocol/compressed-token/unified"; + +const tx = await revokeInterface(rpc, payer, senderAta, mint, owner); + +console.log("Revoked:", tx); +``` + + + + + + + +## Related guides + + + + Send a single token transfer. + + + Separate the fee payer from the token owner. + + + Load cold accounts and share ATA address with the sender. + + + + \ No newline at end of file diff --git a/light-token/payments/verify-payments.mdx b/light-token/payments/verify-payments.mdx new file mode 100644 index 00000000..a98bf477 --- /dev/null +++ b/light-token/payments/verify-payments.mdx @@ -0,0 +1,128 @@ +--- +title: "Verify Payments" +sidebarTitle: "Get Token Balance and Transaction History" +description: "Query token balances and transaction history to verify incoming payments." +keywords: ["verify payment solana", "token balance solana", "transaction history light token", "payment verification"] +--- + +import ToolkitsSetup from "/snippets/setup/toolkits-setup.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; +import AgentSkillPayments from "/snippets/setup/agent-skill-payments.mdx"; +import PaymentsAiPrompt from "/snippets/ai-prompts/toolkits/payments.mdx"; + + + + + + + + + + + + + + + + + + + + + +
SPLLight
**Get Balance**getAccount()getAtaInterface()
**Tx History**getSignaturesForAddress()rpc.getSignaturesForOwnerInterface()
+ + + + + + +## Setup + + + +```typescript +import { createRpc } from "@lightprotocol/stateless.js"; + +const rpc = createRpc(RPC_ENDPOINT); +``` + +## Query balance + +`getAtaInterface` returns a unified balance aggregating Light Token (hot + cold), SPL, and Token 2022 sources in `parsed.amount`. + + +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-balance.ts). + + +```typescript +import { + getAssociatedTokenAddressInterface, + getAtaInterface, +} from "@lightprotocol/compressed-token/unified"; + +const ata = getAssociatedTokenAddressInterface(mint, owner); +const account = await getAtaInterface(rpc, ata, owner, mint); + +console.log(account.parsed.amount); // Unified balance +``` + + + +```typescript +import { getAccount } from "@solana/spl-token"; + +const account = await getAccount(connection, ata); + +console.log(account.amount); +``` + + + +## Transaction history + + +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-history.ts). + + +Query all transactions for an owner across both on-chain and compressed state: + +```typescript +const result = await rpc.getSignaturesForOwnerInterface(owner); + +console.log(result.signatures); // Merged + deduplicated +console.log(result.solana); // On-chain transactions only +console.log(result.compressed); // Compressed transactions only +``` + +Use `getSignaturesForAddressInterface(address)` if you want address-specific rather than owner-wide history. + + + +```typescript +const signatures = await connection.getSignaturesForAddress(ata); +``` + + + + + + + + + +## Related guides + + + + Load cold accounts and prepare to receive. + + + Send a single token transfer. + + + Validate recipient addresses before sending. + + + + \ No newline at end of file diff --git a/light-token/payments/verify-recipient-address.mdx b/light-token/payments/verify-recipient-address.mdx new file mode 100644 index 00000000..fdde7e32 --- /dev/null +++ b/light-token/payments/verify-recipient-address.mdx @@ -0,0 +1,101 @@ +--- +title: "Verify Address" +sidebarTitle: "Verify Recipients Address" +description: "Verify recipient addresses before sending payments. Address validation prevents sending tokens to invalid or unexpected account types." +keywords: ["verify address solana", "address validation light token", "solana address check", "payment safety"] +--- + +import ToolkitsSetup from "/snippets/setup/toolkits-setup.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; +import AgentSkillPayments from "/snippets/setup/agent-skill-payments.mdx"; +import PaymentsAiPrompt from "/snippets/ai-prompts/toolkits/payments.mdx"; + + + + + + + + + + + + + + + + + + +
SPLLight
**Get Account**getAccount()getAtaInterface()
+ + + + + + +## Understanding Solana addresses + +Solana has two types of addresses: + +- **On-curve addresses** (wallets): derived from a keypair, can sign transactions. +- **Off-curve addresses** (PDAs): program-derived addresses, can only sign via CPI. + +## Verification flow + +Before sending a payment: + +1. Check if the address is on-curve (wallet) or off-curve (PDA). +2. If the address has an on-chain account, check the account owner and type. +3. For Light Token, derive the associated token account to verify it matches the expected mint. + +```typescript +import { + getAssociatedTokenAddressInterface, + getAtaInterface, +} from "@lightprotocol/compressed-token/unified"; + +// Derive the expected associated token account for this recipient and mint +const expectedAta = getAssociatedTokenAddressInterface(mint, recipientWallet); + +// Check if the account exists and is active +try { + const account = await getAtaInterface(rpc, expectedAta, recipientWallet, mint); + console.log("Account exists, balance:", account.parsed.amount); +} catch { + console.log("Account does not exist yet — will be created on first transfer"); +} +``` + + +Use `getAssociatedTokenAddressInterface()` instead of SPL's `getAssociatedTokenAddressSync()` to derive +Light Token associated token account addresses. The derivation uses the Light Token Program ID. + + + +When you send a payment with `transferInterface()` or `createTransferInterfaceInstructions()`, +the SDK automatically creates the recipient's associated token account if it doesn't exist. +Address verification is a safety check, not a prerequisite. + + + + + + + + +## Related guides + + + + Send a single token transfer. + + + Load cold accounts and prepare to receive. + + + Query balances and transaction history. + + + + \ No newline at end of file diff --git a/light-token/payments/wrap-unwrap.mdx b/light-token/payments/wrap-unwrap.mdx new file mode 100644 index 00000000..9289fa22 --- /dev/null +++ b/light-token/payments/wrap-unwrap.mdx @@ -0,0 +1,182 @@ +--- +title: "Wrap and Unwrap" +sidebarTitle: "Wrap and Unwrap" +description: "Move tokens between SPL / Token 2022 and Light Token accounts for interoperability with applications that don't support Light Token yet." +keywords: ["wrap tokens solana", "unwrap tokens solana", "spl to light token", "cex onramp solana", "token interoperability"] +--- + +import RegisterSplMint from "/snippets/setup/register-spl-mint.mdx"; +import ToolkitsSetup from "/snippets/setup/toolkits-setup.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; +import AgentSkillPayments from "/snippets/setup/agent-skill-payments.mdx"; +import PaymentsAiPrompt from "/snippets/ai-prompts/toolkits/payments.mdx"; + +Wrap moves tokens from an SPL or Token 2022 account into a Light Token associated token account. +Unwrap moves tokens back. Use wrap/unwrap to interoperate with applications that only support +SPL or Token 2022, such as centralized exchanges or DeFi. + + + + + + + + + + + + + + + + + + + + + +
SPLLight
**Wrap from SPL**N/AcreateWrapInstruction()
**Unwrap to SPL**N/AcreateUnwrapInstructions() / unwrap()
+ + + + + + +## Setup + + + +```typescript +import { createRpc } from "@lightprotocol/stateless.js"; + +const rpc = createRpc(RPC_ENDPOINT); +``` + +## Wrap from SPL + + +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/interop/wrap.ts). + + + + + +```typescript +import { Transaction } from "@solana/web3.js"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { + createWrapInstruction, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token/unified"; +import { getSplInterfaceInfos } from "@lightprotocol/compressed-token"; + +const splAta = getAssociatedTokenAddressSync(mint, owner.publicKey); +const tokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); + +const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint); +const splInterfaceInfo = splInterfaceInfos.find((i) => i.isInitialized); + +const tx = new Transaction().add( + createWrapInstruction( + splAta, + tokenAta, + owner.publicKey, + mint, + amount, + splInterfaceInfo, + decimals, + payer.publicKey + ) +); +``` + + + + +```typescript +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { + wrap, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token/unified"; + +const splAta = getAssociatedTokenAddressSync(mint, owner.publicKey); +const tokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); + +await wrap(rpc, payer, splAta, tokenAta, owner, mint, amount); +``` + + + + +## Unwrap to SPL + + +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/interop/unwrap.ts). + + +Unwrap moves the token balance from a Light Token associated token account to an SPL associated token account. +Use this to compose with applications that do not yet support Light Token. + + + + +```typescript +import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { createUnwrapInstructions } from "@lightprotocol/compressed-token/unified"; + +const splAta = getAssociatedTokenAddressSync(mint, owner.publicKey); + +// Each inner array = one transaction. Handles loading + unwrapping together. +const instructions = await createUnwrapInstructions( + rpc, + splAta, + owner.publicKey, + mint, + amount, + payer.publicKey +); + +for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); +} +``` + + + + +```typescript +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { unwrap } from "@lightprotocol/compressed-token/unified"; + +const splAta = getAssociatedTokenAddressSync(mint, owner.publicKey); + +await unwrap(rpc, payer, splAta, owner, mint, amount); +``` + + + + + + + + + + +## Related guides + + + + Send a single token transfer. + + + Load cold accounts and prepare to receive. + + + Abstract SOL fees from the user. + + + + \ No newline at end of file diff --git a/light-token/wallets/gasless-transactions.mdx b/light-token/wallets/gasless-transactions.mdx new file mode 100644 index 00000000..d898332b --- /dev/null +++ b/light-token/wallets/gasless-transactions.mdx @@ -0,0 +1,221 @@ +--- +title: Gasless Transactions +sidebarTitle: Gasless transactions +description: Abstract SOL fees so users never hold SOL. The Light SDK sponsors rent-exemption for Solana accounts and keeps accounts active through periodic top-ups paid by the fee payer. Sponsor top-ups and transaction fees by setting your application as the fee payer. +keywords: ["gasless transactions solana", "fee abstraction solana", "rent sponsorship light token", "gasless solana tokens", "rent free tokens on solana"] +--- + +import TransferInstructionCode from "/snippets/code-snippets/light-token/gasless-transactions/ts-instruction.mdx"; +import RustInstructionCode from "/snippets/code-snippets/light-token/gasless-transactions/rust-instruction.mdx"; +import RentSponsorshipExplained from "/snippets/rent-sponsorship-explained.mdx"; +import GaslessTransactionsAiPrompt from "/snippets/ai-prompts/wallets/gasless-transactions.mdx"; +import SupportFooter from "/snippets/support-footer.mdx"; + +--- + +## Sponsor Top-ups and Transaction Fees + + +Rent sponsorship is a built-in feature of the Light SDK’s that sponsors rent-exemption. +This is dealt with under the hood in a way that doesn’t disrupt the UX of what your users are used to with SPL-token. + + + +Solana transactions have a designated fee payer, the account that pays the +network fee. By default, this is the first signer. You can specify a +different account as the fee payer, allowing a third party (the "sponsor") to +cover fees on behalf of the sender. + +Light Token extends this: The `payer` parameter on any Light Token +instruction determines who pays rent top-ups in addition to transaction fees. +Set your application as the payer so users never interact with SOL. + +In total, your sponsor covers three costs: + +| | | | +|:-----|:-------|:----| +| **Account creation** | 11,000 lamports | Initial bump on virtual rent balance. Rent-exemption is sponsored. | +| **Rent top-ups** | ~766 lamports per write | Fee payer bumps the virtual rent balance on each write to keep accounts active. Set `payer` parameter on any Light Token instruction. | +| **Transaction fees**
+ Priority Fee | 5,000 lamports
+ Priority Fee per tx | Standard Solana fee payer. Set `feePayer` on the transaction. | + + +| | Light | SPL / Token 2022 | +| :--- | :--- | :--- | +| **Transfer** | *~\$0.001* | *~\$0.001* | +| **Create Token Account** | **~\$0.001** (0.00001 SOL) | **~\$0.30** (0.0029 SOL) | +| **Transfer + Create Token Account** | **~\$0.001** (0.00001 SOL) | **~\$0.30** (0.0029 SOL) | + + + +## Send a gasless transfer + + +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/gasless-transactions/typescript/gasless-transfer.ts). + + + + + + + + + +Generate or load a keypair for the sponsor who will pay transaction fees and rent top-ups. +The sponsor needs SOL but doesn't need to hold the tokens being transferred. + +```typescript +import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; + +const rpc = createRpc(RPC_ENDPOINT); + +// Sponsor: your application server +const sponsor = Keypair.fromSecretKey(/* your server keypair */); + +// User: only signs to authorize the transfer +const sender = Keypair.fromSecretKey(/* user's keypair */); +``` + + + + + +Create the transfer instruction with the sponsor as `payer` and the sender as `authority`. +The sender owns the tokens and must sign the transfer. + +```typescript +import { createTransferInterfaceInstructions } from "@lightprotocol/compressed-token/unified"; + +const instructions = await createTransferInterfaceInstructions( + rpc, + sponsor.publicKey, // payer: covers rent top-ups and transaction fees + mint, + amount, + sender.publicKey, // authority: user signs to authorize + recipient.publicKey +); +``` + + + + + +Both the sponsor and sender must sign the transaction: + +| | Parameter | What it does | +|:-----|:----------|:-------------| +| **Payer** (fee payer) | First positional arg | Signs to authorize payment of rent top-ups and transaction fees. Can be your application server. | +| **Authority** (owner) | `owner` / authority arg | Signs to authorize the token transfer. The account holder. | + + +```typescript +import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; + +for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + // Both sponsor and sender must sign + await sendAndConfirmTransaction(rpc, tx, [sponsor, sender]); +} +``` + + + + + + + + + + + + + + + + +Load or generate a keypair for the sponsor. The sponsor needs SOL but doesn't need to hold the tokens being transferred. + +```rust +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; + +// Sponsor: your application server +let sponsor: Keypair = /* your server keypair */; + +// User: only signs to authorize the transfer +let sender: Keypair = /* user's keypair */; +let recipient: Pubkey = /* recipient address */; +let mint: Pubkey = /* e.g. USDC mint */; +``` + + + + + +Build the transfer instruction with the sponsor as `payer` and the sender as `authority`. + +```rust +use light_token::instruction::{ + get_associated_token_address, TransferInterface, LIGHT_TOKEN_PROGRAM_ID, +}; + +let sender_ata = get_associated_token_address(&sender.pubkey(), &mint); +let recipient_ata = get_associated_token_address(&recipient, &mint); + +let transfer_ix = TransferInterface { + source: sender_ata, + destination: recipient_ata, + amount: 500_000, + decimals: 6, + authority: sender.pubkey(), // user signs to authorize + payer: sponsor.pubkey(), // sponsor covers rent top-ups and transaction fees + spl_interface: None, + source_owner: LIGHT_TOKEN_PROGRAM_ID, + destination_owner: LIGHT_TOKEN_PROGRAM_ID, +} +.instruction()?; +``` + + + + + +Both the sponsor and sender must sign the transaction. + +```rust +let sig = rpc + .create_and_send_transaction( + &[transfer_ix], + &sponsor.pubkey(), // fee payer + &[&sponsor, &sender], // both sign + ) + .await?; + +println!("Tx: {sig}"); +``` + + + + + + + + + + + + + + + +## Related guides + + + + Send a single token transfer. + + + Load cold accounts and prepare to receive. + + + + diff --git a/light-token/wallets/overview.mdx b/light-token/wallets/overview.mdx index 04959d72..f90fea7b 100644 --- a/light-token/wallets/overview.mdx +++ b/light-token/wallets/overview.mdx @@ -64,7 +64,7 @@ import SupportFooter from "/snippets/support-footer.mdx"; Find full runnable code examples - [here](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets). + [here](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments). @@ -77,7 +77,7 @@ import SupportFooter from "/snippets/support-footer.mdx"; Snippets below assume `rpc`, `payer`, `mint`, `owner`, `recipient`, and `amount` are defined. -See the [full examples](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets) for runnable setup. +See the [full examples](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments) for runnable setup. ```typescript import { createRpc } from "@lightprotocol/stateless.js"; @@ -108,7 +108,7 @@ loop handles the rare multi-transaction case automatically. ## Receive Payments -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/receive.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/receive/receive.ts). Load creates the associated token account (ATA) if needed and loads any compressed state into it. Share the ATA address with the sender. @@ -187,7 +187,7 @@ const ata = await getOrCreateAssociatedTokenAccount( ## Send Payments -Find a full code example: [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/send-instruction.ts) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/send-action.ts). +Find a full code example: [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-instruction.ts) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-action.ts). @@ -276,7 +276,7 @@ await transfer(connection, payer, sourceAta, destinationAta, owner, amount); ## Show Balance -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/get-balance.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-balance.ts). ```typescript @@ -306,7 +306,7 @@ console.log(account.amount); ## Transaction History -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/get-history.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-history.ts). ```typescript @@ -332,7 +332,7 @@ const signatures = await connection.getSignaturesForAddress(ata); Wrap tokens from SPL/Token-2022 accounts to light-token ATA. -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/wrap.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/interop/wrap.ts). @@ -392,7 +392,7 @@ Unwrap moves the token balance from a light-token account to a SPL-token account Use this to compose with applications that do not yet support light-token. -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/unwrap.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/interop/unwrap.ts). @@ -449,7 +449,7 @@ await unwrap(rpc, payer, splAta, owner, mint, amount);
- + diff --git a/light-token/wallets/sponsor-top-ups.mdx b/light-token/wallets/sponsor-top-ups.mdx deleted file mode 100644 index 7089be58..00000000 --- a/light-token/wallets/sponsor-top-ups.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Sponsor Rent Top-Ups for Users -description: The Light SDK sponsors rent-exemption for Solana accounts and keeps accounts active through periodic top-ups paid by the fee payer. Sponsor top-ups by setting your application as the fee payer to abstract away holding SOL from your users or provide a rent-free experience similar to transaction fee sponsorship. -keywords: ["rent free tokens on solana", "rent sponsorship solana", "rent exemption solana", "light token rent", "gasless solana tokens"] ---- - -import TransferInstructionCode from "/snippets/code-snippets/light-token/sponsor-rent-top-ups/ts-instruction.mdx"; -import RustInstructionCode from "/snippets/code-snippets/light-token/sponsor-rent-top-ups/rust-instruction.mdx"; -import RentSponsorshipExplained from "/snippets/rent-sponsorship-explained.mdx"; -import SponsorTopUpsAiPrompt from "/snippets/ai-prompts/wallets/sponsor-top-ups.mdx"; -import SupportFooter from "/snippets/support-footer.mdx"; - ---- - - -Rent sponsorship is a built-in feature of the Light SDK’s that sponsors rent-exemption for all account types to reduce creation cost. -This is dealt with under the hood in a way that doesn’t disrupt the UX of what your users are used to with SPL-token. - - - - -## How to use Sponsored Rent Top-Ups for Accounts and Transfers - -To sponsor rent top-ups for your users, set the `payer` parameter to the sponsor’s public key when calling `createTransferInterfaceInstructions` (TypeScript) or building a `TransferInterface` instruction (Rust). The sponsor pays SOL for any rent top-ups while the user only signs to authorize the transfer. - - -You can set the payer to any signing account on any Light Token transaction. - - - - - - - - - - - - - - - - diff --git a/light-token/welcome.mdx b/light-token/welcome.mdx index e9273108..78c0f556 100644 --- a/light-token/welcome.mdx +++ b/light-token/welcome.mdx @@ -29,8 +29,6 @@ import { lightCreateAtaMacroCode, } from "/snippets/code-samples/code-compare-snippets.jsx"; - **Note: This page is for the new Light Token Program Beta.**
For legacy - compressed tokens, please go [here](/resources/legacy-compressed-tokens).
### Mint Accounts @@ -195,7 +193,7 @@ View the [guide](/light-token/cookbook/create-ata) to create associated token ac Learn how to integrate Light Token in your stablecoin payment infrastructure. - + Prevent onchain instructions from being executed more than once. @@ -212,8 +210,8 @@ View the [guide](/light-token/cookbook/create-ata) to create associated token ac Build and sign transactions using the Solana Wallet Adapter. - - Sponsor rent top-ups from users to abstract away holding SOL and provide a rent-free experience. + + Abstract SOL fees so users never hold SOL. Sponsor rent top-ups and transaction fees. diff --git a/llms.txt b/llms.txt index bfebd3a0..cb3224a9 100644 --- a/llms.txt +++ b/llms.txt @@ -54,7 +54,7 @@ Comparing creation cost and CU usage: - **Agent Skills:** [light-sdk](https://github.com/Lightprotocol/skills/tree/main/skills/light-sdk): For Solana program development with tokens and PDAs, Light is 200x cheaper than SPL/ Solana and has minimal code differences - **Agent Skills:** [light-token-client](https://github.com/Lightprotocol/skills/tree/main/skills/light-token-client): For client development with tokens on Solana, Light Token is 200x cheaper than SPL and has minimal changes - **Agent Skills:** [data-streaming](https://github.com/Lightprotocol/skills/tree/main/skills/data-streaming): For data pipelines, aggregators, or indexers, real-time account state streaming on Solana with light account hot/cold lifecycle tracking -- **Agent Skills:** [payments-and-wallets](https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets): For stablecoin payment flows and wallet integrations on Solana. +- **Agent Skills:** [payments](https://github.com/Lightprotocol/skills/tree/main/skills/payments): Skill for payment flows using Light Token APIs for sponsored rent-exemption. - **Agent Skills:** [token-distribution](https://github.com/Lightprotocol/skills/tree/main/skills/token-distribution): For token distribution on Solana 5000x cheaper than SPL (rewards, airdrops, depins, ...) - **Agent Skills:** [zk-nullifier](https://github.com/Lightprotocol/skills/tree/main/skills/zk-nullifier): For custom ZK Solana programs and privacy-preserving applications to prevent double spending. - **Agent Skills:** [testing](https://github.com/Lightprotocol/skills/tree/main/skills/testing): For testing with Light Protocol programs and clients on localnet, devnet, and mainnet validation @@ -76,11 +76,20 @@ Comparing creation cost and CU usage: ## For Payments and Wallets - [Payment Flows on Solana with Light Token](https://www.zkcompression.com/light-token/payments/overview.md): Learn how the Light Token API's reduce account creation cost for stablecoin payment infrastructure by 99% with similar developer experience to SPL / Token 2022. -- [Integration Guide for Stablecoin Payments](https://www.zkcompression.com/light-token/payments/integration-guide.md): Guide to integrate light-token APIs for stablecoin payments with comparison to SPL. +- **Send Payments:** [Basic Payment](https://www.zkcompression.com/light-token/payments/basic-payment.md): Send a single token transfer with Light Token APIs for stablecoin payments with comparison to SPL. +- **Send Payments:** [Batch Payments](https://www.zkcompression.com/light-token/payments/batch-payments.md): Send payments to multiple recipients in a single transaction or sequentially. +- **Send Payments:** [Payment with Memo](https://www.zkcompression.com/light-token/payments/payment-with-memo.md): Attach invoice IDs, payment references, or notes to Light Token transfers using Solana's memo program. The memo is recorded in the transaction logs for reconciliation. +- [Receive Payments](https://www.zkcompression.com/light-token/payments/receive-payments.md): Prepare to receive token payments by loading cold accounts and sharing your associated token account address. +- **Verify Payments:** [Verify Payments](https://www.zkcompression.com/light-token/payments/verify-payments.md): Query token balances and transaction history to verify incoming payments. +- **Verify Payments:** [Verify Address](https://www.zkcompression.com/light-token/payments/verify-recipient-address.md): Verify recipient addresses before sending payments. Address validation prevents sending tokens to invalid or unexpected account types. +- [Wrap and Unwrap](https://www.zkcompression.com/light-token/payments/wrap-unwrap.md): Move tokens between SPL / Token 2022 and Light Token accounts for interoperability with applications that don't support Light Token yet. +- **Advanced:** [Spend Permissions via Delegation](https://www.zkcompression.com/light-token/payments/spend-permissions.md): Delegate token spending to a third party with an amount cap. The delegate can transfer tokens on behalf of the owner up to the approved amount, without the owner signing each transaction. +- **Advanced:** [Create Nullifier PDAs](https://www.zkcompression.com/pda/compressed-pdas/nullifier-pda.md): Create rent-free nullifier PDAs to prevent duplicate actions. +- [Production Readiness](https://www.zkcompression.com/light-token/payments/production-readiness.md): Non-exhaustive checklist for deploying Light Token payment flows to production, including RPC infrastructure, error handling, and security. - [Integration Guide for Wallet Applications](https://www.zkcompression.com/light-token/wallets/overview.md): Guide for Wallet Applications to add Light-token support. - [Sign with Privy](https://www.zkcompression.com/light-token/wallets/privy.md): Integrate light-token with Privy embedded wallets for rent-free token accounts and transfers. - [Sign with Wallet Adapter](https://www.zkcompression.com/light-token/wallets/wallet-adapter.md): Integrate light-token with Solana Wallet Adapter for rent-free token accounts and transfers. -- [Sponsor Rent Top-Ups for Users](https://www.zkcompression.com/light-token/wallets/sponsor-top-ups.md): The Light SDK sponsors rent-exemption for Solana accounts and keeps accounts active through periodic top-ups paid by the fee payer. Sponsor top-ups by setting your application as the fee payer to abstract away holding SOL from your users or provide a rent-free experience similar to transaction fee sponsorship. +- [Gasless Transactions](https://www.zkcompression.com/light-token/wallets/gasless-transactions.md): Abstract SOL fees so users never hold SOL. The Light SDK sponsors rent-exemption for Solana accounts and keeps accounts active through periodic top-ups paid by the fee payer. Sponsor top-ups and transaction fees by setting your application as the fee payer. ## For DeFi - [Router Integration](https://www.zkcompression.com/light-token/defi/routers.md): Add support for rent-free AMMs on Solana. @@ -93,18 +102,21 @@ Comparing creation cost and CU usage: ## Light Token Basics - [The Light Token Program](https://www.zkcompression.com/light-token/welcome.md): High-performance token standard that is functionally equivalent to SPL while reducing account creation cost by 200x and being more CU efficient on hot paths. The Light Token SDK keeps code changes minimal and is a superset of the SPL-token API. -- [Create Mint Account with Token Metadata](https://www.zkcompression.com/light-token/cookbook/create-mint.md): Program and client guides to create a mint with token metadata. For existing SPL or Token 2022 mints add an interface PDA for interoperability. Includes step-by-step implementation and full code examples. +- **Mint Accounts:** [Create Mint Account with Token Metadata](https://www.zkcompression.com/light-token/cookbook/create-mint.md): Create a Light mint with token metadata, or create SPL and Token 2022 mints with interface PDA for interoperability. +- **Mint Accounts:** [Add Interface PDA to Existing SPL / Token 2022 Mints](https://www.zkcompression.com/light-token/cookbook/add-interface-pda.md): Create an interface PDA for an existing SPL or Token 2022 mint for interoperability with Light Token. Does not require mint authority. - [Create Associated Light Token Accounts](https://www.zkcompression.com/light-token/cookbook/create-ata.md): Client and program guide to create associated Light Token accounts. Includes step-by-step implementation and full code examples. - [Create Light Token Account](https://www.zkcompression.com/light-token/cookbook/create-token-account.md): Client and program guide to create Light Token accounts. Includes step-by-step implementation and full code examples. - [Mint to Light Token Accounts](https://www.zkcompression.com/light-token/cookbook/mint-to.md): Client and program guide to mint tokens to a account. Includes step-by-step implementation and full code examples. -- [Transfer Interface](https://www.zkcompression.com/light-token/cookbook/transfer-interface.md): Program and client guide for transfers between Light Token and SPL token accounts. The interface detects account types and invokes the right programs. -- [Transfer Checked](https://www.zkcompression.com/light-token/cookbook/transfer-checked.md): Rust client guide to transfer Light Tokens with decimal validation. Includes step-by-step implementation and full code examples. -- [Approve and Revoke Delegates](https://www.zkcompression.com/light-token/cookbook/approve-revoke.md): Rust client guide to approve and revoke delegates for Light Token accounts. Includes step-by-step implementation and full code examples. +- [Transfer Interface](https://www.zkcompression.com/light-token/cookbook/transfer-interface.md): Transfer tokens between Light Token, SPL and Token 2022 accounts. The interface checks decimals under the hood and detects account types to invoke the right programs. +- [Transfer Checked](https://www.zkcompression.com/light-token/cookbook/transfer-checked.md): Program CPI guide for Light Token transfer with decimal validation. Includes step-by-step implementation and full code examples. +- **Delegates:** [Approve and Revoke Delegates](https://www.zkcompression.com/light-token/cookbook/approve-revoke.md): Guide to approve and revoke delegates for Light Token accounts. Includes step-by-step implementation and full code examples. +- **Delegates:** [Delegated Transfer](https://www.zkcompression.com/light-token/cookbook/transfer-delegated.md): Transfer tokens as an approved delegate. The delegate signs instead of the owner, spending within the approved allowance. - [Freeze and Thaw Light Token Accounts](https://www.zkcompression.com/light-token/cookbook/freeze-thaw.md): Rust client guide to freeze and thaw Light Token accounts. Includes step-by-step implementation and full code examples. - [Wrap & Unwrap SPL/Token 2022 <> Light Token](https://www.zkcompression.com/light-token/cookbook/wrap-unwrap.md): Move tokens between SPL/Token 2022 token and Light Token accounts. Use to interact with applications that only support SPL/Token 2022. - [Load Token Balances to Light Associated Token Account](https://www.zkcompression.com/light-token/cookbook/load-ata.md): Unify token balances from compressed tokens (cold Light Tokens), SPL, and Token 2022 to one light ATA. - [Close Light Token Account](https://www.zkcompression.com/light-token/cookbook/close-token-account.md): Program guide to close Light Token accounts via CPI. Includes step-by-step implementation and full code examples. - [Burn Light Tokens](https://www.zkcompression.com/light-token/cookbook/burn.md): Rust client guide to burn Light Tokens. Includes step-by-step implementation and full code examples. +- [Token 2022 Extensions with Light Token](https://www.zkcompression.com/light-token/extensions/overview.md): Most Token 2022 extensions are supported by Light Token to add features through extra instructions to a token mint or token account. - [Client examples](https://www.zkcompression.com/light-token/examples/client.md): TypeScript and Rust client examples for light-token SDK. - [Program examples](https://www.zkcompression.com/light-token/examples/program.md): Anchor program examples for light-token CPI. @@ -113,7 +125,6 @@ Comparing creation cost and CU usage: - [Light PDA](https://www.zkcompression.com/pda/light-pda/overview.md): Create Solana PDA accounts with sponsored rent-exemption and minimal code changes. Use like any Solana PDA, e.g. for DeFi pools, vaults, pool accounts, or other shared state. - **Compressed PDA:** [Overview & Program Template](https://www.zkcompression.com/pda/compressed-pdas/overview.md): Compressed PDAs provide full composability and functionality of accounts at PDAs, without rent-exemption cost per account. Suited for accounts where each user or entity gets their own PDA and state is infrequently accessed. - **Compressed PDA:** [Client Guide](https://www.zkcompression.com/pda/compressed-pdas/guides/client-guide.md): Rust and Typescript client guides with step-by-step implementation and full code examples. -- **Compressed PDA:** [Create Nullifier PDAs](https://www.zkcompression.com/pda/compressed-pdas/how-to-create-nullifier-pdas.md): Create rent-free nullifier PDAs to prevent duplicate actions. - **Compressed PDA > Program Guides:** [Overview](https://www.zkcompression.com/pda/compressed-pdas/guides.md): Overview to guides for Solana programs to create, update, close, reinitialize, and burn permanently compressed accounts. - **Compressed PDA:** [Program Examples](https://www.zkcompression.com/pda/compressed-pdas/program-examples.md): Program example repository for compressed accounts with tests. @@ -125,7 +136,7 @@ Comparing creation cost and CU usage: ## Learn - [Overview of core concepts](https://www.zkcompression.com/learn/overview.md): Learn about Light Token and ZK Compression Core. -- [Core concepts of the Light Token program (Beta)](https://www.zkcompression.com/learn/light-token-standard.md): The Light Token Program is a high performance token program that reduces the cost of account creations by 200x, while being more CU efficient than SPL on hot paths. +- [Core concepts of the Light Token program](https://www.zkcompression.com/learn/light-token-standard.md): The Light Token Program is a high performance token program that reduces the cost of account creations by 200x, while being more CU efficient than SPL on hot paths. - **Core Concepts (ZK Compression):** [High-level System Overview](https://www.zkcompression.com/learn/core-concepts.md): Overview to ZK Compression's Core Concepts. Get a high-level system overview and learn about the compressed account model, lifecycle of a transaction, and considerations. - **Core Concepts (ZK Compression):** [Compressed Account Model](https://www.zkcompression.com/learn/core-concepts/compressed-account-model.md): Overview to compressed accounts and comparison to Solana accounts. - **Core Concepts (ZK Compression):** [Merkle trees and Validity Proofs](https://www.zkcompression.com/learn/core-concepts/merkle-trees-validity-proofs.md): Learn the core concepts of state trees, address trees, and validity proofs for compressed accounts. @@ -148,20 +159,21 @@ Comparing creation cost and CU usage: - [Support](https://www.zkcompression.com/support.md): Get expert help with Light-Token and ZK Compression. Discord community, Telegram, and Email support available. ## Examples for DeFi -- [cp-swap-reference](https://github.com/Lightprotocol/program-examples/tree/main/cp-swap-reference): Fork of Raydium AMM that creates markets without paying rent-exemption. -- [pinocchio-swap](https://github.com/Lightprotocol/program-examples/tree/main/pinocchio-swap): Light Token swap reference implementation. -- [token-swap](https://github.com/Lightprotocol/program-examples/tree/main/token-swap): AMM with liquidity pools and swaps. -- [escrow](https://github.com/Lightprotocol/program-examples/tree/main/escrow): Peer-to-peer light-token swap with offer/accept flow. -- [fundraiser](https://github.com/Lightprotocol/program-examples/tree/main/fundraiser): Token fundraiser with target, deadline, and refunds. -- [create-and-transfer](https://github.com/Lightprotocol/program-examples/tree/main/create-and-transfer): Create account via macro and transfer via CPI. -- [light-token-minter](https://github.com/Lightprotocol/program-examples/tree/main/light-token-minter): Create light-mints with metadata, mint tokens. +- [cp-swap-reference](https://github.com/Lightprotocol/cp-swap-reference): Fork of Raydium AMM that creates markets without paying rent-exemption. +- [pinocchio-swap](https://github.com/Lightprotocol/examples-light-token/tree/main/pinocchio/swap): Light Token swap reference implementation. +- [token-swap](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/token-swap): AMM with liquidity pools and swaps. +- [escrow](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/escrow): Peer-to-peer light-token swap with offer/accept flow. +- [fundraiser](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/fundraiser): Token fundraiser with target, deadline, and refunds. +- [create-and-transfer](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/create-and-transfer): Create account via macro and transfer via CPI. +- [light-token-minter](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/light-token-minter): Create light-mints with metadata, mint tokens. ## Examples for Payments and Wallets -- [payments-and-wallets](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets): Wallet integrations and payment flows. +- [payments](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments): Wallet integrations and payment flows. - [sign-with-privy](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy): Light-token operations signed with Privy wallets. - [sign-with-wallet-adapter](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-wallet-adapter): Light-token operations signed with Wallet Adapter. -- [sponsor-rent-top-ups](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sponsor-rent-top-ups): Sponsor rent top-ups by setting your application as the fee payer. -- [spl-to-light](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/spl-to-light): Transfer from SPL to Light via TransferInterface. +- [gasless-transactions](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/gasless-transactions): Abstract SOL fees so users never hold SOL. Sponsor rent top-ups and transaction fees. +- [spl-to-light](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/instructions/spl_to_light_transfer.rs): Transfer from SPL to Light via TransferInterface. +- [streaming-tokens](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/streaming-tokens): Stream mint events using Laserstream. ## OpenAPI Specs - [api](https://www.zkcompression.com/openapi/api.yaml) diff --git a/pda/compressed-pdas/how-to-create-nullifier-pdas.mdx b/pda/compressed-pdas/nullifier-pda.mdx similarity index 96% rename from pda/compressed-pdas/how-to-create-nullifier-pdas.mdx rename to pda/compressed-pdas/nullifier-pda.mdx index cd6410b9..8afb27a1 100644 --- a/pda/compressed-pdas/how-to-create-nullifier-pdas.mdx +++ b/pda/compressed-pdas/nullifier-pda.mdx @@ -21,7 +21,7 @@ We also deployed a reference implementation to public networks so you can get st | **Example Tx** | [Solana Explorer](https://explorer.solana.com/tx/38fA6kbKRcYb5XSez9ffQzfCcMbMHcaJGseCogShNXC5SemQsEo88ZMSPCLP9xv9PG8qSJnhWvWFqSYJnfBMLrpB) | -For the usage example source code, see here: [create_nullifier.rs](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/actions/create_nullifier.rs#L25) +Example source code: [TypeScript](https://github.com/Lightprotocol/nullifier-program/tree/main/examples/action-create-nullifier.ts) | [Rust](https://github.com/Lightprotocol/nullifier-program/tree/main/examples/rust) diff --git a/pda/compressed-pdas/overview.mdx b/pda/compressed-pdas/overview.mdx index 78bee24d..a0c91fd4 100644 --- a/pda/compressed-pdas/overview.mdx +++ b/pda/compressed-pdas/overview.mdx @@ -24,7 +24,7 @@ Compressed PDAs are suited for: - Per-user state (profiles, preferences, credentials) - DePIN node accounts and stake accounts - Nullifiers to prevent an on-chain instruction from -being executed twice (little implementation overhead and useful e.g. for [payments](/pda/compressed-pdas/how-to-create-nullifier-pdas) or [ZK programs](zk/overview)) +being executed twice (little implementation overhead and useful e.g. for [payments](/pda/compressed-pdas/nullifier-pda) or [ZK programs](zk/overview)) - App state that is written to and read from infrequently For DeFi pools, vaults, shared state, pool accounts, or config accounts, use [Light-PDA](/pda/light-pda/overview) with minimal code changes. diff --git a/pda/light-pda/overview.mdx b/pda/light-pda/overview.mdx index 66df2ea3..cc37cdaf 100644 --- a/pda/light-pda/overview.mdx +++ b/pda/light-pda/overview.mdx @@ -204,8 +204,6 @@ depending on number and type of accounts being initialized or loaded. --- -API is in Beta and subject to change. - Questions or need hands-on support? [Telegram](https://t.me/swen_light) | [email](mailto:support@lightprotocol.com) | [Discord](https://discord.com/invite/7cJ8BhAXhu) diff --git a/pda/overview.mdx b/pda/overview.mdx index c685b656..7f512418 100644 --- a/pda/overview.mdx +++ b/pda/overview.mdx @@ -38,7 +38,7 @@ Fully compatible with existing Solana programs, but requires custom logic. > - Per-user state (profiles, preferences, credentials) > - DePIN node accounts and stake accounts > - Nullifiers to prevent an on-chain instruction from -> being executed twice (little implementation overhead and useful e.g. for [payments](/pda/compressed-pdas/how-to-create-nullifier-pdas) or [ZK programs](zk/overview)) +> being executed twice (little implementation overhead and useful e.g. for [payments](/pda/compressed-pdas/nullifier-pda) or [ZK programs](zk/overview)) > - App state that is written to and read from infrequently \ No newline at end of file diff --git a/resources/legacy-compressed-tokens.mdx b/resources/legacy-compressed-tokens.mdx index cb06ea34..827eb3e1 100644 --- a/resources/legacy-compressed-tokens.mdx +++ b/resources/legacy-compressed-tokens.mdx @@ -15,10 +15,9 @@ import AgentSkillAirdrop from "/snippets/setup/agent-skill-airdrop.mdx"; -**Note: The new [Light Token Program Beta](/light-token/welcome)** is live on Solana Devnet with mainnet expected in Q1 2026. -We recommend to use Light Token for most purposes except airdrops and other forms of token distribution. -The SDK for compressed tokens can still be used once Light Token is on Solana Mainnet. -For production use today, use Compressed Tokens, which are on Solana Mainnet. +The [Light Token Program](/light-token/welcome) is live on Solana mainnet. +Use Light Token for most purposes except airdrops and other forms of token distribution. +The compressed token SDK remains supported for token distribution use cases. | Creation | Solana | Compressed | diff --git a/resources/terminology.mdx b/resources/terminology.mdx index b8cd42d2..436d75bb 100644 --- a/resources/terminology.mdx +++ b/resources/terminology.mdx @@ -39,7 +39,7 @@ Skills for compressed PDAs and more are in development. | Build rent-free Solana programs with Light SDK (Anchor or Pinocchio). Includes router integration. | [light-sdk](https://github.com/Lightprotocol/skills/tree/main/skills/light-sdk) | | Use Light Token client SDKs (TypeScript and Rust) for mints, ATAs, transfers | [light-token-client](https://github.com/Lightprotocol/skills/tree/main/skills/light-token-client) | | Stream account state via Laserstream gRPC | [data-streaming](https://github.com/Lightprotocol/skills/tree/main/skills/data-streaming) | -| Wallets and payment flows with Light Token. Optional nullifier to prevent your on-chain instruction from being executed more than once. | [payments-and-wallets](https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets) | +| Skill for payment flows using Light Token APIs for sponsored rent-exemption. | [payments](https://github.com/Lightprotocol/skills/tree/main/skills/payments) | | Airdrops, DePIN, token distribution | [token-distribution](https://github.com/Lightprotocol/skills/tree/main/skills/token-distribution) | | Anti-double-spend nullifiers for privacy-preserving ZK programs | [zk-nullifier](https://github.com/Lightprotocol/skills/tree/main/skills/zk-nullifier) | | Testing programs and clients on localnet, devnet, mainnet | [testing](https://github.com/Lightprotocol/skills/tree/main/skills/testing) | @@ -301,7 +301,7 @@ The documentation provides two implementations with rent-free PDA accounts: | **Rust SDK** | [light-nullifier-program](https://crates.io/crates/light-nullifier-program) | | **TypeScript SDK** | [@lightprotocol/nullifier-program](https://www.npmjs.com/package/@lightprotocol/nullifier-program) | -> [Docs](/pda/compressed-pdas/how-to-create-nullifier-pdas) | [Skill](https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets) | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/actions/create_nullifier.rs) +> [Docs](/pda/compressed-pdas/nullifier-pda) | [Skill](https://github.com/Lightprotocol/skills/tree/main/skills/payments) | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/actions/create_nullifier.rs) **ZK nullifier** — For ZK or privacy-preserving programs on Solana to prevent double-spending. Can be integrated with minimal code changes. diff --git a/scripts/copy-code-snippets.sh b/scripts/copy-code-snippets.sh index d4f3e3f0..458ff4bf 100755 --- a/scripts/copy-code-snippets.sh +++ b/scripts/copy-code-snippets.sh @@ -3,8 +3,11 @@ # Script to copy code from program-examples to docs/snippets/code-snippets # Wraps each file in appropriate markdown code blocks -PROGRAM_EXAMPLES="/home/tilo/Workspace/program-examples/basic-operations" -SNIPPETS_DIR="/home/tilo/Workspace/docs/snippets/code-snippets" +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROGRAM_EXAMPLES="${PROGRAM_EXAMPLES_ROOT:?Set PROGRAM_EXAMPLES_ROOT to program-examples repo root}/basic-operations" +SNIPPETS_DIR="$SCRIPT_DIR/../snippets/code-snippets" # Operations to process OPERATIONS=("create" "update" "close" "reinit" "burn") diff --git a/scripts/copy-light-token-snippets.sh b/scripts/copy-light-token-snippets.sh index c24d40c1..c86120d7 100644 --- a/scripts/copy-light-token-snippets.sh +++ b/scripts/copy-light-token-snippets.sh @@ -3,8 +3,11 @@ # Script to copy TypeScript code from streaming-tokens to docs/snippets/code-snippets/light-token # Wraps each file in typescript markdown code blocks -EXAMPLES="/home/tilo/Workspace/streaming-tokens/typescript-client" -SNIPPETS_DIR="/home/tilo/Workspace/docs/snippets/code-snippets/light-token" +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +EXAMPLES="${EXAMPLES_LIGHT_TOKEN:?Set EXAMPLES_LIGHT_TOKEN to examples-light-token repo root}/typescript-client" +SNIPPETS_DIR="$SCRIPT_DIR/../snippets/code-snippets/light-token" # Recipes to process (matching directory and file names) RECIPES=("create-mint" "create-ata" "mint-to" "transfer-interface" "load-ata" "wrap" "unwrap") diff --git a/scripts/copy-payments-snippets.sh b/scripts/copy-payments-snippets.sh new file mode 100755 index 00000000..a0fbbe37 --- /dev/null +++ b/scripts/copy-payments-snippets.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# Script to copy TypeScript code from examples-light-token payments to docs snippets. +# Wraps each file in typescript markdown code blocks. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +EXAMPLES="${EXAMPLES_LIGHT_TOKEN:?Set EXAMPLES_LIGHT_TOKEN to examples-light-token repo root}/toolkits/payments" +SNIPPETS_DIR="$SCRIPT_DIR/../snippets/code-snippets/payments" + +# Function to wrap TypeScript code in markdown +wrap_typescript() { + local input_file="$1" + local output_file="$2" + mkdir -p "$(dirname "$output_file")" + echo '```typescript' > "$output_file" + cat "$input_file" >> "$output_file" + echo '```' >> "$output_file" + echo "Created: $output_file" +} + +# Send examples +echo "Processing: send/" +for file in send-action send-instruction batch-send payment-with-memo sign-all-transactions; do + src="$EXAMPLES/send/$file.ts" + if [ -f "$src" ]; then + wrap_typescript "$src" "$SNIPPETS_DIR/send/$file.mdx" + else + echo " WARNING: Not found - $src" + fi +done + +# Receive examples +echo "Processing: receive/" +src="$EXAMPLES/receive/receive.ts" +if [ -f "$src" ]; then + wrap_typescript "$src" "$SNIPPETS_DIR/receive/receive.mdx" +else + echo " WARNING: Not found - $src" +fi + +# Verify examples +echo "Processing: verify/" +for file in get-balance get-history verify-address; do + src="$EXAMPLES/verify/$file.ts" + if [ -f "$src" ]; then + wrap_typescript "$src" "$SNIPPETS_DIR/verify/$file.mdx" + else + echo " WARNING: Not found - $src" + fi +done + +# Spend permissions examples +echo "Processing: spend-permissions/" +for file in delegate-approve delegate-revoke delegate-check delegate-full-flow; do + src="$EXAMPLES/spend-permissions/$file.ts" + if [ -f "$src" ]; then + wrap_typescript "$src" "$SNIPPETS_DIR/spend-permissions/$file.mdx" + else + echo " WARNING: Not found - $src" + fi +done + +# Interop examples +echo "Processing: interop/" +for file in wrap unwrap register-spl-mint; do + src="$EXAMPLES/interop/$file.ts" + if [ -f "$src" ]; then + wrap_typescript "$src" "$SNIPPETS_DIR/interop/$file.mdx" + else + echo " WARNING: Not found - $src" + fi +done + +echo "" +echo "Done! Created snippets in: $SNIPPETS_DIR" +echo "" +echo "Files created:" +find "$SNIPPETS_DIR" -name "*.mdx" -type f | sort \ No newline at end of file diff --git a/scripts/copy-privy-snippets.sh b/scripts/copy-privy-snippets.sh index f74f9552..828e08eb 100755 --- a/scripts/copy-privy-snippets.sh +++ b/scripts/copy-privy-snippets.sh @@ -4,9 +4,12 @@ # Source: examples-light-token/privy/{nodejs,react} # Output: snippets/code-snippets/privy/{operation}/{nodejs,react}.mdx -NODEJS_SRC="/home/tilo/Workspace/examples-light-token-main/toolkits/sign-with-privy/nodejs/src" -REACT_SRC="/home/tilo/Workspace/examples-light-token-main/toolkits/sign-with-privy/react/src/hooks" -SNIPPETS_DIR="/home/tilo/Workspace/docs-main/snippets/code-snippets/privy" +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +NODEJS_SRC="${EXAMPLES_LIGHT_TOKEN:?Set EXAMPLES_LIGHT_TOKEN to examples-light-token repo root}/toolkits/sign-with-privy/nodejs/src" +REACT_SRC="${EXAMPLES_LIGHT_TOKEN}/toolkits/sign-with-privy/react/src/hooks" +SNIPPETS_DIR="$SCRIPT_DIR/../snippets/code-snippets/privy" # Operations to process OPERATIONS=("transfer" "receive" "wrap" "unwrap" "balances" "transaction-history") diff --git a/scripts/copy-program-snippets.sh b/scripts/copy-program-snippets.sh index d1c07900..475f77cb 100644 --- a/scripts/copy-program-snippets.sh +++ b/scripts/copy-program-snippets.sh @@ -3,13 +3,16 @@ # Script to copy program code from example repos to docs snippets # Creates CodeGroup MDX files with lib.rs/instruction.rs and test.rs combined -SNIPPETS_DIR="/home/tilo/Workspace/docs/snippets/code-snippets/light-token" +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SNIPPETS_DIR="$SCRIPT_DIR/../snippets/code-snippets/light-token" # ============================================================================= # ANCHOR PROGRAMS # ============================================================================= -ANCHOR_EXAMPLES_DIR="/home/tilo/Workspace/examples-light-token-anchor/programs/anchor/basic-instructions" +ANCHOR_EXAMPLES_DIR="${EXAMPLES_LIGHT_TOKEN:?Set EXAMPLES_LIGHT_TOKEN to examples-light-token repo root}/programs/anchor/basic-instructions" # Anchor recipes (output-name:anchor-dir-name) # Some have different directory names (e.g., close-token-account uses 'close' dir) @@ -82,7 +85,7 @@ done # ANCHOR MACROS # ============================================================================= -ANCHOR_MACROS_DIR="/home/tilo/Workspace/examples-light-token-anchor/programs/anchor/basic-macros" +ANCHOR_MACROS_DIR="${EXAMPLES_LIGHT_TOKEN}/programs/anchor/basic-macros" ANCHOR_MACRO_RECIPES=( "create-mint:create-mint" diff --git a/scripts/copy-rust-snippets.sh b/scripts/copy-rust-snippets.sh index 4cad3dc9..eff19bc3 100644 --- a/scripts/copy-rust-snippets.sh +++ b/scripts/copy-rust-snippets.sh @@ -3,8 +3,11 @@ # Script to copy Rust client code from examples-light-token-rust-client to docs snippets # Creates action.mdx and instruction.mdx files wrapped in rust code blocks -EXAMPLES_DIR="/home/tilo/Workspace/examples-light-token/rust-client" -SNIPPETS_DIR="/home/tilo/Workspace/docs/snippets/code-snippets/light-token" +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +EXAMPLES_DIR="${EXAMPLES_LIGHT_TOKEN:?Set EXAMPLES_LIGHT_TOKEN to examples-light-token repo root}/rust-client" +SNIPPETS_DIR="$SCRIPT_DIR/../snippets/code-snippets/light-token" # Full recipes (action + instruction in same directory) FULL_RECIPES=("create-mint" "mint-to" "transfer-interface" "transfer-checked") diff --git a/scripts/copy-wallet-adapter-snippets.sh b/scripts/copy-wallet-adapter-snippets.sh index 4ffb0ae5..cec4fe7b 100755 --- a/scripts/copy-wallet-adapter-snippets.sh +++ b/scripts/copy-wallet-adapter-snippets.sh @@ -4,8 +4,11 @@ # Source: examples-light-token/toolkits/sign-with-wallet-adapter/react/src/hooks # Output: snippets/code-snippets/wallet-adapter/{operation}/react.mdx -REACT_SRC="/home/tilo/Workspace/examples-light-token-main/toolkits/sign-with-wallet-adapter/react/src/hooks" -SNIPPETS_DIR="/home/tilo/Workspace/docs-main/snippets/code-snippets/wallet-adapter" +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REACT_SRC="${EXAMPLES_LIGHT_TOKEN:?Set EXAMPLES_LIGHT_TOKEN to examples-light-token repo root}/toolkits/sign-with-wallet-adapter/react/src/hooks" +SNIPPETS_DIR="$SCRIPT_DIR/../snippets/code-snippets/wallet-adapter" # Operations to process OPERATIONS=("transfer" "receive" "wrap" "unwrap" "balances" "transaction-history") diff --git a/scripts/generate-llms-txt.js b/scripts/generate-llms-txt.js index 45da54f8..0afefa54 100644 --- a/scripts/generate-llms-txt.js +++ b/scripts/generate-llms-txt.js @@ -139,8 +139,8 @@ function buildAiTools(anchor) { 'For data pipelines, aggregators, or indexers, real-time account state streaming on Solana with light account hot/cold lifecycle tracking', ], [ - 'payments-and-wallets', - 'For stablecoin payment flows and wallet integrations on Solana.', + 'payments', + 'Skill for payment flows using Light Token APIs for sponsored rent-exemption.', ], [ 'token-distribution', @@ -248,21 +248,22 @@ function buildOpenApiSpecs() { // ── Hardcoded examples ────────────────────────────────────────────── const EXAMPLES_DEFI = [ - '- [cp-swap-reference](https://github.com/Lightprotocol/program-examples/tree/main/cp-swap-reference): Fork of Raydium AMM that creates markets without paying rent-exemption.', - '- [pinocchio-swap](https://github.com/Lightprotocol/program-examples/tree/main/pinocchio-swap): Light Token swap reference implementation.', - '- [token-swap](https://github.com/Lightprotocol/program-examples/tree/main/token-swap): AMM with liquidity pools and swaps.', - '- [escrow](https://github.com/Lightprotocol/program-examples/tree/main/escrow): Peer-to-peer light-token swap with offer/accept flow.', - '- [fundraiser](https://github.com/Lightprotocol/program-examples/tree/main/fundraiser): Token fundraiser with target, deadline, and refunds.', - '- [create-and-transfer](https://github.com/Lightprotocol/program-examples/tree/main/create-and-transfer): Create account via macro and transfer via CPI.', - '- [light-token-minter](https://github.com/Lightprotocol/program-examples/tree/main/light-token-minter): Create light-mints with metadata, mint tokens.', + '- [cp-swap-reference](https://github.com/Lightprotocol/cp-swap-reference): Fork of Raydium AMM that creates markets without paying rent-exemption.', + '- [pinocchio-swap](https://github.com/Lightprotocol/examples-light-token/tree/main/pinocchio/swap): Light Token swap reference implementation.', + '- [token-swap](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/token-swap): AMM with liquidity pools and swaps.', + '- [escrow](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/escrow): Peer-to-peer light-token swap with offer/accept flow.', + '- [fundraiser](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/fundraiser): Token fundraiser with target, deadline, and refunds.', + '- [create-and-transfer](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/create-and-transfer): Create account via macro and transfer via CPI.', + '- [light-token-minter](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/light-token-minter): Create light-mints with metadata, mint tokens.', ]; const EXAMPLES_PAYMENTS = [ - '- [payments-and-wallets](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets): Wallet integrations and payment flows.', + '- [payments](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments): Wallet integrations and payment flows.', '- [sign-with-privy](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy): Light-token operations signed with Privy wallets.', '- [sign-with-wallet-adapter](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-wallet-adapter): Light-token operations signed with Wallet Adapter.', - '- [sponsor-rent-top-ups](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sponsor-rent-top-ups): Sponsor rent top-ups by setting your application as the fee payer.', - '- [spl-to-light](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/spl-to-light): Transfer from SPL to Light via TransferInterface.', + '- [gasless-transactions](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/gasless-transactions): Abstract SOL fees so users never hold SOL. Sponsor rent top-ups and transaction fees.', + '- [spl-to-light](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/instructions/spl_to_light_transfer.rs): Transfer from SPL to Light via TransferInterface.', + '- [streaming-tokens](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/streaming-tokens): Stream mint events using Laserstream.', ]; // ── Hardcoded primitives routing table ──────────────────────────────── @@ -270,8 +271,8 @@ const EXAMPLES_PAYMENTS = [ const PRIMITIVES_SECTION = ` | Primitive | Use case | Constraints | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | -| Light Token | Most token use cases (Payment Rails, Consumer Apps, DeFi). Rent-free mint and token accounts. More compute-unit efficient on the hot path. | Currently in Beta and on Solana Devnet with mainnet in Q1 2026 | -| Light-PDA | DeFi program state such as AMM pools and vaults. Can be implemented with minimal code changes. | Currently in Beta and on Solana Devnet with mainnet in Q1 2026 | +| Light Token | Most token use cases (Payment Rails, Consumer Apps, DeFi). Rent-free mint and token accounts. More compute-unit efficient on the hot path. | | +| Light-PDA | DeFi program state such as AMM pools and vaults. Can be implemented with minimal code changes. | | | Compressed Token | Only for Airdrops and token distribution. Prefer Light Token for other purposes. Used by Light Token under the hood for rent-free storage of inactive Light Tokens. Supported by Phantom and Backpack. | Do not use for general-purpose token features. Use Light Token instead. | | Compressed PDA | User state and app state, nullifiers (payments and ZK applications), DePIN nodes, and stake accounts. Similar to program-derived addresses without a rent-exempt balance. | Not for shared state, pool accounts, or config accounts. Use Light-PDA instead | @@ -329,13 +330,13 @@ function generate() { // Light Token sections (payments, defi, streaming, then basics) const ltGroup = docAnchor.groups.find( - (g) => g.group === 'Light Token Program', + (g) => g.group === 'Light Token APIs', ); if (ltGroup) sections.push(...splitLightTokenProgram(ltGroup)); // Remaining Documentation groups (PDA, Other Use Cases, Learn, Resources) for (const group of docAnchor.groups) { - if (group.group === 'Introduction' || group.group === 'Light Token Program') + if (group.group === 'Introduction' || group.group === 'Light Token APIs') continue; sections.push({ name: sectionRenames[group.group] || group.group, diff --git a/scripts/sync-skills.sh b/scripts/sync-skills.sh index 47adac6b..c16953f2 100755 --- a/scripts/sync-skills.sh +++ b/scripts/sync-skills.sh @@ -1,8 +1,11 @@ #!/bin/bash # Sync skills from agent-skills repo to docs-main/ai-tools/skills -SOURCE="/home/tilo/Workspace/agent-skills/skills" -DEST="/home/tilo/Workspace/docs-main/ai-tools/skills" +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SOURCE="${AGENT_SKILLS_ROOT:?Set AGENT_SKILLS_ROOT to agent-skills repo root}/skills" +DEST="$SCRIPT_DIR/../ai-tools/skills" rsync -av --delete "$SOURCE/" "$DEST/" diff --git a/skill.md b/skill.md index e063a0a9..89e1841f 100644 --- a/skill.md +++ b/skill.md @@ -82,35 +82,16 @@ npx skills add Lightprotocol/skills | Use case | Skill | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | +| Skill for payment flows using Light Token APIs for sponsored rent-exemption. | [payments](https://github.com/Lightprotocol/skills/tree/main/skills/payments) | | For Solana program development with tokens and PDAs, Light is 200x cheaper than SPL/ Solana and has minimal code differences | [light-sdk](https://github.com/Lightprotocol/skills/tree/main/skills/light-sdk) | | For client development with tokens on Solana, Light Token is 200x cheaper than SPL and has minimal changes | [light-token-client](https://github.com/Lightprotocol/skills/tree/main/skills/light-token-client) | | For data pipelines, aggregators, or indexers, real-time account state streaming on Solana with light account hot/cold lifecycle tracking | [data-streaming](https://github.com/Lightprotocol/skills/tree/main/skills/data-streaming) | -| For stablecoin payment flows and wallet integrations on Solana 200x cheaper token accounts | [payments-and-wallets](https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets) | | For token distribution on Solana 5000x cheaper than SPL (rewards, airdrops, depins, ...) | [token-distribution](https://github.com/Lightprotocol/skills/tree/main/skills/token-distribution) | | For custom ZK Solana programs and privacy-preserving applications to prevent double spending | [zk-nullifier](https://github.com/Lightprotocol/skills/tree/main/skills/zk-nullifier) | | For program development on Solana with infrequently accessed state, such as per-user state, DePIN registrations, ... | [solana-compression](https://github.com/Lightprotocol/skills/tree/main/skills/solana-compression) | | For testing with Light Protocol programs and clients on localnet, devnet, and mainnet validation | [testing](https://github.com/Lightprotocol/skills/tree/main/skills/testing) | | For questions about compressed accounts, Light SDK, Solana development, Claude Code features, or agent skills | [ask-mcp](https://github.com/Lightprotocol/skills/tree/main/skills/ask-mcp) | -### Install to Claude Code - -Add the marketplace and install: - -``` -/plugin marketplace add Lightprotocol/skills -/plugin install solana-rent-free-dev -``` - -All skills are included. Use them by name (`/light-sdk`, `/token-distribution`, `/testing`, etc.) or let Claude invoke them based on task context. - -### Install to Cursor - -1. Open Settings (**Cmd+Shift+J** / **Ctrl+Shift+J**) -2. Navigate to **Rules & Commands** → **Project Rules** → **Add Rule** → **Remote Rule (GitHub)** -3. Enter: `https://github.com/Lightprotocol/skills.git` - -Skills are auto-discovered based on context. Ask about light-token, defi, payments, or program migration and the agent uses the relevant skill automatically. - ### Install to Any Agent ``` @@ -131,7 +112,7 @@ A token standard functionally equivalent to SPL that stores mint and token accou The token program pays rent-exemption cost for you. When an account has no remaining sponsored rent, the account is automatically compressed. Your tokens are cryptographically preserved as a compressed token account (rent-free). The account is loaded into hot account state in-flight when someone interacts with it again. -Use for: Launchpads, DeFi, token transfers, payments, ... . +Use for: Stablecoin Orchestration, Cards, Agent Commerce, Defi, ... . ### light-PDA @@ -169,27 +150,39 @@ Use rent-free PDAs for: user state, app state, nullifiers for payments, DePIN no - **light-token accounts hold SPL and Token-2022 balances**, not just light-mint balances. - When sponsored rent on a light-token or light-PDA runs out, the account compresses. It decompresses on next interaction. -## Examples - -### Toolkits +## Payment Flows + +| Name | Description | Docs | Examples | +|------|-------------|------|----------| +| Overview | Learn how the Light Token APIs reduce account creation cost for stablecoin payment infrastructure by 99% with similar developer experience to SPL / Token 2022. | [overview](https://zkcompression.com/light-token/payments/overview) | | +| Basic payment | Send a single token transfer with Light Token APIs for stablecoin payments with comparison to SPL. | [basic-payment](https://zkcompression.com/light-token/payments/basic-payment) | [send-action](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-action.ts) \| [send-instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/send-instruction.ts) | +| Batch payments | Send payments to multiple recipients in a single transaction or sequentially. | [batch-payments](https://zkcompression.com/light-token/payments/batch-payments) | [batch-send](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/batch-send.ts) | +| Payment with memo | Attach invoice IDs, payment references, or notes to Light Token transfers using Solana's memo program. The memo is recorded in the transaction logs for reconciliation. | [payment-with-memo](https://zkcompression.com/light-token/payments/payment-with-memo) | [payment-with-memo](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/payment-with-memo.ts) | +| Receive payments | Prepare to receive token payments by loading cold accounts and sharing your associated token account address. | [receive-payments](https://zkcompression.com/light-token/payments/receive-payments) | [receive](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/receive/receive.ts) | +| Verify payments | Query token balances and transaction history to verify incoming payments. | [verify-payments](https://zkcompression.com/light-token/payments/verify-payments) | [get-balance](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-balance.ts) \| [get-history](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/get-history.ts) | +| Verify address | Verify recipient addresses before sending payments. Address validation prevents sending tokens to invalid or unexpected account types. | [verify-recipient-address](https://zkcompression.com/light-token/payments/verify-recipient-address) | [verify-address](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/verify-address.ts) | +| Wrap and unwrap | Move tokens between SPL / Token 2022 and Light Token accounts for interoperability with applications that don't support Light Token yet. | [wrap-unwrap](https://zkcompression.com/light-token/payments/wrap-unwrap) | [wrap](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/interop/wrap.ts) \| [unwrap](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/interop/unwrap.ts) | +| Spend permissions | Delegate token spending to a third party with an amount cap. The delegate can transfer tokens on behalf of the owner up to the approved amount, without the owner signing each transaction. | [spend-permissions](https://zkcompression.com/light-token/payments/spend-permissions) | [delegate-full-flow](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/spend-permissions/delegate-full-flow.ts) | +| Nullifier PDAs | Create rent-free nullifier PDAs to prevent duplicate actions. | [nullifier-pda](https://zkcompression.com/pda/compressed-pdas/nullifier-pda) | | +| Production readiness | Non-exhaustive checklist for deploying Light Token payment flows to production, including RPC infrastructure, error handling, and security. | [production-readiness](https://zkcompression.com/light-token/payments/production-readiness) | | +| Wallet integration | Guide for Wallet Applications to add Light-token support. | [wallets/overview](https://zkcompression.com/light-token/wallets/overview) | | +| Sign with Privy | Integrate light-token with Privy embedded wallets for rent-free token accounts and transfers. | [privy](https://zkcompression.com/light-token/wallets/privy) | [sign-with-privy](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy) | +| Sign with Wallet Adapter | Integrate light-token with Solana Wallet Adapter for rent-free token accounts and transfers. | [wallet-adapter](https://zkcompression.com/light-token/wallets/wallet-adapter) | [sign-with-wallet-adapter](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-wallet-adapter) | +| Gasless transactions | Abstract SOL fees so users never hold SOL. Sponsor top-ups and transaction fees by setting your application as the fee payer. | [gasless-transactions](https://zkcompression.com/light-token/wallets/gasless-transactions) | [gasless-transactions](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/gasless-transactions) | -| | Description | -|---------|-------------| -| [Payments and Wallets](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets) | All you need for wallet integrations and payment flows. Minimal API differences to SPL. | -| [Streaming Tokens](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/streaming-tokens/) | Stream mint events using Laserstream | -| [Sign with Privy](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy/) | Light-token operations signed with Privy wallets (Node.js + React) | -| [Sponsor Rent Top-Ups](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sponsor-rent-top-ups/) | Sponsor rent top-ups for users by setting your application as the fee payer | +## Examples ### TypeScript client (`@lightprotocol/compressed-token`) | Operation | Docs guide | GitHub example | | --------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `createMintInterface` | [create-mint](https://zkcompression.com/light-token/cookbook/create-mint) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/create-mint.ts) | +| `createMintInterface` | [create-mint](https://zkcompression.com/light-token/cookbook/create-mint) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/create-mint.ts) \| [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/create-mint.ts) | | `createAtaInterface` | [create-ata](https://zkcompression.com/light-token/cookbook/create-ata) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/create-ata.ts) \| [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/create-ata.ts) | | `mintToInterface` | [mint-to](https://zkcompression.com/light-token/cookbook/mint-to) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/mint-to.ts) \| [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/mint-to.ts) | | `transferInterface` | [transfer-interface](https://zkcompression.com/light-token/cookbook/transfer-interface) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/transfer-interface.ts) \| [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/transfer-interface.ts) | | `approve` | [approve-revoke](https://zkcompression.com/light-token/cookbook/approve-revoke) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-approve.ts) | | `revoke` | [approve-revoke](https://zkcompression.com/light-token/cookbook/approve-revoke) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-revoke.ts) | +| `delegateTransfer` | [transfer-delegated](https://zkcompression.com/light-token/cookbook/transfer-delegated) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-transfer.ts) | | `wrap` | [wrap-unwrap](https://zkcompression.com/light-token/cookbook/wrap-unwrap) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/wrap.ts) \| [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/wrap.ts) | | `unwrap` | [wrap-unwrap](https://zkcompression.com/light-token/cookbook/wrap-unwrap) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/unwrap.ts) \| [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/unwrap.ts) | | `loadAta` | [load-ata](https://zkcompression.com/light-token/cookbook/load-ata) | [action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/load-ata.ts) \| [instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/load-ata.ts) | @@ -240,7 +233,7 @@ Use rent-free PDAs for: user state, app state, nullifiers for payments, DePIN no | | Description | | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | | [counter](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/counter) | Create PDA with sponsored rent-exemption | -| [create-ata](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-ata) | Create associated light-token account | +| [create-ata](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-associated-token-account) | Create associated light-token account | | [create-mint](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-mint) | Create light-token mint | | [create-token-account](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-token-account) | Create light-token account | @@ -250,7 +243,7 @@ CPI calls can be combined with existing and/or light macros. The API is a supers | Operation | Docs guide | GitHub example | | ---------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -| `CreateAssociatedAccountCpi` | [create-ata](https://zkcompression.com/light-token/cookbook/create-ata) | [src](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-ata/src/lib.rs) | +| `CreateAssociatedAccountCpi` | [create-ata](https://zkcompression.com/light-token/cookbook/create-ata) | [src](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-associated-token-account/src/lib.rs) | | `CreateTokenAccountCpi` | [create-token-account](https://zkcompression.com/light-token/cookbook/create-token-account) | [src](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-token-account/src/lib.rs) | | `CreateMintCpi` | [create-mint](https://zkcompression.com/light-token/cookbook/create-mint) | [src](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-mint/src/lib.rs) | | `MintToCpi` | [mint-to](https://zkcompression.com/light-token/cookbook/mint-to) | [src](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/mint-to/src/lib.rs) | diff --git a/snippets/ai-prompts/all-prompts.mdx b/snippets/ai-prompts/all-prompts.mdx index 9a3cce93..a221fc9b 100644 --- a/snippets/ai-prompts/all-prompts.mdx +++ b/snippets/ai-prompts/all-prompts.mdx @@ -29,15 +29,50 @@ import Airdrop from "/snippets/ai-prompts/toolkits/airdrop.mdx"; {/* wallets */} import WalletAdapter from "/snippets/ai-prompts/wallets/wallet-adapter.mdx"; import Privy from "/snippets/ai-prompts/wallets/privy.mdx"; -import SponsorTopUps from "/snippets/ai-prompts/wallets/sponsor-top-ups.mdx"; +import GaslessTransactions from "/snippets/ai-prompts/wallets/gasless-transactions.mdx"; {/* migration */} import V1ToV2Migration from "/snippets/ai-prompts/v1-to-v2-migration.mdx"; + +## Stablecoin Payments + + +Copy the prompt below or view the [guide](/light-token/payments/integration-guide). + + + + +Copy the prompt below or view the [guide](/light-token/wallets/wallet-adapter). + + + + +Copy the prompt below or view the [guide](/light-token/wallets/privy). + + + + +Copy the prompt below or view the [guide](/light-token/wallets/gasless-transactions). + + + + +Copy the prompt below or view the [guide](/pda/compressed-pdas/nullifier-pda). + + + ## Light Token Recipes +## Token Distribution + + +Copy the prompt below or view the [guide](/token-distribution). + + + ## DeFi @@ -67,40 +102,6 @@ Copy the prompt below or view the [guide](/light-token/streaming/tokens). -## Stablecoin Payments - - -Copy the prompt below or view the [guide](/light-token/payments/integration-guide). - - - - -Copy the prompt below or view the [guide](/pda/compressed-pdas/how-to-create-nullifier-pdas). - - - -## Wallets - - -Copy the prompt below or view the [guide](/light-token/wallets/overview). - - - - -Copy the prompt below or view the [guide](/light-token/wallets/wallet-adapter). - - - - -Copy the prompt below or view the [guide](/light-token/wallets/privy). - - - - -Copy the prompt below or view the [guide](/light-token/wallets/sponsor-top-ups). - - - ## Light-PDA @@ -145,9 +146,3 @@ Copy the prompt below or view the [guide](/resources/migration-v1-to-v2). -## Token Distribution - - -Copy the prompt below or view the [guide](/token-distribution). - - diff --git a/snippets/ai-prompts/defi/defi-anchor.mdx b/snippets/ai-prompts/defi/defi-anchor.mdx index e4b73430..6209f4d3 100644 --- a/snippets/ai-prompts/defi/defi-anchor.mdx +++ b/snippets/ai-prompts/defi/defi-anchor.mdx @@ -87,7 +87,7 @@ Violations are bugs. All generated code must satisfy these constraints. - Constructor takes 9 args: \`(amount, decimals, source, destination, authority, fee_payer, light_token_cpi_authority, mint, system_program)\` - Use \`Option\` for SPL interface PDA accounts - Conditionally call \`.with_spl_interface(...)\` when \`spl_interface_pda.is_some()\` -- This enables cross-standard transfers (SPL/T22 source to Light destination) +- This enables cross-standard transfers (SPL/Token 2022 source to Light destination) **Authority pattern:** - Single authority PDA owns all vaults: \`seeds = [AUTHORITY_SEED], bump\` @@ -196,7 +196,7 @@ Violations are bugs. All generated code must satisfy these constraints. - Constructor takes 9 args: `(amount, decimals, source, destination, authority, fee_payer, light_token_cpi_authority, mint, system_program)` - Use `Option` for SPL interface PDA accounts - Conditionally call `.with_spl_interface(...)` when `spl_interface_pda.is_some()` -- This enables cross-standard transfers (SPL/T22 source to Light destination) +- This enables cross-standard transfers (SPL/Token 2022 source to Light destination) **Authority pattern:** - Single authority PDA owns all vaults: `seeds = [AUTHORITY_SEED], bump` diff --git a/snippets/ai-prompts/light-token-prompts.mdx b/snippets/ai-prompts/light-token-prompts.mdx index 1839dbf9..d0215c2a 100644 --- a/snippets/ai-prompts/light-token-prompts.mdx +++ b/snippets/ai-prompts/light-token-prompts.mdx @@ -60,7 +60,7 @@ Copy the prompt below or view the [guide](/light-token/payments/integration-guid
-Copy the prompt below or view the [guide](/pda/compressed-pdas/how-to-create-nullifier-pdas). +Copy the prompt below or view the [guide](/pda/compressed-pdas/nullifier-pda). diff --git a/snippets/ai-prompts/program-cookbook/transfer-checked.mdx b/snippets/ai-prompts/program-cookbook/transfer-checked.mdx index a94d4950..b39a18de 100644 --- a/snippets/ai-prompts/program-cookbook/transfer-checked.mdx +++ b/snippets/ai-prompts/program-cookbook/transfer-checked.mdx @@ -15,6 +15,9 @@ Context: Key CPI struct: \`light_token::instruction::TransferCheckedCpi\` +Note: TransferInterface uses TransferChecked under the hood for Light-to-Light transfers. +Use TransferChecked/TransferCheckedCpi directly only when you need Light-to-Light without interface routing. + ### 1. Index project - Grep \`declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|transfer|decimals|amount\` across src/ - Glob \`**/*.rs\` and \`**/Cargo.toml\` for project structure @@ -73,6 +76,9 @@ Context: Key CPI struct: `light_token::instruction::TransferCheckedCpi` +Note: TransferInterface uses TransferChecked under the hood for Light-to-Light transfers. +Use TransferChecked/TransferCheckedCpi directly only when you need Light-to-Light without interface routing. + ### 1. Index project - Grep `declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|transfer|decimals|amount` across src/ - Glob `**/*.rs` and `**/Cargo.toml` for project structure diff --git a/snippets/ai-prompts/toolkits/nullifiers.mdx b/snippets/ai-prompts/toolkits/nullifiers.mdx index 00a3ba5f..f22b8829 100644 --- a/snippets/ai-prompts/toolkits/nullifiers.mdx +++ b/snippets/ai-prompts/toolkits/nullifiers.mdx @@ -7,7 +7,7 @@ allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, T ## Create rent-free nullifier PDAs to prevent duplicate actions Context: -- Guide: https://zkcompression.com/pda/compressed-pdas/how-to-create-nullifier-pdas +- Guide: https://zkcompression.com/pda/compressed-pdas/nullifier-pda - Skills and resources index: https://zkcompression.com/skill.md - Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/zk-nullifier - Rust crates: light-nullifier-program, light-client @@ -72,7 +72,7 @@ allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, T ## Create rent-free nullifier PDAs to prevent duplicate actions Context: -- Guide: https://zkcompression.com/pda/compressed-pdas/how-to-create-nullifier-pdas +- Guide: https://zkcompression.com/pda/compressed-pdas/nullifier-pda - Skills and resources index: https://zkcompression.com/skill.md - Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/zk-nullifier - Rust crates: light-nullifier-program, light-client diff --git a/snippets/ai-prompts/toolkits/payments.mdx b/snippets/ai-prompts/toolkits/payments.mdx index 56478c39..dacae5aa 100644 --- a/snippets/ai-prompts/toolkits/payments.mdx +++ b/snippets/ai-prompts/toolkits/payments.mdx @@ -10,14 +10,15 @@ Context: - Guide: https://zkcompression.com/light-token/payments/integration-guide - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js -- Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets +- Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments SPL → Light Token API mapping: | Operation | SPL | Light Token | | Receive | getOrCreateAssociatedTokenAccount() | createLoadAtaInstructions() / loadAta() | | Transfer | createTransferInstruction() | createTransferInterfaceInstructions() | +| Delegated Transfer | transfer() (delegate signs) | transferInterface({ owner }) | | Get Balance | getAccount() | getAtaInterface() | | Tx History | getSignaturesForAddress() | getSignaturesForOwnerInterface() | | Wrap SPL | N/A | createWrapInstruction() / wrap() | @@ -80,14 +81,15 @@ Context: - Guide: https://zkcompression.com/light-token/payments/integration-guide - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js -- Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets +- Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments SPL → Light Token API mapping: | Operation | SPL | Light Token | | Receive | getOrCreateAssociatedTokenAccount() | createLoadAtaInstructions() / loadAta() | | Transfer | createTransferInstruction() | createTransferInterfaceInstructions() | +| Delegated Transfer | transfer() (delegate signs) | transferInterface({ owner }) | | Get Balance | getAccount() | getAtaInterface() | | Tx History | getSignaturesForAddress() | getSignaturesForOwnerInterface() | | Wrap SPL | N/A | createWrapInstruction() / wrap() | diff --git a/snippets/ai-prompts/toolkits/wallets.mdx b/snippets/ai-prompts/toolkits/wallets.mdx index 9d2c548b..bd07e643 100644 --- a/snippets/ai-prompts/toolkits/wallets.mdx +++ b/snippets/ai-prompts/toolkits/wallets.mdx @@ -10,9 +10,9 @@ Context: - Guide: https://zkcompression.com/light-token/wallets/overview - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js -- Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets +- Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments SPL → Light Token API mapping: | Operation | SPL | Light Token | @@ -81,9 +81,9 @@ Context: - Guide: https://zkcompression.com/light-token/wallets/overview - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js -- Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets +- Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments SPL → Light Token API mapping: | Operation | SPL | Light Token | diff --git a/snippets/ai-prompts/ts-cookbook/transfer-interface.mdx b/snippets/ai-prompts/ts-cookbook/transfer-interface.mdx index 90717c64..a3d1947c 100644 --- a/snippets/ai-prompts/ts-cookbook/transfer-interface.mdx +++ b/snippets/ai-prompts/ts-cookbook/transfer-interface.mdx @@ -12,11 +12,15 @@ Context: - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js -transferInterface() transfers tokens between token accounts (SPL, Token 2022, or Light Token) in a single call. +transferInterface() transfers tokens in a single call. The recipient parameter is a wallet public key. +The SDK derives and creates the destination ATA internally. All transfers use transferChecked under the hood. - Light Token → Light Token: transfers between Light Token accounts - SPL → Light Token: locks SPL tokens in interface PDA, mints to Light Token account - Light Token → SPL: burns Light Token balance, releases SPL tokens from interface PDA +Edge case: for explicit destination token accounts (PDA/program-owned), use transferToAccountInterface() +or createTransferToAccountInterfaceInstructions(). Most integrations should use transferInterface(). + ### 1. Index project - Grep \`@solana/spl-token|Connection|Keypair|transfer|transferInterface\` across src/ - Glob \`**/*.ts\` for project structure @@ -71,11 +75,15 @@ Context: - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js -transferInterface() transfers tokens between token accounts (SPL, Token 2022, or Light Token) in a single call. +transferInterface() transfers tokens in a single call. The recipient parameter is a wallet public key. +The SDK derives and creates the destination ATA internally. All transfers use transferChecked under the hood. - Light Token → Light Token: transfers between Light Token accounts - SPL → Light Token: locks SPL tokens in interface PDA, mints to Light Token account - Light Token → SPL: burns Light Token balance, releases SPL tokens from interface PDA +Edge case: for explicit destination token accounts (PDA/program-owned), use transferToAccountInterface() +or createTransferToAccountInterfaceInstructions(). Most integrations should use transferInterface(). + ### 1. Index project - Grep `@solana/spl-token|Connection|Keypair|transfer|transferInterface` across src/ - Glob `**/*.ts` for project structure diff --git a/snippets/ai-prompts/wallets/sponsor-top-ups.mdx b/snippets/ai-prompts/wallets/gasless-transactions.mdx similarity index 94% rename from snippets/ai-prompts/wallets/sponsor-top-ups.mdx rename to snippets/ai-prompts/wallets/gasless-transactions.mdx index 9cf76b5f..1df29cfa 100644 --- a/snippets/ai-prompts/wallets/sponsor-top-ups.mdx +++ b/snippets/ai-prompts/wallets/gasless-transactions.mdx @@ -1,16 +1,16 @@ - + {`--- -description: Sponsor rent top-ups for Light Token users +description: Gasless transactions for Light Token users allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression --- -## Sponsor rent top-ups for Light Token users +## Gasless transactions for Light Token users Context: -- Guide: https://zkcompression.com/light-token/wallets/sponsor-top-ups +- Guide: https://zkcompression.com/light-token/wallets/gasless-transactions - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js How rent sponsorship works: @@ -74,17 +74,17 @@ Key APIs: ```text --- -description: Sponsor rent top-ups for Light Token users +description: Gasless transactions for Light Token users allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression --- -## Sponsor rent top-ups for Light Token users +## Gasless transactions for Light Token users Context: -- Guide: https://zkcompression.com/light-token/wallets/sponsor-top-ups +- Guide: https://zkcompression.com/light-token/wallets/gasless-transactions - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js How rent sponsorship works: diff --git a/snippets/ai-prompts/wallets/privy.mdx b/snippets/ai-prompts/wallets/privy.mdx index 9edfebb6..192e875c 100644 --- a/snippets/ai-prompts/wallets/privy.mdx +++ b/snippets/ai-prompts/wallets/privy.mdx @@ -10,10 +10,10 @@ Context: - Guide: https://zkcompression.com/light-token/wallets/privy - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js -- Node.js example: https://github.com/Lightprotocol/examples-light-token/tree/main/privy/nodejs -- React example: https://github.com/Lightprotocol/examples-light-token/tree/main/privy/react +- Node.js example: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy/nodejs +- React example: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy/react SPL → Light Token API mapping: | Operation | SPL | Light Token | @@ -81,10 +81,10 @@ Context: - Guide: https://zkcompression.com/light-token/wallets/privy - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js -- Node.js example: https://github.com/Lightprotocol/examples-light-token/tree/main/privy/nodejs -- React example: https://github.com/Lightprotocol/examples-light-token/tree/main/privy/react +- Node.js example: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy/nodejs +- React example: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy/react SPL → Light Token API mapping: | Operation | SPL | Light Token | diff --git a/snippets/ai-prompts/wallets/wallet-adapter.mdx b/snippets/ai-prompts/wallets/wallet-adapter.mdx index a4175f50..4ab74974 100644 --- a/snippets/ai-prompts/wallets/wallet-adapter.mdx +++ b/snippets/ai-prompts/wallets/wallet-adapter.mdx @@ -10,7 +10,7 @@ Context: - Guide: https://zkcompression.com/light-token/wallets/wallet-adapter - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js - React example: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-wallet-adapter/react @@ -79,7 +79,7 @@ Context: - Guide: https://zkcompression.com/light-token/wallets/wallet-adapter - Skills and resources index: https://zkcompression.com/skill.md - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison -- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets +- Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js - React example: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-wallet-adapter/react diff --git a/snippets/code-samples/code-compare-snippets.jsx b/snippets/code-samples/code-compare-snippets.jsx index b567ec32..5a4ec162 100644 --- a/snippets/code-samples/code-compare-snippets.jsx +++ b/snippets/code-samples/code-compare-snippets.jsx @@ -444,15 +444,16 @@ export const splApproveCode = [ ].join("\n"); export const lightApproveCode = [ - 'import { approve } from "@lightprotocol/compressed-token";', + 'import { approveInterface } from "@lightprotocol/compressed-token";', "", - "const tx = await approve(", + "const tx = await approveInterface(", " rpc,", " payer,", + " tokenAccount,", " mint,", + " delegate,", " amount,", - " owner,", - " delegate", + " owner", ");", ].join("\n"); @@ -469,12 +470,13 @@ export const splRevokeCode = [ ].join("\n"); export const lightRevokeCode = [ - 'import { revoke } from "@lightprotocol/compressed-token";', + 'import { revokeInterface } from "@lightprotocol/compressed-token";', "", - "const tx = await revoke(", + "const tx = await revokeInterface(", " rpc,", " payer,", - " delegatedAccounts,", + " tokenAccount,", + " mint,", " owner", ");", ].join("\n"); diff --git a/snippets/code-snippets/compressed-token/approve/action.mdx b/snippets/code-snippets/compressed-token/approve/action.mdx deleted file mode 100644 index 357a0996..00000000 --- a/snippets/code-snippets/compressed-token/approve/action.mdx +++ /dev/null @@ -1,39 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { createMint, mintTo, approve } from "@lightprotocol/compressed-token"; -import BN from "bn.js"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - // Setup: Create mint and mint tokens - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); - const owner = Keypair.generate(); - await mintTo(rpc, payer, mint, owner.publicKey, payer, 1_000_000_000); - - // Approve delegate - const delegate = Keypair.generate(); - const tx = await approve(rpc, payer, mint, new BN(500_000_000), owner, delegate.publicKey); - - console.log("Mint:", mint.toBase58()); - console.log("Delegate:", delegate.publicKey.toBase58()); - console.log("Tx:", tx); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/compress-spl-account/action.mdx b/snippets/code-snippets/compressed-token/compress-spl-account/action.mdx deleted file mode 100644 index 892342fb..00000000 --- a/snippets/code-snippets/compressed-token/compress-spl-account/action.mdx +++ /dev/null @@ -1,51 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { createMint, compressSplTokenAccount } from "@lightprotocol/compressed-token"; -import { createAssociatedTokenAccount, mintTo, TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - // Setup: Create mint and SPL token account with tokens - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); - const owner = Keypair.generate(); - const tokenAccount = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey, - undefined, - TOKEN_PROGRAM_ID - ); - await mintTo(rpc, payer, mint, tokenAccount, payer, bn(1_000_000_000).toNumber()); - - // Compress entire SPL token account balance - const tx = await compressSplTokenAccount( - rpc, - payer, - mint, - owner, - tokenAccount - ); - - console.log("Mint:", mint.toBase58()); - console.log("Tx:", tx); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/compress/action.mdx b/snippets/code-snippets/compressed-token/compress/action.mdx deleted file mode 100644 index d7d33b58..00000000 --- a/snippets/code-snippets/compressed-token/compress/action.mdx +++ /dev/null @@ -1,39 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { createMint, mintTo, decompress, compress } from "@lightprotocol/compressed-token"; -import { createAssociatedTokenAccount } from "@solana/spl-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - // Setup: Get SPL tokens (needed to compress) - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); - const splAta = await createAssociatedTokenAccount(rpc, payer, mint, payer.publicKey); - await mintTo(rpc, payer, mint, payer.publicKey, payer, 1_000_000_000); - await decompress(rpc, payer, mint, 1_000_000_000, payer, splAta); - - // Compress SPL tokens - const recipient = Keypair.generate(); - const tx = await compress(rpc, payer, mint, 500_000_000, payer, splAta, recipient.publicKey); - - console.log("Mint:", mint.toBase58()); - console.log("Tx:", tx); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/create-mint/action.mdx b/snippets/code-snippets/compressed-token/create-mint/action.mdx deleted file mode 100644 index 186789db..00000000 --- a/snippets/code-snippets/compressed-token/create-mint/action.mdx +++ /dev/null @@ -1,35 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { createMint } from "@lightprotocol/compressed-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - const { mint, transactionSignature } = await createMint( - rpc, - payer, - payer.publicKey, - 9 - ); - - console.log("Mint:", mint.toBase58()); - console.log("Tx:", transactionSignature); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/create-mint/instruction.mdx b/snippets/code-snippets/compressed-token/create-mint/instruction.mdx deleted file mode 100644 index 47be6211..00000000 --- a/snippets/code-snippets/compressed-token/create-mint/instruction.mdx +++ /dev/null @@ -1,72 +0,0 @@ -```typescript -import "dotenv/config"; -import { - Keypair, - ComputeBudgetProgram, - PublicKey, - Transaction, - sendAndConfirmTransaction, -} from "@solana/web3.js"; -import { - createRpc, - getBatchAddressTreeInfo, - selectStateTreeInfo, - LIGHT_TOKEN_PROGRAM_ID, -} from "@lightprotocol/stateless.js"; -import { createMintInstruction } from "@lightprotocol/compressed-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -const COMPRESSED_MINT_SEED = Buffer.from("compressed_mint"); - -function findMintAddress(mintSigner: PublicKey): [PublicKey, number] { - return PublicKey.findProgramAddressSync( - [COMPRESSED_MINT_SEED, mintSigner.toBuffer()], - LIGHT_TOKEN_PROGRAM_ID - ); -} - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); -// localnet: -// const rpc = createRpc(); - -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - const mintSigner = Keypair.generate(); - const addressTreeInfo = getBatchAddressTreeInfo(); - const stateTreeInfo = selectStateTreeInfo(await rpc.getStateTreeInfos()); - const [mintPda] = findMintAddress(mintSigner.publicKey); - - const validityProof = await rpc.getValidityProofV2( - [], - [{ address: mintPda.toBytes(), treeInfo: addressTreeInfo }] - ); - - const ix = createMintInstruction( - mintSigner.publicKey, - 9, - payer.publicKey, - null, - payer.publicKey, - validityProof, - addressTreeInfo, - stateTreeInfo - ); - - const tx = new Transaction().add( - ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }), - ix - ); - const signature = await sendAndConfirmTransaction(rpc, tx, [payer, mintSigner]); - - console.log("Mint:", mintPda.toBase58()); - console.log("Tx:", signature); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/create-token-pool/action.mdx b/snippets/code-snippets/compressed-token/create-token-pool/action.mdx deleted file mode 100644 index 55975443..00000000 --- a/snippets/code-snippets/compressed-token/create-token-pool/action.mdx +++ /dev/null @@ -1,36 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair, PublicKey } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { createSplInterface } from "@lightprotocol/compressed-token"; -import { createMint as createSplMint, TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - // Setup: Create existing SPL mint - const mintKeypair = Keypair.generate(); - await createSplMint(rpc, payer, payer.publicKey, null, 9, mintKeypair, undefined, TOKEN_PROGRAM_ID); - - // Create SPL interface for existing mint - const tx = await createSplInterface(rpc, payer, mintKeypair.publicKey); - - console.log("Mint:", mintKeypair.publicKey.toBase58()); - console.log("Tx:", tx); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/decompress/action.mdx b/snippets/code-snippets/compressed-token/decompress/action.mdx deleted file mode 100644 index 8bd72642..00000000 --- a/snippets/code-snippets/compressed-token/decompress/action.mdx +++ /dev/null @@ -1,37 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { createMint, mintTo, decompress } from "@lightprotocol/compressed-token"; -import { createAssociatedTokenAccount } from "@solana/spl-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - // Setup: Create mint and mint compressed tokens - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); - await mintTo(rpc, payer, mint, payer.publicKey, payer, 1_000_000_000); - const splAta = await createAssociatedTokenAccount(rpc, payer, mint, payer.publicKey); - - // Decompress to SPL tokens - const tx = await decompress(rpc, payer, mint, 500_000_000, payer, splAta); - - console.log("Mint:", mint.toBase58()); - console.log("Tx:", tx); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/merge-token-accounts/action.mdx b/snippets/code-snippets/compressed-token/merge-token-accounts/action.mdx deleted file mode 100644 index eea48df8..00000000 --- a/snippets/code-snippets/compressed-token/merge-token-accounts/action.mdx +++ /dev/null @@ -1,38 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { createMint, mintTo, mergeTokenAccounts } from "@lightprotocol/compressed-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - // Setup: Create mint and mint multiple times to create multiple accounts - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); - const owner = Keypair.generate(); - await mintTo(rpc, payer, mint, owner.publicKey, payer, bn(100_000_000)); - await mintTo(rpc, payer, mint, owner.publicKey, payer, bn(200_000_000)); - await mintTo(rpc, payer, mint, owner.publicKey, payer, bn(300_000_000)); - - // Merge all accounts for owner - const tx = await mergeTokenAccounts(rpc, payer, mint, owner); - - console.log("Mint:", mint.toBase58()); - console.log("Tx:", tx); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/mint-to/action.mdx b/snippets/code-snippets/compressed-token/mint-to/action.mdx deleted file mode 100644 index be0a05b7..00000000 --- a/snippets/code-snippets/compressed-token/mint-to/action.mdx +++ /dev/null @@ -1,36 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { createMint, mintTo } from "@lightprotocol/compressed-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - // Setup: Create mint - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); - - // Mint compressed tokens - const recipient = Keypair.generate(); - const tx = await mintTo(rpc, payer, mint, recipient.publicKey, payer, 1_000_000_000); - - console.log("Mint:", mint.toBase58()); - console.log("Recipient:", recipient.publicKey.toBase58()); - console.log("Tx:", tx); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/mint-to/instruction.mdx b/snippets/code-snippets/compressed-token/mint-to/instruction.mdx deleted file mode 100644 index bad87881..00000000 --- a/snippets/code-snippets/compressed-token/mint-to/instruction.mdx +++ /dev/null @@ -1,70 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair, ComputeBudgetProgram, Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; -import { createRpc, bn, DerivationMode } from "@lightprotocol/stateless.js"; -import { - createMintInterface, - createAtaInterface, - createMintToInterfaceInstruction, - getMintInterface, - getAssociatedTokenAddressInterface, -} from "@lightprotocol/compressed-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); -// localnet: -// const rpc = createRpc(); - -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - const { mint } = await createMintInterface(rpc, payer, payer, null, 9); - - const recipient = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, recipient.publicKey); - const destination = getAssociatedTokenAddressInterface(mint, recipient.publicKey); - - const mintInterface = await getMintInterface(rpc, mint); - - let validityProof; - if (mintInterface.merkleContext) { - validityProof = await rpc.getValidityProofV2( - [ - { - hash: bn(mintInterface.merkleContext.hash), - leafIndex: mintInterface.merkleContext.leafIndex, - treeInfo: mintInterface.merkleContext.treeInfo, - proveByIndex: mintInterface.merkleContext.proveByIndex, - }, - ], - [], - DerivationMode.compressible - ); - } - - const ix = createMintToInterfaceInstruction( - mintInterface, - destination, - payer.publicKey, - payer.publicKey, - 1_000_000_000, - validityProof - ); - - const tx = new Transaction().add( - ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), - ix - ); - const signature = await sendAndConfirmTransaction(rpc, tx, [payer]); - - console.log("Mint:", mint.toBase58()); - console.log("Tx:", signature); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/revoke/action.mdx b/snippets/code-snippets/compressed-token/revoke/action.mdx deleted file mode 100644 index 52dfef41..00000000 --- a/snippets/code-snippets/compressed-token/revoke/action.mdx +++ /dev/null @@ -1,40 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { createMint, mintTo, approve, revoke } from "@lightprotocol/compressed-token"; -import BN from "bn.js"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - // Setup: Create mint, mint tokens, and approve delegate - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); - const owner = Keypair.generate(); - await mintTo(rpc, payer, mint, owner.publicKey, payer, 1_000_000_000); - const delegate = Keypair.generate(); - await approve(rpc, payer, mint, new BN(500_000_000), owner, delegate.publicKey); - - // Get delegated accounts and revoke - const delegatedAccounts = await rpc.getCompressedTokenAccountsByDelegate(delegate.publicKey, { mint }); - const tx = await revoke(rpc, payer, delegatedAccounts.items, owner); - - console.log("Mint:", mint.toBase58()); - console.log("Tx:", tx); -})(); -``` diff --git a/snippets/code-snippets/compressed-token/transfer/action.mdx b/snippets/code-snippets/compressed-token/transfer/action.mdx deleted file mode 100644 index ff7ab410..00000000 --- a/snippets/code-snippets/compressed-token/transfer/action.mdx +++ /dev/null @@ -1,38 +0,0 @@ -```typescript -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { createMint, mintTo, transfer } from "@lightprotocol/compressed-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// localnet: -// const RPC_URL = undefined; -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - // devnet: - const rpc = createRpc(RPC_URL); - // localnet: - // const rpc = createRpc(); - - // Setup: Create mint and mint tokens - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); - const sender = Keypair.generate(); - await mintTo(rpc, payer, mint, sender.publicKey, payer, 1_000_000_000); - - // Transfer compressed tokens - const recipient = Keypair.generate(); - const tx = await transfer(rpc, payer, mint, 500_000_000, sender, recipient.publicKey); - - console.log("Mint:", mint.toBase58()); - console.log("Recipient:", recipient.publicKey.toBase58()); - console.log("Tx:", tx); -})(); -``` diff --git a/snippets/code-snippets/light-token/approve-revoke/approve-action.mdx b/snippets/code-snippets/light-token/approve-revoke/approve-action.mdx index e358f1f3..8fc9b231 100644 --- a/snippets/code-snippets/light-token/approve-revoke/approve-action.mdx +++ b/snippets/code-snippets/light-token/approve-revoke/approve-action.mdx @@ -5,8 +5,9 @@ import { createRpc } from "@lightprotocol/stateless.js"; import { createMintInterface, mintToCompressed, - approve, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { approveInterface } from "@lightprotocol/compressed-token/unified"; import { homedir } from "os"; import { readFileSync } from "fs"; @@ -18,24 +19,30 @@ const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")), - ), + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) ); (async function () { const { mint } = await createMintInterface(rpc, payer, payer, null, 9); - await mintToCompressed(rpc, payer, mint, payer, [{ recipient: payer.publicKey, amount: 1000n }]); + await mintToCompressed(rpc, payer, mint, payer, [ + { recipient: payer.publicKey, amount: 1000n }, + ]); + const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); const delegate = Keypair.generate(); - const tx = await approve( + const tx = await approveInterface( rpc, payer, + senderAta, mint, - 500, - payer, delegate.publicKey, + 500_000, + payer ); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + console.log("Allowance: 500,000 tokens"); console.log("Tx:", tx); })(); ``` diff --git a/snippets/code-snippets/light-token/approve-revoke/revoke-action.mdx b/snippets/code-snippets/light-token/approve-revoke/revoke-action.mdx index cd3d7cc2..1dc888dc 100644 --- a/snippets/code-snippets/light-token/approve-revoke/revoke-action.mdx +++ b/snippets/code-snippets/light-token/approve-revoke/revoke-action.mdx @@ -5,9 +5,12 @@ import { createRpc } from "@lightprotocol/stateless.js"; import { createMintInterface, mintToCompressed, - approve, - revoke, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { + approveInterface, + revokeInterface, +} from "@lightprotocol/compressed-token/unified"; import { homedir } from "os"; import { readFileSync } from "fs"; @@ -19,23 +22,32 @@ const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")), - ), + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) ); (async function () { const { mint } = await createMintInterface(rpc, payer, payer, null, 9); - await mintToCompressed(rpc, payer, mint, payer, [{ recipient: payer.publicKey, amount: 1000n }]); + await mintToCompressed(rpc, payer, mint, payer, [ + { recipient: payer.publicKey, amount: 1000n }, + ]); + const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); const delegate = Keypair.generate(); - await approve(rpc, payer, mint, 500, payer, delegate.publicKey); - - const delegatedAccounts = await rpc.getCompressedTokenAccountsByDelegate( + await approveInterface( + rpc, + payer, + senderAta, + mint, delegate.publicKey, - { mint }, + 500_000, + payer ); - const tx = await revoke(rpc, payer, delegatedAccounts.items, payer); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + const tx = await revokeInterface(rpc, payer, senderAta, mint, payer); + console.log("Revoked all delegate permissions"); console.log("Tx:", tx); })(); ``` diff --git a/snippets/code-snippets/light-token/approve-revoke/rust-client/approve-instruction.mdx b/snippets/code-snippets/light-token/approve-revoke/rust-client/approve-instruction.mdx index 3a60f85c..d8a5c27f 100644 --- a/snippets/code-snippets/light-token/approve-revoke/rust-client/approve-instruction.mdx +++ b/snippets/code-snippets/light-token/approve-revoke/rust-client/approve-instruction.mdx @@ -23,6 +23,7 @@ async fn main() -> Result<(), Box> { delegate: delegate.pubkey(), owner: payer.pubkey(), amount: delegate_amount, + fee_payer: payer.pubkey(), } .instruction()?; diff --git a/snippets/code-snippets/light-token/approve-revoke/rust-client/revoke-instruction.mdx b/snippets/code-snippets/light-token/approve-revoke/rust-client/revoke-instruction.mdx index d18bc441..8aabf3e4 100644 --- a/snippets/code-snippets/light-token/approve-revoke/rust-client/revoke-instruction.mdx +++ b/snippets/code-snippets/light-token/approve-revoke/rust-client/revoke-instruction.mdx @@ -18,6 +18,7 @@ async fn main() -> Result<(), Box> { let revoke_instruction = Revoke { token_account: associated_token_account, owner: payer.pubkey(), + fee_payer: payer.pubkey(), } .instruction()?; diff --git a/snippets/code-snippets/light-token/approve/anchor-program/full-example.mdx b/snippets/code-snippets/light-token/approve/anchor-program/full-example.mdx index ef0eca9d..91bb8562 100644 --- a/snippets/code-snippets/light-token/approve/anchor-program/full-example.mdx +++ b/snippets/code-snippets/light-token/approve/anchor-program/full-example.mdx @@ -18,6 +18,7 @@ pub mod light_token_anchor_approve { owner: ctx.accounts.owner.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), amount, + fee_payer: ctx.accounts.fee_payer.to_account_info(), } .invoke()?; Ok(()) @@ -34,6 +35,8 @@ pub struct ApproveAccounts<'info> { /// CHECK: Validated by light-token CPI pub delegate: AccountInfo<'info>, pub owner: Signer<'info>, + #[account(mut)] + pub fee_payer: Signer<'info>, pub system_program: Program<'info, System>, } ``` @@ -66,6 +69,7 @@ async fn test_approve() { token_account: env.associated_token_account, delegate: delegate.pubkey(), owner: env.payer.pubkey(), + fee_payer: env.payer.pubkey(), system_program: system_program::ID, } .to_account_metas(Some(true)), diff --git a/snippets/code-snippets/light-token/burn/anchor-program/full-example.mdx b/snippets/code-snippets/light-token/burn/anchor-program/full-example.mdx index e3054379..8c2a8c41 100644 --- a/snippets/code-snippets/light-token/burn/anchor-program/full-example.mdx +++ b/snippets/code-snippets/light-token/burn/anchor-program/full-example.mdx @@ -18,8 +18,7 @@ pub mod light_token_anchor_burn { amount, authority: ctx.accounts.authority.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), - max_top_up: None, - fee_payer: None, + fee_payer: ctx.accounts.fee_payer.to_account_info(), } .invoke()?; Ok(()) @@ -37,6 +36,8 @@ pub struct BurnAccounts<'info> { #[account(mut)] pub mint: AccountInfo<'info>, pub authority: Signer<'info>, + #[account(mut)] + pub fee_payer: Signer<'info>, pub system_program: Program<'info, System>, } ``` @@ -67,6 +68,7 @@ async fn test_burn() { source: env.associated_token_account, mint: env.mint_pda, authority: env.payer.pubkey(), + fee_payer: env.payer.pubkey(), system_program: system_program::ID, } .to_account_metas(Some(true)), diff --git a/snippets/code-snippets/light-token/burn/rust-client/instruction.mdx b/snippets/code-snippets/light-token/burn/rust-client/instruction.mdx index a43ab642..68a7c587 100644 --- a/snippets/code-snippets/light-token/burn/rust-client/instruction.mdx +++ b/snippets/code-snippets/light-token/burn/rust-client/instruction.mdx @@ -23,8 +23,7 @@ async fn main() -> Result<(), Box> { mint, amount: burn_amount, authority: payer.pubkey(), - max_top_up: None, - fee_payer: None, + fee_payer: payer.pubkey(), } .instruction()?; diff --git a/snippets/code-snippets/light-token/create-ata/action.mdx b/snippets/code-snippets/light-token/create-ata/action.mdx index c971fcf9..8bbe8231 100644 --- a/snippets/code-snippets/light-token/create-ata/action.mdx +++ b/snippets/code-snippets/light-token/create-ata/action.mdx @@ -10,10 +10,10 @@ import { homedir } from "os"; import { readFileSync } from "fs"; // devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); // localnet: -// const rpc = createRpc(); +const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( diff --git a/snippets/code-snippets/light-token/create-ata/anchor-macro/full-example.mdx b/snippets/code-snippets/light-token/create-ata/anchor-macro/full-example.mdx index f8d20b9e..89b73bc7 100644 --- a/snippets/code-snippets/light-token/create-ata/anchor-macro/full-example.mdx +++ b/snippets/code-snippets/light-token/create-ata/anchor-macro/full-example.mdx @@ -3,11 +3,11 @@ #![allow(deprecated)] use anchor_lang::prelude::*; -use light_compressible::CreateAccountsProof; -use light_sdk::derive_light_cpi_signer; -use light_sdk_macros::{light_program, LightAccounts}; -use light_sdk_types::{CpiSigner, LIGHT_TOKEN_PROGRAM_ID}; -use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR}; +use light_account::{ + derive_light_cpi_signer, light_program, CreateAccountsProof, CpiSigner, LightAccounts, + LIGHT_TOKEN_PROGRAM_ID, +}; +use light_token::instruction::{LIGHT_TOKEN_CONFIG, LIGHT_TOKEN_RENT_SPONSOR}; declare_id!("CLsn9MTFv97oMTsujRoQAw1u2rSm2HnKtGuWUbbc8Jfn"); @@ -31,7 +31,6 @@ pub mod light_token_macro_create_associated_token_account { #[derive(AnchorSerialize, AnchorDeserialize, Clone)] pub struct CreateAssociatedTokenAccountParams { pub create_accounts_proof: CreateAccountsProof, - pub associated_token_account_bump: u8, } #[derive(Accounts, LightAccounts)] @@ -48,12 +47,12 @@ pub struct CreateAssociatedTokenAccount<'info> { /// CHECK: Validated by light_account macro #[account(mut)] - #[light_account(init, associated_token::authority = associated_token_account_owner, associated_token::mint = associated_token_account_mint, associated_token::bump = params.associated_token_account_bump)] + #[light_account(init, associated_token::authority = associated_token_account_owner, associated_token::mint = associated_token_account_mint)] pub associated_token_account: UncheckedAccount<'info>, /// CHECK: Validated by address constraint - #[account(address = COMPRESSIBLE_CONFIG_V1)] - pub light_token_compressible_config: AccountInfo<'info>, + #[account(address = LIGHT_TOKEN_CONFIG)] + pub light_token_config: AccountInfo<'info>, /// CHECK: Validated by address constraint #[account(mut, address = LIGHT_TOKEN_RENT_SPONSOR)] @@ -74,8 +73,8 @@ use light_program_test::{ program_test::{setup_mock_program_data, LightProgramTest}, ProgramTestConfig, Rpc, }; -use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; -use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; +use light_account::{derive_rent_sponsor_pda, LIGHT_TOKEN_PROGRAM_ID}; +use light_token::instruction::{LIGHT_TOKEN_CONFIG, LIGHT_TOKEN_RENT_SPONSOR}; use solana_instruction::Instruction; use solana_signer::Signer; use test_utils::create_mint; @@ -95,11 +94,13 @@ async fn test_create_associated_token_account() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + let (rent_sponsor, _) = derive_rent_sponsor_pda(&program_id); + let (init_config_ix, _config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + rent_sponsor, payer.pubkey(), ) .build(); @@ -114,7 +115,7 @@ async fn test_create_associated_token_account() { let associated_token_account_owner = payer.pubkey(); // Derive the associated token account address using Light Token SDK's derivation - let (associated_token_account, associated_token_account_bump) = light_token::instruction::derive_token_ata(&associated_token_account_owner, &mint); + let associated_token_account = light_token::instruction::derive_token_ata(&associated_token_account_owner, &mint); // Get proof (no PDA accounts for associated token account-only instruction) let proof_result = get_create_accounts_proof(&rpc, &program_id, vec![]) @@ -127,8 +128,8 @@ async fn test_create_associated_token_account() { associated_token_account_mint: mint, associated_token_account_owner, associated_token_account, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: RENT_SPONSOR, + light_token_config: LIGHT_TOKEN_CONFIG, + light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), system_program: solana_sdk::system_program::ID, }; @@ -136,7 +137,6 @@ async fn test_create_associated_token_account() { let instruction_data = light_token_macro_create_associated_token_account::instruction::CreateAssociatedTokenAccount { params: CreateAssociatedTokenAccountParams { create_accounts_proof: proof_result.create_accounts_proof, - associated_token_account_bump, }, }; diff --git a/snippets/code-snippets/light-token/create-ata/anchor-program/full-example.mdx b/snippets/code-snippets/light-token/create-ata/anchor-program/full-example.mdx index 6a63b452..86019ec4 100644 --- a/snippets/code-snippets/light-token/create-ata/anchor-program/full-example.mdx +++ b/snippets/code-snippets/light-token/create-ata/anchor-program/full-example.mdx @@ -11,13 +11,12 @@ declare_id!("35MukgdfpNUbPMhTmEk63ECV8vjgpNVFRH9nP8ovMN58"); pub mod light_token_anchor_create_associated_token_account { use super::*; - pub fn create_associated_token_account(ctx: Context, bump: u8, idempotent: bool) -> Result<()> { + pub fn create_associated_token_account(ctx: Context, idempotent: bool) -> Result<()> { let cpi = CreateAssociatedAccountCpi { payer: ctx.accounts.payer.to_account_info(), owner: ctx.accounts.owner.to_account_info(), mint: ctx.accounts.mint.to_account_info(), ata: ctx.accounts.associated_token_account.to_account_info(), - bump, }; if idempotent { @@ -138,7 +137,7 @@ async fn test_create_associated_token_account() { // You can use light, spl, t22 mints to create a light token associated token account. // Derive associated token account address and bump - let (associated_token_account, associated_token_account_bump) = derive_token_ata(&payer.pubkey(), &mint_pda); + let associated_token_account = derive_token_ata(&payer.pubkey(), &mint_pda); // Call the anchor program to create associated token account let compressible_config = config_pda(); @@ -158,7 +157,6 @@ async fn test_create_associated_token_account() { } .to_account_metas(Some(true)), data: CreateAssociatedTokenAccount { - bump: associated_token_account_bump, idempotent: false, } .data(), diff --git a/snippets/code-snippets/light-token/create-ata/instruction.mdx b/snippets/code-snippets/light-token/create-ata/instruction.mdx index c507ff05..55dea6d5 100644 --- a/snippets/code-snippets/light-token/create-ata/instruction.mdx +++ b/snippets/code-snippets/light-token/create-ata/instruction.mdx @@ -5,7 +5,7 @@ import { Transaction, sendAndConfirmTransaction, } from "@solana/web3.js"; -import { createRpc, LIGHT_TOKEN_PROGRAM_ID } from "@lightprotocol/stateless.js"; +import { createRpc, CTOKEN_PROGRAM_ID } from "@lightprotocol/stateless.js"; import { createMintInterface, createAssociatedTokenAccountInterfaceInstruction, @@ -22,8 +22,8 @@ const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")), - ), + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) ); (async function () { @@ -32,7 +32,7 @@ const payer = Keypair.fromSecretKey( const owner = Keypair.generate(); const associatedToken = getAssociatedTokenAddressInterface( mint, - owner.publicKey, + owner.publicKey ); const ix = createAssociatedTokenAccountInterfaceInstruction( @@ -40,7 +40,7 @@ const payer = Keypair.fromSecretKey( associatedToken, owner.publicKey, mint, - LIGHT_TOKEN_PROGRAM_ID, + CTOKEN_PROGRAM_ID ); const tx = new Transaction().add(ix); diff --git a/snippets/code-snippets/light-token/create-mint/action.mdx b/snippets/code-snippets/light-token/create-mint/action.mdx index 24a856a8..0607d20e 100644 --- a/snippets/code-snippets/light-token/create-mint/action.mdx +++ b/snippets/code-snippets/light-token/create-mint/action.mdx @@ -2,15 +2,18 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc } from "@lightprotocol/stateless.js"; -import { createMintInterface, createTokenMetadata } from "@lightprotocol/compressed-token"; +import { + createMintInterface, + createTokenMetadata, +} from "@lightprotocol/compressed-token"; import { homedir } from "os"; import { readFileSync } from "fs"; // devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); // localnet: -// const rpc = createRpc(); +const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( @@ -25,10 +28,14 @@ const payer = Keypair.fromSecretKey( payer, null, 9, - undefined, - undefined, - undefined, - createTokenMetadata("Example Token", "EXT", "https://example.com/metadata.json") + undefined, // keypair + undefined, // confirmOptions (default) + undefined, // programId (CTOKEN_PROGRAM_ID) + createTokenMetadata( + "Example Token", + "EXT", + "https://example.com/metadata.json" + ) ); console.log("Mint:", mint.toBase58()); diff --git a/snippets/code-snippets/light-token/create-mint/anchor-macro/full-example.mdx b/snippets/code-snippets/light-token/create-mint/anchor-macro/full-example.mdx index 25a7c0bb..cd4f5a0e 100644 --- a/snippets/code-snippets/light-token/create-mint/anchor-macro/full-example.mdx +++ b/snippets/code-snippets/light-token/create-mint/anchor-macro/full-example.mdx @@ -3,10 +3,9 @@ #![allow(deprecated)] use anchor_lang::prelude::*; -use light_compressible::CreateAccountsProof; -use light_sdk::derive_light_cpi_signer; -use light_sdk_macros::{light_program, LightAccounts}; -use light_sdk_types::CpiSigner; +use light_account::{ + derive_light_cpi_signer, light_program, CreateAccountsProof, CpiSigner, LightAccounts, +}; declare_id!("HVmVqSJyMejBeUigePMSfX4aENJzCGHNxAJuT2PDMPRx"); @@ -59,12 +58,12 @@ pub struct CreateMint<'info> { /// CHECK: Compression config PDA pub compression_config: AccountInfo<'info>, - /// CHECK: Light Token compressible config - pub light_token_compressible_config: AccountInfo<'info>, + /// CHECK: Light Token config + pub light_token_config: AccountInfo<'info>, /// CHECK: Rent sponsor #[account(mut)] - pub rent_sponsor: AccountInfo<'info>, + pub light_token_rent_sponsor: AccountInfo<'info>, /// CHECK: Light Token program pub light_token_program: AccountInfo<'info>, @@ -108,12 +107,12 @@ pub struct CreateMintWithMetadata<'info> { /// CHECK: Compression config PDA pub compression_config: AccountInfo<'info>, - /// CHECK: Light Token compressible config - pub light_token_compressible_config: AccountInfo<'info>, + /// CHECK: Light Token config + pub light_token_config: AccountInfo<'info>, /// CHECK: Rent sponsor #[account(mut)] - pub rent_sponsor: AccountInfo<'info>, + pub light_token_rent_sponsor: AccountInfo<'info>, /// CHECK: Light Token program pub light_token_program: AccountInfo<'info>, @@ -156,8 +155,8 @@ use light_program_test::{ program_test::{setup_mock_program_data, LightProgramTest}, ProgramTestConfig, Rpc, }; -use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; -use light_token::instruction::{find_mint_address, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; +use light_account::{derive_rent_sponsor_pda, LIGHT_TOKEN_PROGRAM_ID}; +use light_token::instruction::{find_mint_address, LIGHT_TOKEN_CONFIG, LIGHT_TOKEN_RENT_SPONSOR}; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; @@ -176,11 +175,13 @@ async fn setup() -> (light_program_test::LightProgramTest, Keypair, Pubkey, Pubk let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + let (rent_sponsor, _) = derive_rent_sponsor_pda(&program_id); + let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + rent_sponsor, payer.pubkey(), ) .build(); @@ -219,10 +220,10 @@ async fn test_create_mint() { mint_signer: mint_signer_pda, mint: mint_pda, compression_config: config_pda, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: RENT_SPONSOR, + light_token_config: LIGHT_TOKEN_CONFIG, + light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), - light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), + light_token_cpi_authority: light_token::constants::LIGHT_TOKEN_CPI_AUTHORITY, system_program: solana_sdk::system_program::ID, }; @@ -291,10 +292,10 @@ async fn test_create_mint_with_metadata() { mint_signer: mint_signer_pda, mint: mint_pda, compression_config: config_pda, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: RENT_SPONSOR, + light_token_config: LIGHT_TOKEN_CONFIG, + light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), - light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), + light_token_cpi_authority: light_token::constants::LIGHT_TOKEN_CPI_AUTHORITY, system_program: solana_sdk::system_program::ID, }; diff --git a/snippets/code-snippets/light-token/create-mint/instruction.mdx b/snippets/code-snippets/light-token/create-mint/instruction.mdx index 9789b974..000a6cfe 100644 --- a/snippets/code-snippets/light-token/create-mint/instruction.mdx +++ b/snippets/code-snippets/light-token/create-mint/instruction.mdx @@ -25,7 +25,7 @@ const COMPRESSED_MINT_SEED = Buffer.from("compressed_mint"); function findMintAddress(mintSigner: PublicKey): [PublicKey, number] { return PublicKey.findProgramAddressSync( [COMPRESSED_MINT_SEED, mintSigner.toBuffer()], - CTOKEN_PROGRAM_ID, + CTOKEN_PROGRAM_ID ); } @@ -37,8 +37,8 @@ const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")), - ), + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) ); (async function () { @@ -49,7 +49,7 @@ const payer = Keypair.fromSecretKey( const validityProof = await rpc.getValidityProofV2( [], - [{ address: mintPda.toBytes(), treeInfo: addressTreeInfo }], + [{ address: mintPda.toBytes(), treeInfo: addressTreeInfo }] ); const ix = createMintInstruction( @@ -64,13 +64,13 @@ const payer = Keypair.fromSecretKey( createTokenMetadata( "Example Token", "EXT", - "https://example.com/metadata.json", - ), + "https://example.com/metadata.json" + ) ); const tx = new Transaction().add( ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }), - ix, + ix ); const signature = await sendAndConfirmTransaction(rpc, tx, [ payer, diff --git a/snippets/code-snippets/light-token/create-mint/rust-client/instruction.mdx b/snippets/code-snippets/light-token/create-mint/rust-client/instruction.mdx index f56e0885..bce376d0 100644 --- a/snippets/code-snippets/light-token/create-mint/rust-client/instruction.mdx +++ b/snippets/code-snippets/light-token/create-mint/rust-client/instruction.mdx @@ -18,7 +18,7 @@ use solana_sdk::{signature::Keypair, signer::Signer}; #[tokio::main] async fn main() -> Result<(), Box> { - let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)).await?; + let mut rpc = LightProgramTest::new(ProgramTestConfig::new(false, None)).await?; let payer = rpc.get_payer().insecure_clone(); let mint_seed = Keypair::new(); diff --git a/snippets/code-snippets/light-token/create-token-account/anchor-macro/full-example.mdx b/snippets/code-snippets/light-token/create-token-account/anchor-macro/full-example.mdx index e7164530..6287b236 100644 --- a/snippets/code-snippets/light-token/create-token-account/anchor-macro/full-example.mdx +++ b/snippets/code-snippets/light-token/create-token-account/anchor-macro/full-example.mdx @@ -3,11 +3,10 @@ #![allow(deprecated)] use anchor_lang::prelude::*; -use light_compressible::CreateAccountsProof; -use light_sdk::derive_light_cpi_signer; -use light_sdk_macros::{light_program, LightAccounts}; -use light_sdk_types::CpiSigner; -use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR}; +use light_account::{ + derive_light_cpi_signer, light_program, CreateAccountsProof, CpiSigner, LightAccounts, +}; +use light_token::instruction::{LIGHT_TOKEN_CONFIG, LIGHT_TOKEN_RENT_SPONSOR}; declare_id!("9p5BUDtVmRRJqp2sN73ZUZDbaYtYvEWuxzrHH3A2ni9y"); @@ -47,16 +46,17 @@ pub struct CreateTokenVault<'info> { )] #[light_account( init, - token::authority = [VAULT_SEED, self.mint.key()], + token::seeds = [VAULT_SEED, self.mint.key()], token::mint = mint, token::owner = vault_authority, + token::owner_seeds = [VAULT_AUTH_SEED], token::bump = params.vault_bump )] pub vault: UncheckedAccount<'info>, /// CHECK: Validated by address constraint - #[account(address = COMPRESSIBLE_CONFIG_V1)] - pub light_token_compressible_config: AccountInfo<'info>, + #[account(address = LIGHT_TOKEN_CONFIG)] + pub light_token_config: AccountInfo<'info>, /// CHECK: Validated by address constraint #[account(mut, address = LIGHT_TOKEN_RENT_SPONSOR)] @@ -104,11 +104,11 @@ use test_utils::create_mint; /// 3. The vault has the correct owner and mint #[tokio::test] async fn test_create_token_vault() { - use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; + use light_token::instruction::{LIGHT_TOKEN_CONFIG, LIGHT_TOKEN_RENT_SPONSOR}; use light_token_macro_create_token_account::{ CreateTokenVaultParams, VAULT_AUTH_SEED, VAULT_SEED, }; - use light_token_types::CPI_AUTHORITY_PDA; + use light_token::constants::LIGHT_TOKEN_CPI_AUTHORITY; let program_id = light_token_macro_create_token_account::ID; let config = ProgramTestConfig::new_v2( @@ -138,9 +138,9 @@ async fn test_create_token_vault() { mint, vault_authority, vault, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: RENT_SPONSOR, - light_token_cpi_authority: CPI_AUTHORITY_PDA.into(), + light_token_config: LIGHT_TOKEN_CONFIG, + light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + light_token_cpi_authority: LIGHT_TOKEN_CPI_AUTHORITY, light_token_program: LIGHT_TOKEN_PROGRAM_ID, system_program: solana_sdk::system_program::ID, }; diff --git a/snippets/code-snippets/light-token/delegate-transfer/action.mdx b/snippets/code-snippets/light-token/delegate-transfer/action.mdx new file mode 100644 index 00000000..a6ef57a5 --- /dev/null +++ b/snippets/code-snippets/light-token/delegate-transfer/action.mdx @@ -0,0 +1,67 @@ +```typescript +import "dotenv/config"; +import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + mintToCompressed, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token"; +import { + approveInterface, + transferInterface, +} from "@lightprotocol/compressed-token/unified"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); +// localnet: +const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + await mintToCompressed(rpc, payer, mint, payer, [ + { recipient: payer.publicKey, amount: 1000n }, + ]); + + const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); + const delegate = Keypair.generate(); + const recipient = Keypair.generate(); + + // Approve delegate + await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + // Delegate transfers tokens on behalf of the owner + const tx = await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + delegate, + 200_000, + undefined, + undefined, + { owner: payer.publicKey } + ); + + console.log("Delegated transfer:", tx); +})(); +``` diff --git a/snippets/code-snippets/light-token/sponsor-rent-top-ups/rust-instruction.mdx b/snippets/code-snippets/light-token/gasless-transactions/rust-instruction.mdx similarity index 100% rename from snippets/code-snippets/light-token/sponsor-rent-top-ups/rust-instruction.mdx rename to snippets/code-snippets/light-token/gasless-transactions/rust-instruction.mdx diff --git a/snippets/code-snippets/light-token/sponsor-rent-top-ups/ts-instruction.mdx b/snippets/code-snippets/light-token/gasless-transactions/ts-instruction.mdx similarity index 100% rename from snippets/code-snippets/light-token/sponsor-rent-top-ups/ts-instruction.mdx rename to snippets/code-snippets/light-token/gasless-transactions/ts-instruction.mdx diff --git a/snippets/code-snippets/light-token/load-ata/action.mdx b/snippets/code-snippets/light-token/load-ata/action.mdx index 919ef14c..fa1675d2 100644 --- a/snippets/code-snippets/light-token/load-ata/action.mdx +++ b/snippets/code-snippets/light-token/load-ata/action.mdx @@ -12,10 +12,10 @@ import { homedir } from "os"; import { readFileSync } from "fs"; // devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); // localnet: -// const rpc = createRpc(); +const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( @@ -24,14 +24,19 @@ const payer = Keypair.fromSecretKey( ); (async function () { - // Setup: Get compressed tokens (cold storage) + // Inactive Light Tokens are cryptographically preserved on the Solana ledger + // as compressed tokens (cold storage) + // Setup: Get compressed tokens in light-token associated token account const { mint } = await createMintInterface(rpc, payer, payer, null, 9); await mintToCompressed(rpc, payer, mint, payer, [ { recipient: payer.publicKey, amount: 1000n }, ]); - // Load compressed tokens to hot balance - const lightTokenAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); + // Load compressed tokens to light associated token account (hot balance) + const lightTokenAta = getAssociatedTokenAddressInterface( + mint, + payer.publicKey + ); const tx = await loadAta(rpc, lightTokenAta, payer, mint, payer); console.log("Tx:", tx); diff --git a/snippets/code-snippets/light-token/load-ata/instruction.mdx b/snippets/code-snippets/light-token/load-ata/instruction.mdx index 7d10f34e..7b2c9c72 100644 --- a/snippets/code-snippets/light-token/load-ata/instruction.mdx +++ b/snippets/code-snippets/light-token/load-ata/instruction.mdx @@ -23,8 +23,8 @@ const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")), - ), + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) ); (async function () { @@ -32,29 +32,31 @@ const payer = Keypair.fromSecretKey( // as compressed tokens (cold storage) // Setup: Get compressed tokens in light-token associated token account const { mint } = await createMintInterface(rpc, payer, payer, null, 9); - await mintToCompressed(rpc, payer, mint, payer, [{ recipient: payer.publicKey, amount: 1000n }]); + await mintToCompressed(rpc, payer, mint, payer, [ + { recipient: payer.publicKey, amount: 1000n }, + ]); const lightTokenAta = getAssociatedTokenAddressInterface( mint, - payer.publicKey, + payer.publicKey ); - // Load compressed tokens to light associated token account (hot balance). Usually one tx. Empty = noop. - const instructions = await createLoadAtaInstructions( + // Load compressed tokens to light associated token account (hot balance) + const ixs = await createLoadAtaInstructions( rpc, lightTokenAta, payer.publicKey, mint, - payer.publicKey, + payer.publicKey ); - if (instructions.length === 0) return console.log("Nothing to load"); + if (ixs.length === 0) return console.log("Nothing to load"); - for (const ixs of instructions) { - const { blockhash } = await rpc.getLatestBlockhash(); - const tx = buildAndSignTx(ixs, payer, blockhash); - const sig = await sendAndConfirmTx(rpc, tx); - console.log("Tx:", sig); + for (const batch of ixs) { + const blockhash = await rpc.getLatestBlockhash(); + const tx = buildAndSignTx(batch, payer, blockhash.blockhash); + const signature = await sendAndConfirmTx(rpc, tx); + console.log("Tx:", signature); } })(); ``` diff --git a/snippets/code-snippets/light-token/mint-to/action.mdx b/snippets/code-snippets/light-token/mint-to/action.mdx index 9aa231b6..ec43e0f1 100644 --- a/snippets/code-snippets/light-token/mint-to/action.mdx +++ b/snippets/code-snippets/light-token/mint-to/action.mdx @@ -12,10 +12,10 @@ import { homedir } from "os"; import { readFileSync } from "fs"; // devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); // localnet: -// const rpc = createRpc(); +const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( @@ -29,8 +29,18 @@ const payer = Keypair.fromSecretKey( const recipient = Keypair.generate(); await createAtaInterface(rpc, payer, mint, recipient.publicKey); - const destination = getAssociatedTokenAddressInterface(mint, recipient.publicKey); - const tx = await mintToInterface(rpc, payer, mint, destination, payer, 1_000_000_000); + const destination = getAssociatedTokenAddressInterface( + mint, + recipient.publicKey + ); + const tx = await mintToInterface( + rpc, + payer, + mint, + destination, + payer, + 1_000_000_000 + ); console.log("Tx:", tx); })(); diff --git a/snippets/code-snippets/light-token/mint-to/anchor-program/full-example.mdx b/snippets/code-snippets/light-token/mint-to/anchor-program/full-example.mdx index 79393ab2..82f6509e 100644 --- a/snippets/code-snippets/light-token/mint-to/anchor-program/full-example.mdx +++ b/snippets/code-snippets/light-token/mint-to/anchor-program/full-example.mdx @@ -18,8 +18,7 @@ pub mod light_token_anchor_mint_to { amount, authority: ctx.accounts.authority.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), - max_top_up: None, - fee_payer: None, + fee_payer: ctx.accounts.fee_payer.to_account_info(), } .invoke()?; Ok(()) @@ -37,6 +36,8 @@ pub struct MintToAccounts<'info> { #[account(mut)] pub destination: AccountInfo<'info>, pub authority: Signer<'info>, + #[account(mut)] + pub fee_payer: Signer<'info>, pub system_program: Program<'info, System>, } ``` @@ -63,6 +64,7 @@ async fn test_mint_to() { mint: env.mint_pda, destination: env.associated_token_account, authority: env.payer.pubkey(), + fee_payer: env.payer.pubkey(), system_program: system_program::ID, } .to_account_metas(Some(true)), diff --git a/snippets/code-snippets/light-token/mint-to/instruction.mdx b/snippets/code-snippets/light-token/mint-to/instruction.mdx index fcaf80d0..7a2ccc63 100644 --- a/snippets/code-snippets/light-token/mint-to/instruction.mdx +++ b/snippets/code-snippets/light-token/mint-to/instruction.mdx @@ -25,8 +25,8 @@ const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")), - ), + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) ); (async function () { @@ -36,7 +36,7 @@ const payer = Keypair.fromSecretKey( await createAtaInterface(rpc, payer, mint, recipient.publicKey); const destination = getAssociatedTokenAddressInterface( mint, - recipient.publicKey, + recipient.publicKey ); const mintInterface = await getMintInterface(rpc, mint); @@ -53,7 +53,7 @@ const payer = Keypair.fromSecretKey( }, ], [], - DerivationMode.compressible, + DerivationMode.compressible ); } @@ -63,12 +63,12 @@ const payer = Keypair.fromSecretKey( payer.publicKey, payer.publicKey, 1_000_000_000, - validityProof, + validityProof ); const tx = new Transaction().add( ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), - ix, + ix ); const signature = await sendAndConfirmTransaction(rpc, tx, [payer]); diff --git a/snippets/code-snippets/light-token/mint-to/rust-client/instruction.mdx b/snippets/code-snippets/light-token/mint-to/rust-client/instruction.mdx index cd9f6d90..c3ddcf2f 100644 --- a/snippets/code-snippets/light-token/mint-to/rust-client/instruction.mdx +++ b/snippets/code-snippets/light-token/mint-to/rust-client/instruction.mdx @@ -23,8 +23,7 @@ async fn main() -> Result<(), Box> { destination: associated_token_account, amount: mint_amount, authority: payer.pubkey(), - max_top_up: None, - fee_payer: None, + fee_payer: payer.pubkey(), } .instruction()?; diff --git a/snippets/code-snippets/light-token/revoke/anchor-program/full-example.mdx b/snippets/code-snippets/light-token/revoke/anchor-program/full-example.mdx index ff84e805..5464969c 100644 --- a/snippets/code-snippets/light-token/revoke/anchor-program/full-example.mdx +++ b/snippets/code-snippets/light-token/revoke/anchor-program/full-example.mdx @@ -16,6 +16,7 @@ pub mod light_token_anchor_revoke { token_account: ctx.accounts.token_account.to_account_info(), owner: ctx.accounts.owner.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), + fee_payer: ctx.accounts.fee_payer.to_account_info(), } .invoke()?; Ok(()) @@ -30,6 +31,8 @@ pub struct RevokeAccounts<'info> { #[account(mut)] pub token_account: AccountInfo<'info>, pub owner: Signer<'info>, + #[account(mut)] + pub fee_payer: Signer<'info>, pub system_program: Program<'info, System>, } ``` @@ -58,6 +61,7 @@ async fn test_revoke() { delegate: delegate.pubkey(), owner: env.payer.pubkey(), amount: 500_000, + fee_payer: env.payer.pubkey(), } .instruction() .unwrap(); @@ -74,6 +78,7 @@ async fn test_revoke() { light_token_program: LIGHT_TOKEN_PROGRAM_ID, token_account: env.associated_token_account, owner: env.payer.pubkey(), + fee_payer: env.payer.pubkey(), system_program: system_program::ID, } .to_account_metas(Some(true)), diff --git a/snippets/code-snippets/light-token/transfer-checked/anchor-program/full-example.mdx b/snippets/code-snippets/light-token/transfer-checked/anchor-program/full-example.mdx index cd870a7c..d933e078 100644 --- a/snippets/code-snippets/light-token/transfer-checked/anchor-program/full-example.mdx +++ b/snippets/code-snippets/light-token/transfer-checked/anchor-program/full-example.mdx @@ -24,8 +24,7 @@ pub mod light_token_anchor_transfer_checked { decimals, authority: ctx.accounts.authority.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), - max_top_up: None, - fee_payer: None, + fee_payer: ctx.accounts.fee_payer.to_account_info(), } .invoke()?; Ok(()) @@ -45,6 +44,8 @@ pub struct TransferCheckedAccounts<'info> { #[account(mut)] pub destination: AccountInfo<'info>, pub authority: Signer<'info>, + #[account(mut)] + pub fee_payer: Signer<'info>, pub system_program: Program<'info, System>, } ``` @@ -82,6 +83,7 @@ async fn test_transfer_checked() { mint: env.mint_pda, destination: dest_associated_token_account, authority: env.payer.pubkey(), + fee_payer: env.payer.pubkey(), system_program: system_program::ID, } .to_account_metas(Some(true)), diff --git a/snippets/code-snippets/light-token/transfer-checked/native-program/full-example.mdx b/snippets/code-snippets/light-token/transfer-checked/native-program/full-example.mdx index 646df352..97f8e5d9 100644 --- a/snippets/code-snippets/light-token/transfer-checked/native-program/full-example.mdx +++ b/snippets/code-snippets/light-token/transfer-checked/native-program/full-example.mdx @@ -24,7 +24,7 @@ pub fn transfer_checked_invoke( let amount = u64::from_le_bytes(data[0..8].try_into().unwrap()); let decimals = data[8]; - // TransferChecked validates decimals. Only for Light->Light. Use TransferInterface for SPL/T22 + // TransferChecked validates decimals. Only for Light->Light. Use TransferInterface for SPL/Token 2022 TransferCheckedCpi { source: source.clone(), mint: mint.clone(), diff --git a/snippets/code-snippets/light-token/transfer-interface/action.mdx b/snippets/code-snippets/light-token/transfer-interface/action.mdx index 46e33578..a8bb21d2 100644 --- a/snippets/code-snippets/light-token/transfer-interface/action.mdx +++ b/snippets/code-snippets/light-token/transfer-interface/action.mdx @@ -13,10 +13,10 @@ import { homedir } from "os"; import { readFileSync } from "fs"; // devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); // localnet: -// const rpc = createRpc(); +const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( @@ -29,13 +29,30 @@ const payer = Keypair.fromSecretKey( const sender = Keypair.generate(); await createAtaInterface(rpc, payer, mint, sender.publicKey); - const senderAta = getAssociatedTokenAddressInterface(mint, sender.publicKey); + const senderAta = getAssociatedTokenAddressInterface( + mint, + sender.publicKey + ); await mintToInterface(rpc, payer, mint, senderAta, payer, 1_000_000_000); const recipient = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, recipient.publicKey); + const recipientAta = getAssociatedTokenAddressInterface( + mint, + recipient.publicKey + ); - // destination is a wallet pubkey; the action creates the recipient ATA. - const tx = await transferInterface(rpc, payer, senderAta, mint, recipient.publicKey, sender, 500_000_000); + // Transfer tokens between light-token associated token accounts + // destination is recipient wallet; transferInterface creates recipient ATA idempotently + const tx = await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + sender, + 500_000_000 + ); console.log("Tx:", tx); })(); diff --git a/snippets/code-snippets/light-token/transfer-interface/anchor-program/full-example.mdx b/snippets/code-snippets/light-token/transfer-interface/anchor-program/full-example.mdx index 1f9ba0fd..a315ed2e 100644 --- a/snippets/code-snippets/light-token/transfer-interface/anchor-program/full-example.mdx +++ b/snippets/code-snippets/light-token/transfer-interface/anchor-program/full-example.mdx @@ -25,13 +25,14 @@ pub mod light_token_anchor_transfer_interface { ctx.accounts.authority.to_account_info(), ctx.accounts.payer.to_account_info(), ctx.accounts.cpi_authority.to_account_info(), + ctx.accounts.mint.to_account_info(), ctx.accounts.system_program.to_account_info(), ); if let Some(bump) = spl_interface_pda_bump { transfer = transfer .with_spl_interface( - ctx.accounts.mint.as_ref().map(|a| a.to_account_info()), + Some(ctx.accounts.mint.to_account_info()), ctx.accounts .spl_token_program .as_ref() @@ -42,10 +43,10 @@ pub mod light_token_anchor_transfer_interface { .map(|a| a.to_account_info()), Some(bump), ) - .map_err(|e| ProgramError::from(e))?; + ?; } - transfer.invoke().map_err(|e| ProgramError::from(e))?; + transfer.invoke()?; Ok(()) } } @@ -67,9 +68,9 @@ pub struct TransferAccounts<'info> { pub cpi_authority: AccountInfo<'info>, pub system_program: Program<'info, System>, - // SPL interface accounts (optional, for cross-type transfers) /// CHECK: Validated by light-token CPI - token mint - pub mint: Option>, + pub mint: AccountInfo<'info>, + // SPL interface accounts (optional, for cross-type transfers) /// CHECK: SPL Token or Token-2022 program pub spl_token_program: Option>, /// CHECK: Validated by light-token CPI - pool PDA @@ -123,7 +124,7 @@ async fn test_transfer() { payer: env.payer.pubkey(), cpi_authority: cpi_authority_pda, system_program: system_program::ID, - mint: None, + mint: env.mint_pda, spl_token_program: None, spl_interface_pda: None, } @@ -189,7 +190,7 @@ async fn test_transfer_spl_to_light() { .await .unwrap(); - // 2. Create SPL Interface PDA + // 2. Create SPL token pool (spl_interface_pda) let create_pool_ix = CreateSplInterfacePda::new(payer.pubkey(), mint, spl_token::ID, false).instruction(); @@ -246,7 +247,7 @@ async fn test_transfer_spl_to_light() { // 4. Create Light ATA for destination let recipient = Keypair::new(); - let (dest_ata, _) = derive_token_ata(&recipient.pubkey(), &mint); + let dest_ata = derive_token_ata(&recipient.pubkey(), &mint); let create_ata_ix = CreateAssociatedTokenAccount::new(payer.pubkey(), recipient.pubkey(), mint) .instruction() .unwrap(); @@ -271,7 +272,7 @@ async fn test_transfer_spl_to_light() { payer: payer.pubkey(), cpi_authority: cpi_authority_pda, system_program: system_program::ID, - mint: Some(mint), + mint: mint, spl_token_program: Some(spl_token::ID), spl_interface_pda: Some(spl_interface_pda), } diff --git a/snippets/code-snippets/light-token/transfer-interface/instruction.mdx b/snippets/code-snippets/light-token/transfer-interface/instruction.mdx index 8fd08921..5e94b693 100644 --- a/snippets/code-snippets/light-token/transfer-interface/instruction.mdx +++ b/snippets/code-snippets/light-token/transfer-interface/instruction.mdx @@ -2,7 +2,6 @@ import "dotenv/config"; import { Keypair, - ComputeBudgetProgram, Transaction, sendAndConfirmTransaction, } from "@solana/web3.js"; @@ -25,8 +24,8 @@ const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")), - ), + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) ); (async function () { @@ -36,7 +35,7 @@ const payer = Keypair.fromSecretKey( await createAtaInterface(rpc, payer, mint, sender.publicKey); const senderAta = getAssociatedTokenAddressInterface( mint, - sender.publicKey, + sender.publicKey ); await mintToInterface(rpc, payer, mint, senderAta, payer, 1_000_000_000); @@ -44,7 +43,7 @@ const payer = Keypair.fromSecretKey( await createAtaInterface(rpc, payer, mint, recipient.publicKey); const recipientAta = getAssociatedTokenAddressInterface( mint, - recipient.publicKey, + recipient.publicKey ); // Transfer tokens. Optional feePayer lets an application cover top-ups @@ -54,7 +53,7 @@ const payer = Keypair.fromSecretKey( recipientAta, sender.publicKey, // owner (signs the transfer) 500_000_000, - payer.publicKey, // optional: separate feePayer covers top-up + payer.publicKey // optional: separate feePayer covers top-up ); const tx = new Transaction().add(ix); diff --git a/snippets/code-snippets/light-token/transfer-interface/rust-client/instruction.mdx b/snippets/code-snippets/light-token/transfer-interface/rust-client/instruction.mdx index bc4ebd49..afce1ac0 100644 --- a/snippets/code-snippets/light-token/transfer-interface/rust-client/instruction.mdx +++ b/snippets/code-snippets/light-token/transfer-interface/rust-client/instruction.mdx @@ -15,7 +15,7 @@ use solana_sdk::signer::Signer; #[tokio::main] async fn main() -> Result<(), Box> { - let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(true, None)).await?; + let mut rpc = LightProgramTest::new(ProgramTestConfig::new(true, None)).await?; let payer = rpc.get_payer().insecure_clone(); let decimals = 2u8; @@ -51,8 +51,8 @@ async fn main() -> Result<(), Box> { decimals, authority: payer.pubkey(), payer: payer.pubkey(), + mint, spl_interface: Some(spl_interface), - max_top_up: None, source_owner: spl_token::ID, destination_owner: LIGHT_TOKEN_PROGRAM_ID, } diff --git a/snippets/code-snippets/light-token/unwrap/action.mdx b/snippets/code-snippets/light-token/unwrap/action.mdx index cdd5d61c..1afdef1f 100644 --- a/snippets/code-snippets/light-token/unwrap/action.mdx +++ b/snippets/code-snippets/light-token/unwrap/action.mdx @@ -1,18 +1,26 @@ ```typescript import "dotenv/config"; import { Keypair } from "@solana/web3.js"; -import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { createMint, mintTo } from "@lightprotocol/compressed-token"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + createAtaInterface, + mintToInterface, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token"; import { unwrap } from "@lightprotocol/compressed-token/unified"; -import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { + createAssociatedTokenAccount, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; import { homedir } from "os"; import { readFileSync } from "fs"; // devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); // localnet: -// const rpc = createRpc(); +const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( @@ -21,18 +29,25 @@ const payer = Keypair.fromSecretKey( ); (async function () { - // Setup: Get compressed tokens (cold storage) - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); - await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); + // Setup: Create and mint tokens to light-token associated token account + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + await createAtaInterface(rpc, payer, mint, payer.publicKey); + const destination = getAssociatedTokenAddressInterface( + mint, + payer.publicKey + ); + await mintToInterface(rpc, payer, mint, destination, payer, 1000); - // Unwrap rent-free tokens to SPL ATA + // Unwrap light-token to SPL associated token account const splAta = await createAssociatedTokenAccount( rpc, payer, mint, - payer.publicKey + payer.publicKey, + undefined, + TOKEN_2022_PROGRAM_ID ); - const tx = await unwrap(rpc, payer, splAta, payer, mint, bn(500)); + const tx = await unwrap(rpc, payer, splAta, payer, mint, 500); console.log("Tx:", tx); })(); diff --git a/snippets/code-snippets/light-token/unwrap/instruction.mdx b/snippets/code-snippets/light-token/unwrap/instruction.mdx index 5f38a5b3..bd9ca201 100644 --- a/snippets/code-snippets/light-token/unwrap/instruction.mdx +++ b/snippets/code-snippets/light-token/unwrap/instruction.mdx @@ -5,20 +5,27 @@ import { Transaction, sendAndConfirmTransaction, } from "@solana/web3.js"; -import { createRpc, bn } from "@lightprotocol/stateless.js"; +import { createRpc } from "@lightprotocol/stateless.js"; import { createMintInterface, - mintToCompressed, + createAtaInterface, + mintToInterface, + getAssociatedTokenAddressInterface, createUnwrapInstructions, } from "@lightprotocol/compressed-token/unified"; -import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { + createAssociatedTokenAccount, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; import { homedir } from "os"; import { readFileSync } from "fs"; // devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY}`; -const rpc = createRpc(RPC_URL); -// localnet: const rpc = createRpc(); +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); +// localnet: +const rpc = createRpc(); + const payer = Keypair.fromSecretKey( new Uint8Array( JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) @@ -26,16 +33,23 @@ const payer = Keypair.fromSecretKey( ); (async function () { + // Setup: Create and mint tokens to light-token associated token account const { mint } = await createMintInterface(rpc, payer, payer, null, 9); - await mintToCompressed(rpc, payer, mint, payer, [ - { recipient: payer.publicKey, amount: 1000n }, - ]); + await createAtaInterface(rpc, payer, mint, payer.publicKey); + const destination = getAssociatedTokenAddressInterface( + mint, + payer.publicKey + ); + await mintToInterface(rpc, payer, mint, destination, payer, 1000); + // Create destination SPL ATA const splAta = await createAssociatedTokenAccount( rpc, payer, mint, - payer.publicKey + payer.publicKey, + undefined, + TOKEN_2022_PROGRAM_ID ); // Returns TransactionInstruction[][]. Each inner array is one txn. @@ -45,13 +59,14 @@ const payer = Keypair.fromSecretKey( splAta, payer.publicKey, mint, - 500n, + 500, payer.publicKey ); for (const ixs of instructions) { const tx = new Transaction().add(...ixs); - await sendAndConfirmTransaction(rpc, tx, [payer]); + const signature = await sendAndConfirmTransaction(rpc, tx, [payer]); + console.log("Tx:", signature); } })(); ``` diff --git a/snippets/code-snippets/light-token/wrap/action.mdx b/snippets/code-snippets/light-token/wrap/action.mdx index d81baafa..18b646a7 100644 --- a/snippets/code-snippets/light-token/wrap/action.mdx +++ b/snippets/code-snippets/light-token/wrap/action.mdx @@ -1,24 +1,28 @@ ```typescript import "dotenv/config"; import { Keypair } from "@solana/web3.js"; -import { createRpc, bn } from "@lightprotocol/stateless.js"; +import { createRpc } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - decompress, + createMintInterface, + createAtaInterface, + mintToInterface, + decompressInterface, wrap, getAssociatedTokenAddressInterface, createAtaInterfaceIdempotent, } from "@lightprotocol/compressed-token"; -import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { + createAssociatedTokenAccount, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; import { homedir } from "os"; import { readFileSync } from "fs"; // devnet: -const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); // localnet: -// const rpc = createRpc(); +const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( @@ -28,21 +32,31 @@ const payer = Keypair.fromSecretKey( (async function () { // Setup: Get SPL tokens (needed to wrap) - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + await createAtaInterface(rpc, payer, mint, payer.publicKey); + const destination = getAssociatedTokenAddressInterface( + mint, + payer.publicKey + ); + await mintToInterface(rpc, payer, mint, destination, payer, 1000); const splAta = await createAssociatedTokenAccount( rpc, payer, mint, - payer.publicKey + payer.publicKey, + undefined, + TOKEN_2022_PROGRAM_ID ); - await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); - await decompress(rpc, payer, mint, bn(1000), payer, splAta); + await decompressInterface(rpc, payer, payer, mint, 1000); - // Wrap SPL tokens to rent-free token ATA - const lightTokenAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); + // Wrap SPL tokens to light-token associated token account + const lightTokenAta = getAssociatedTokenAddressInterface( + mint, + payer.publicKey + ); await createAtaInterfaceIdempotent(rpc, payer, mint, payer.publicKey); - const tx = await wrap(rpc, payer, splAta, lightTokenAta, payer, mint, bn(500)); + const tx = await wrap(rpc, payer, splAta, lightTokenAta, payer, mint, 500); console.log("Tx:", tx); })(); diff --git a/snippets/code-snippets/light-token/wrap/instruction.mdx b/snippets/code-snippets/light-token/wrap/instruction.mdx index e360f5d2..3b34766a 100644 --- a/snippets/code-snippets/light-token/wrap/instruction.mdx +++ b/snippets/code-snippets/light-token/wrap/instruction.mdx @@ -1,25 +1,34 @@ ```typescript import "dotenv/config"; -import { Keypair, ComputeBudgetProgram, Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; -import { createRpc, bn } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - decompress, + Keypair, + ComputeBudgetProgram, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + createAtaInterface, + mintToInterface, + decompressInterface, createWrapInstruction, getAssociatedTokenAddressInterface, createAtaInterfaceIdempotent, getSplInterfaceInfos, } from "@lightprotocol/compressed-token"; -import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { + createAssociatedTokenAccount, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; import { homedir } from "os"; import { readFileSync } from "fs"; // devnet: // const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -const rpc = createRpc(RPC_URL); +// const rpc = createRpc(RPC_URL); // localnet: -// const rpc = createRpc(); +const rpc = createRpc(); const payer = Keypair.fromSecretKey( new Uint8Array( @@ -29,18 +38,28 @@ const payer = Keypair.fromSecretKey( (async function () { // Setup: Get SPL tokens (needed to wrap) - const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + await createAtaInterface(rpc, payer, mint, payer.publicKey); + const destination = getAssociatedTokenAddressInterface( + mint, + payer.publicKey + ); + await mintToInterface(rpc, payer, mint, destination, payer, 1000); const splAta = await createAssociatedTokenAccount( rpc, payer, mint, - payer.publicKey + payer.publicKey, + undefined, + TOKEN_2022_PROGRAM_ID ); - await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); - await decompress(rpc, payer, mint, bn(1000), payer, splAta); + await decompressInterface(rpc, payer, payer, mint, 1000); // Create wrap instruction - const lightTokenAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); + const lightTokenAta = getAssociatedTokenAddressInterface( + mint, + payer.publicKey + ); await createAtaInterfaceIdempotent(rpc, payer, mint, payer.publicKey); const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint); @@ -55,7 +74,7 @@ const payer = Keypair.fromSecretKey( lightTokenAta, payer.publicKey, mint, - bn(500), + 500, splInterfaceInfo, 9, // decimals - must match the mint decimals payer.publicKey diff --git a/snippets/code-snippets/payments/interop/register-spl-mint.mdx b/snippets/code-snippets/payments/interop/register-spl-mint.mdx new file mode 100644 index 00000000..3494b3c9 --- /dev/null +++ b/snippets/code-snippets/payments/interop/register-spl-mint.mdx @@ -0,0 +1,46 @@ +```typescript +import "dotenv/config"; +import { + Keypair, + PublicKey, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { LightTokenProgram } from "@lightprotocol/compressed-token"; +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const rpc = createRpc(RPC_URL); +// localnet: +// const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + // Replace with your existing SPL mint (e.g. USDC) + const mint = new PublicKey("YOUR_EXISTING_MINT_ADDRESS"); + + // One-time: Register the SPL interface PDA for this mint. + // This creates the omnibus account that holds SPL tokens when wrapped to light-token. + // Note: createMintInterface(... TOKEN_PROGRAM_ID) does this automatically for new mints. + const ix = await LightTokenProgram.createSplInterface({ + feePayer: payer.publicKey, + mint, + tokenProgramId: TOKEN_PROGRAM_ID, + }); + + const tx = new Transaction().add(ix); + const signature = await sendAndConfirmTransaction(rpc, tx, [payer]); + + console.log("Mint:", mint.toBase58()); + console.log("Tx:", signature); +})(); +``` diff --git a/snippets/code-snippets/payments/interop/unwrap.mdx b/snippets/code-snippets/payments/interop/unwrap.mdx new file mode 100644 index 00000000..81f03420 --- /dev/null +++ b/snippets/code-snippets/payments/interop/unwrap.mdx @@ -0,0 +1,68 @@ +```typescript +import "dotenv/config"; +import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + createAtaInterface, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token"; +import { wrap, unwrap } from "@lightprotocol/compressed-token/unified"; +import { + TOKEN_PROGRAM_ID, + createAssociatedTokenAccount, + mintTo, +} from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const rpc = createRpc(RPC_URL); +// localnet: +// const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + // 1. Create SPL mint (includes SPL interface PDA registration) + const { mint } = await createMintInterface( + rpc, + payer, + payer, + null, + 9, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + // 2. Create SPL ATA and mint tokens + const splAta = await createAssociatedTokenAccount( + rpc, + payer, + mint, + payer.publicKey, + undefined, + TOKEN_PROGRAM_ID + ); + await mintTo(rpc, payer, mint, splAta, payer, 1000); + + // 3. Create light-token ATA and wrap all SPL tokens into it + await createAtaInterface(rpc, payer, mint, payer.publicKey); + const senderAta = getAssociatedTokenAddressInterface( + mint, + payer.publicKey + ); + await wrap(rpc, payer, splAta, senderAta, payer, mint, BigInt(1000)); + + // 4. Unwrap: move tokens back from light-token to SPL ATA + const tx = await unwrap(rpc, payer, splAta, payer, mint, 500); + + console.log("Tx:", tx); +})(); +``` diff --git a/snippets/code-snippets/payments/interop/wrap.mdx b/snippets/code-snippets/payments/interop/wrap.mdx new file mode 100644 index 00000000..1caafd90 --- /dev/null +++ b/snippets/code-snippets/payments/interop/wrap.mdx @@ -0,0 +1,75 @@ +```typescript +import "dotenv/config"; +import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + createAtaInterface, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token"; +import { wrap } from "@lightprotocol/compressed-token/unified"; +import { + TOKEN_PROGRAM_ID, + createAssociatedTokenAccount, + mintTo, +} from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const rpc = createRpc(RPC_URL); +// localnet: +// const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + // 1. Create SPL mint (includes SPL interface PDA registration) + const { mint } = await createMintInterface( + rpc, + payer, + payer, + null, + 9, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + // 2. Create SPL ATA and mint tokens + const splAta = await createAssociatedTokenAccount( + rpc, + payer, + mint, + payer.publicKey, + undefined, + TOKEN_PROGRAM_ID + ); + await mintTo(rpc, payer, mint, splAta, payer, 1000); + + // 3. Create light-token ATA + await createAtaInterface(rpc, payer, mint, payer.publicKey); + const senderAta = getAssociatedTokenAddressInterface( + mint, + payer.publicKey + ); + + // 4. Wrap: move SPL tokens into the light-token system + const tx = await wrap( + rpc, + payer, + splAta, + senderAta, + payer, + mint, + BigInt(500) + ); + + console.log("Tx:", tx); +})(); +``` diff --git a/snippets/code-snippets/payments/receive/receive.mdx b/snippets/code-snippets/payments/receive/receive.mdx new file mode 100644 index 00000000..0a44a95a --- /dev/null +++ b/snippets/code-snippets/payments/receive/receive.mdx @@ -0,0 +1,52 @@ +```typescript +import { + Keypair, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { + getAssociatedTokenAddressInterface, + createLoadAtaInstructions, +} from "@lightprotocol/compressed-token"; +import { transferInterface } from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + // Transfer to a fresh recipient so they have cold tokens + const recipient = Keypair.generate(); + await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + payer, + 500 + ); + + // --- Receive: load creates ATA if needed + pulls cold state to hot --- + const recipientAta = getAssociatedTokenAddressInterface( + mint, + recipient.publicKey + ); + + // Returns TransactionInstruction[][]. Each inner array is one txn. + // Almost always one. Empty = noop. + const instructions = await createLoadAtaInstructions( + rpc, + recipientAta, + recipient.publicKey, + mint, + payer.publicKey + ); + + for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + await sendAndConfirmTransaction(rpc, tx, [payer]); + } + + console.log("Recipient ATA:", recipientAta.toBase58()); +})(); +``` diff --git a/snippets/code-snippets/payments/send/batch-send.mdx b/snippets/code-snippets/payments/send/batch-send.mdx new file mode 100644 index 00000000..b51bfcc5 --- /dev/null +++ b/snippets/code-snippets/payments/send/batch-send.mdx @@ -0,0 +1,78 @@ +```typescript +import { + Keypair, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { + createAtaInterfaceIdempotent, + getAssociatedTokenAddressInterface, + getAtaInterface, +} from "@lightprotocol/compressed-token"; +import { + createTransferToAccountInterfaceInstructions, + createLoadAtaInstructions, +} from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + // Step 1: Setup — create mint and fund sender + const { mint } = await setup(); + + const recipients = Array.from({ length: 5 }, (_, i) => ({ + address: Keypair.generate().publicKey, + amount: (i + 1) * 100, + })); + + // Step 2: Create ATAs idempotently for sender + all recipients + await createAtaInterfaceIdempotent(rpc, payer, mint, payer.publicKey); + for (const { address } of recipients) { + await createAtaInterfaceIdempotent(rpc, payer, mint, address); + } + + // Step 3: Derive ATA addresses + const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); + const recipientAtas = recipients.map(({ address }) => + getAssociatedTokenAddressInterface(mint, address) + ); + + // Step 4: Create transfer instructions using explicit-account variant + const COMPUTE_BUDGET_ID = + "ComputeBudget111111111111111111111111111111"; + const allTransferIxs = []; + let isFirst = true; + + for (const { address, amount } of recipients) { + const destination = getAssociatedTokenAddressInterface(mint, address); + const ixs = await createTransferToAccountInterfaceInstructions( + rpc, + payer.publicKey, + mint, + amount, + payer.publicKey, + destination + ); + // Deduplicate ComputeBudget across transfers. + for (const ix of ixs[0]) { + if (!isFirst && ix.programId.toBase58() === COMPUTE_BUDGET_ID) + continue; + allTransferIxs.push(ix); + } + isFirst = false; + } + + // Step 5: Batch into single transaction + const batchTx = new Transaction().add(...allTransferIxs); + const sig = await sendAndConfirmTransaction(rpc, batchTx, [payer]); + console.log("Batch tx:", sig); + + // Step 6: Verify balances + for (const { address, amount } of recipients) { + const ata = getAssociatedTokenAddressInterface(mint, address); + const { parsed } = await getAtaInterface(rpc, ata, address, mint); + console.log( + ` ${address.toBase58()}: ${parsed.amount} (expected ${amount})` + ); + } +})(); +``` diff --git a/snippets/code-snippets/payments/send/payment-with-memo.mdx b/snippets/code-snippets/payments/send/payment-with-memo.mdx new file mode 100644 index 00000000..1736530b --- /dev/null +++ b/snippets/code-snippets/payments/send/payment-with-memo.mdx @@ -0,0 +1,63 @@ +```typescript +import { + Keypair, + Transaction, + TransactionInstruction, + PublicKey, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { + createTransferInterfaceInstructions, + sliceLast, +} from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +const MEMO_PROGRAM_ID = new PublicKey( + "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" +); + +(async function () { + const { mint } = await setup(); + const recipient = Keypair.generate(); + + const instructions = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + 100, + payer.publicKey, + recipient.publicKey + ); + + const { rest: loadInstructions, last: transferInstructions } = + sliceLast(instructions); + + // Send load transactions first (if any) + for (const ixs of loadInstructions) { + const tx = new Transaction().add(...ixs); + await sendAndConfirmTransaction(rpc, tx, [payer]); + } + + // Add memo to the transfer transaction + const memoIx = new TransactionInstruction({ + keys: [], + programId: MEMO_PROGRAM_ID, + data: Buffer.from("INV-2024-001"), + }); + + const transferTx = new Transaction().add(...transferInstructions, memoIx); + const signature = await sendAndConfirmTransaction(rpc, transferTx, [payer]); + console.log("Tx with memo:", signature); + + // Read memo back from transaction logs + const txDetails = await rpc.getTransaction(signature, { + maxSupportedTransactionVersion: 0, + }); + + const logs = txDetails?.meta?.logMessages || []; + const memoLogs = logs.filter((log: string) => + log.includes("Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr") + ); + console.log("Memo logs:", memoLogs); +})(); +``` diff --git a/snippets/code-snippets/payments/send/send-action.mdx b/snippets/code-snippets/payments/send/send-action.mdx new file mode 100644 index 00000000..4777ad06 --- /dev/null +++ b/snippets/code-snippets/payments/send/send-action.mdx @@ -0,0 +1,22 @@ +```typescript +import { Keypair } from "@solana/web3.js"; +import { transferInterface } from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + const recipient = Keypair.generate(); + + const sig = await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + payer, + 100 + ); + + console.log("Tx:", sig); +})(); +``` diff --git a/snippets/code-snippets/payments/send/send-instruction.mdx b/snippets/code-snippets/payments/send/send-instruction.mdx new file mode 100644 index 00000000..619bf93a --- /dev/null +++ b/snippets/code-snippets/payments/send/send-instruction.mdx @@ -0,0 +1,30 @@ +```typescript +import { + Keypair, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { createTransferInterfaceInstructions } from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint } = await setup(); + const recipient = Keypair.generate(); + + // Returns TransactionInstruction[][]. Each inner array is one txn. + const instructions = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + 100, + payer.publicKey, + recipient.publicKey + ); + + for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + const sig = await sendAndConfirmTransaction(rpc, tx, [payer]); + console.log("Tx:", sig); + } +})(); +``` diff --git a/snippets/code-snippets/payments/send/sign-all-transactions.mdx b/snippets/code-snippets/payments/send/sign-all-transactions.mdx new file mode 100644 index 00000000..3c551f83 --- /dev/null +++ b/snippets/code-snippets/payments/send/sign-all-transactions.mdx @@ -0,0 +1,62 @@ +```typescript +import { + Keypair, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { + createTransferInterfaceInstructions, + sliceLast, +} from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint } = await setup(); + const recipient = Keypair.generate(); + + const instructions = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + 100, + payer.publicKey, + recipient.publicKey + ); + + // --- Sign all transactions together — one approval for all --- + const transactions = instructions.map( + (ixs) => new Transaction().add(...ixs) + ); + // In a wallet context: await wallet.signAllTransactions(transactions) + for (const tx of transactions) { + const sig = await sendAndConfirmTransaction(rpc, tx, [payer]); + console.log("Tx:", sig); + } + + // --- Parallel optimization: parallelize loads, then send transfer --- + const instructions2 = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + 50, + payer.publicKey, + recipient.publicKey + ); + + const { rest: loadInstructions, last: transferInstructions } = + sliceLast(instructions2); + + // Parallelize loads (empty = nothing to load, will no-op) + await Promise.all( + loadInstructions.map((ixs) => { + const tx = new Transaction().add(...ixs); + return sendAndConfirmTransaction(rpc, tx, [payer]); + }) + ); + + // Send transfer after all loads confirm + const transferTx = new Transaction().add(...transferInstructions); + const sig = await sendAndConfirmTransaction(rpc, transferTx, [payer]); + console.log("Parallel optimized Tx:", sig); +})(); +``` diff --git a/snippets/code-snippets/payments/spend-permissions/delegate-approve.mdx b/snippets/code-snippets/payments/spend-permissions/delegate-approve.mdx new file mode 100644 index 00000000..6047ea3b --- /dev/null +++ b/snippets/code-snippets/payments/spend-permissions/delegate-approve.mdx @@ -0,0 +1,25 @@ +```typescript +import { Keypair } from "@solana/web3.js"; +import { approveInterface } from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + // Approve: grant delegate permission to spend up to 500,000 tokens + const delegate = Keypair.generate(); + const tx = await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + + console.log("Approved delegate:", delegate.publicKey.toBase58()); + console.log("Allowance: 500,000 tokens"); + console.log("Tx:", tx); +})(); +``` diff --git a/snippets/code-snippets/payments/spend-permissions/delegate-check.mdx b/snippets/code-snippets/payments/spend-permissions/delegate-check.mdx new file mode 100644 index 00000000..48e0181c --- /dev/null +++ b/snippets/code-snippets/payments/spend-permissions/delegate-check.mdx @@ -0,0 +1,60 @@ +```typescript +import { Keypair } from "@solana/web3.js"; +import { getAtaInterface } from "@lightprotocol/compressed-token"; +import { approveInterface } from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + // Check before approval — no delegate set + const beforeApproval = await getAtaInterface( + rpc, + senderAta, + payer.publicKey, + mint + ); + console.log("Before approval:"); + console.log( + " Delegate:", + beforeApproval.parsed.delegate?.toBase58() ?? "none" + ); + console.log( + " Delegated amount:", + beforeApproval.parsed.delegatedAmount.toString() + ); + + // Approve delegate + const delegate = Keypair.generate(); + await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + + // Check after approval — delegate is set with allowance + const afterApproval = await getAtaInterface( + rpc, + senderAta, + payer.publicKey, + mint + ); + console.log("\nAfter approval:"); + console.log( + " Delegate:", + afterApproval.parsed.delegate?.toBase58() ?? "none" + ); + console.log( + " Delegated amount:", + afterApproval.parsed.delegatedAmount.toString() + ); + console.log( + " Account balance:", + afterApproval.parsed.amount.toString() + ); +})(); +``` diff --git a/snippets/code-snippets/payments/spend-permissions/delegate-full-flow.mdx b/snippets/code-snippets/payments/spend-permissions/delegate-full-flow.mdx new file mode 100644 index 00000000..18499f30 --- /dev/null +++ b/snippets/code-snippets/payments/spend-permissions/delegate-full-flow.mdx @@ -0,0 +1,65 @@ +```typescript +import { Keypair } from "@solana/web3.js"; +import { getAtaInterface } from "@lightprotocol/compressed-token"; +import { + approveInterface, + revokeInterface, +} from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + // 1. Owner approves delegate to spend up to 500,000 tokens + const delegate = Keypair.generate(); + const approveTx = await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + console.log("1. Approved delegate:", delegate.publicKey.toBase58()); + console.log(" Allowance: 500,000 tokens"); + console.log(" Tx:", approveTx); + + // 2. Check delegation status + const account = await getAtaInterface( + rpc, + senderAta, + payer.publicKey, + mint + ); + console.log("\n2. Account status after approval:"); + console.log( + " Delegate:", + account.parsed.delegate?.toBase58() ?? "none" + ); + console.log( + " Delegated amount:", + account.parsed.delegatedAmount.toString() + ); + console.log(" Account balance:", account.parsed.amount.toString()); + + // 3. Owner revokes all delegate permissions + const revokeTx = await revokeInterface(rpc, payer, senderAta, mint, payer); + console.log("\n3. Revoked all delegate permissions"); + console.log(" Tx:", revokeTx); + + // 4. Verify delegate is removed + const afterRevoke = await getAtaInterface( + rpc, + senderAta, + payer.publicKey, + mint + ); + console.log("\n4. Account status after revoke:"); + console.log( + " Delegate:", + afterRevoke.parsed.delegate?.toBase58() ?? "none" + ); + console.log(" Balance:", afterRevoke.parsed.amount.toString()); +})(); +``` diff --git a/snippets/code-snippets/payments/spend-permissions/delegate-revoke.mdx b/snippets/code-snippets/payments/spend-permissions/delegate-revoke.mdx new file mode 100644 index 00000000..9c1126ec --- /dev/null +++ b/snippets/code-snippets/payments/spend-permissions/delegate-revoke.mdx @@ -0,0 +1,31 @@ +```typescript +import { Keypair } from "@solana/web3.js"; +import { + approveInterface, + revokeInterface, +} from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + // Approve delegate + const delegate = Keypair.generate(); + await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + // Revoke all delegate permissions + const tx = await revokeInterface(rpc, payer, senderAta, mint, payer); + + console.log("Revoked all delegate permissions"); + console.log("Tx:", tx); +})(); +``` diff --git a/snippets/code-snippets/payments/spend-permissions/delegate-transfer-instruction.mdx b/snippets/code-snippets/payments/spend-permissions/delegate-transfer-instruction.mdx new file mode 100644 index 00000000..32bf4887 --- /dev/null +++ b/snippets/code-snippets/payments/spend-permissions/delegate-transfer-instruction.mdx @@ -0,0 +1,34 @@ +```typescript +import { + Keypair, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { createTransferInterfaceInstructions } from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint } = await setup(); + + const delegate = Keypair.generate(); + const recipient = Keypair.generate(); + + // Returns TransactionInstruction[][]. Each inner array is one txn. + const instructions = await createTransferInterfaceInstructions( + rpc, + payer.publicKey, + mint, + 200_000, + delegate.publicKey, + recipient.publicKey, + 9, + { owner: payer.publicKey } + ); + + for (const ixs of instructions) { + const tx = new Transaction().add(...ixs); + const sig = await sendAndConfirmTransaction(rpc, tx, [payer, delegate]); + console.log("Tx:", sig); + } +})(); +``` diff --git a/snippets/code-snippets/payments/spend-permissions/delegate-transfer.mdx b/snippets/code-snippets/payments/spend-permissions/delegate-transfer.mdx new file mode 100644 index 00000000..ae7ddbc3 --- /dev/null +++ b/snippets/code-snippets/payments/spend-permissions/delegate-transfer.mdx @@ -0,0 +1,44 @@ +```typescript +import { Keypair } from "@solana/web3.js"; +import { + approveInterface, + transferInterface, +} from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + // Approve: grant delegate permission to spend up to 500,000 tokens + const delegate = Keypair.generate(); + await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + // Delegate transfers tokens on behalf of the owner + const recipient = Keypair.generate(); + const tx = await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + delegate, + 200_000, + undefined, + undefined, + { owner: payer.publicKey } + ); + + console.log("Delegated transfer to:", recipient.publicKey.toBase58()); + console.log("Amount: 200,000 tokens"); + console.log("Tx:", tx); +})(); +``` diff --git a/snippets/code-snippets/payments/verify/get-balance.mdx b/snippets/code-snippets/payments/verify/get-balance.mdx new file mode 100644 index 00000000..a161ce89 --- /dev/null +++ b/snippets/code-snippets/payments/verify/get-balance.mdx @@ -0,0 +1,37 @@ +```typescript +import { Keypair } from "@solana/web3.js"; +import { + getAssociatedTokenAddressInterface, + getAtaInterface, +} from "@lightprotocol/compressed-token"; +import { transferInterface } from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + const recipient = Keypair.generate(); + await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + payer, + 100 + ); + + // Get recipient's balance + const recipientAta = getAssociatedTokenAddressInterface( + mint, + recipient.publicKey + ); + const { parsed: account } = await getAtaInterface( + rpc, + recipientAta, + recipient.publicKey, + mint + ); + console.log("Recipient's balance:", account.amount); +})(); +``` diff --git a/snippets/code-snippets/payments/verify/get-history.mdx b/snippets/code-snippets/payments/verify/get-history.mdx new file mode 100644 index 00000000..e41e1c9f --- /dev/null +++ b/snippets/code-snippets/payments/verify/get-history.mdx @@ -0,0 +1,24 @@ +```typescript +import { Keypair } from "@solana/web3.js"; +import { transferInterface } from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + const recipient = Keypair.generate(); + await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + payer, + 100 + ); + + // Get transaction history + const result = await rpc.getSignaturesForOwnerInterface(payer.publicKey); + console.log("Signatures:", result.signatures.length); +})(); +``` diff --git a/snippets/code-snippets/payments/verify/verify-address.mdx b/snippets/code-snippets/payments/verify/verify-address.mdx new file mode 100644 index 00000000..c127f078 --- /dev/null +++ b/snippets/code-snippets/payments/verify/verify-address.mdx @@ -0,0 +1,61 @@ +```typescript +import { Keypair } from "@solana/web3.js"; +import { + createMintInterface, + createAtaInterface, + getAssociatedTokenAddressInterface, + getAtaInterface, +} from "@lightprotocol/compressed-token"; +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { rpc, payer } from "../setup.js"; + +(async function () { + const { mint } = await createMintInterface( + rpc, + payer, + payer, + null, + 9, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + // Create an associated token account for the payer (so it exists) + await createAtaInterface(rpc, payer, mint, payer.publicKey); + + // --- Verify an address that HAS an associated token account --- + const existingRecipient = payer.publicKey; + const expectedAta = getAssociatedTokenAddressInterface( + mint, + existingRecipient + ); + + try { + const account = await getAtaInterface( + rpc, + expectedAta, + existingRecipient, + mint + ); + console.log("Account exists, balance:", account.parsed.amount); + } catch { + console.log( + "Account does not exist yet — will be created on first transfer" + ); + } + + // --- Verify an address that does NOT have an associated token account --- + const newRecipient = Keypair.generate().publicKey; + const newAta = getAssociatedTokenAddressInterface(mint, newRecipient); + + try { + const account = await getAtaInterface(rpc, newAta, newRecipient, mint); + console.log("Account exists, balance:", account.parsed.amount); + } catch { + console.log( + "Account does not exist yet — will be created on first transfer" + ); + } +})(); +``` diff --git a/snippets/code-snippets/privy/balances/nodejs.mdx b/snippets/code-snippets/privy/balances/nodejs.mdx index a2ca8cfb..929a0b91 100644 --- a/snippets/code-snippets/privy/balances/nodejs.mdx +++ b/snippets/code-snippets/privy/balances/nodejs.mdx @@ -1,7 +1,7 @@ ```typescript import 'dotenv/config'; import {PublicKey, LAMPORTS_PER_SOL} from '@solana/web3.js'; -import {TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID} from '@solana/spl-token'; +import {TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, getMint} from '@solana/spl-token'; import {createRpc} from '@lightprotocol/stateless.js'; import { getAtaInterface, @@ -37,13 +37,13 @@ export async function getBalances( console.error('Failed to fetch SOL balance:', e); } - // Per-mint accumulator - const mintMap = new Map(); + // Per-mint accumulator (raw values, converted at assembly) + const mintMap = new Map(); const getOrCreate = (mintStr: string) => { let entry = mintMap.get(mintStr); if (!entry) { - entry = {spl: 0, t22: 0, hot: 0, cold: 0, decimals: 9}; + entry = {spl: 0n, t22: 0n, hot: 0n, cold: 0n, decimals: 9, tokenProgram: TOKEN_PROGRAM_ID}; mintMap.set(mintStr, entry); } return entry; @@ -60,7 +60,7 @@ export async function getBalances( const mint = new PublicKey(buf.subarray(0, 32)); const amount = buf.readBigUInt64LE(64); const mintStr = mint.toBase58(); - getOrCreate(mintStr).spl += toUiAmount(amount, 9); + getOrCreate(mintStr).spl += amount; } } catch { // No SPL accounts @@ -77,49 +77,66 @@ export async function getBalances( const mint = new PublicKey(buf.subarray(0, 32)); const amount = buf.readBigUInt64LE(64); const mintStr = mint.toBase58(); - getOrCreate(mintStr).t22 += toUiAmount(amount, 9); + const entry = getOrCreate(mintStr); + entry.t22 += amount; + entry.tokenProgram = TOKEN_2022_PROGRAM_ID; } } catch { // No Token 2022 accounts } - // 3. Hot balance from Light Token associated token account + // 3. Cold balance from compressed token accounts + try { + const compressed = await rpc.getCompressedTokenBalancesByOwnerV2(owner); + for (const item of compressed.value.items) { + const mintStr = item.mint.toBase58(); + getOrCreate(mintStr).cold += BigInt(item.balance.toString()); + } + } catch { + // No compressed accounts + } + + // 4. Fetch actual decimals for each mint const mintKeys = [...mintMap.keys()]; + await Promise.allSettled( + mintKeys.map(async (mintStr) => { + try { + const mint = new PublicKey(mintStr); + const entry = getOrCreate(mintStr); + const mintInfo = await getMint(rpc, mint, undefined, entry.tokenProgram); + entry.decimals = mintInfo.decimals; + } catch { + // Keep default decimals if mint fetch fails + } + }), + ); + + // 5. Hot balance from Light Token associated token account await Promise.allSettled( mintKeys.map(async (mintStr) => { try { const mint = new PublicKey(mintStr); const ata = getAssociatedTokenAddressInterface(mint, owner); const {parsed} = await getAtaInterface(rpc, ata, owner, mint); - getOrCreate(mintStr).hot = toUiAmount(parsed.amount, 9); + getOrCreate(mintStr).hot = BigInt(parsed.amount.toString()); } catch { // Associated token account does not exist for this mint } }), ); - // 4. Cold balance from compressed token accounts - try { - const compressed = await rpc.getCompressedTokenBalancesByOwnerV2(owner); - for (const item of compressed.value.items) { - const mintStr = item.mint.toBase58(); - getOrCreate(mintStr).cold += toUiAmount(BigInt(item.balance.toString()), 9); - } - } catch { - // No compressed accounts - } - - // Assemble result + // 6. Assemble result (convert raw → UI amounts here) const tokens: TokenBalance[] = []; for (const [mintStr, entry] of mintMap) { + const d = entry.decimals; tokens.push({ mint: mintStr, - decimals: entry.decimals, - hot: entry.hot, - cold: entry.cold, - spl: entry.spl, - t22: entry.t22, - unified: entry.hot + entry.cold, + decimals: d, + hot: toUiAmount(entry.hot, d), + cold: toUiAmount(entry.cold, d), + spl: toUiAmount(entry.spl, d), + t22: toUiAmount(entry.t22, d), + unified: toUiAmount(entry.hot + entry.cold, d), }); } @@ -132,9 +149,8 @@ function toBuffer(data: Buffer | Uint8Array | string | unknown): Buffer | null { return null; } -function toUiAmount(raw: bigint | {toNumber: () => number}, decimals: number): number { - const value = typeof raw === 'bigint' ? Number(raw) : raw.toNumber(); - return value / 10 ** decimals; +function toUiAmount(raw: bigint, decimals: number): number { + return Number(raw) / 10 ** decimals; } export default getBalances; diff --git a/snippets/code-snippets/privy/balances/react.mdx b/snippets/code-snippets/privy/balances/react.mdx index d4112fa1..0c9f60e8 100644 --- a/snippets/code-snippets/privy/balances/react.mdx +++ b/snippets/code-snippets/privy/balances/react.mdx @@ -1,7 +1,7 @@ ```typescript import { useState, useCallback } from 'react'; -import { PublicKey } from '@solana/web3.js'; -import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token'; +import { PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js'; +import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, getMint } from '@solana/spl-token'; import { createRpc } from '@lightprotocol/stateless.js'; import { getAssociatedTokenAddressInterface, @@ -32,12 +32,12 @@ export function useUnifiedBalance() { const owner = new PublicKey(ownerAddress); // Per-mint accumulator - const mintMap = new Map(); + const mintMap = new Map(); const getOrCreate = (mintStr: string) => { let entry = mintMap.get(mintStr); if (!entry) { - entry = { spl: 0n, t22: 0n, hot: 0n, cold: 0n, decimals: 9 }; + entry = { spl: 0n, t22: 0n, hot: 0n, cold: 0n, decimals: 9, tokenProgram: TOKEN_PROGRAM_ID }; mintMap.set(mintStr, entry); } return entry; @@ -79,7 +79,9 @@ export function useUnifiedBalance() { const mint = new PublicKey(buf.subarray(0, 32)); const amount = buf.readBigUInt64LE(64); const mintStr = mint.toBase58(); - getOrCreate(mintStr).t22 += amount; + const entry = getOrCreate(mintStr); + entry.t22 += amount; + entry.tokenProgram = TOKEN_2022_PROGRAM_ID; } } catch { // No Token 2022 accounts @@ -96,8 +98,22 @@ export function useUnifiedBalance() { // No compressed accounts } - // 5. Hot balance from Light Token associated token account + // 5. Fetch actual decimals for each mint const mintKeys = [...mintMap.keys()]; + await Promise.allSettled( + mintKeys.map(async (mintStr) => { + try { + const mint = new PublicKey(mintStr); + const entry = getOrCreate(mintStr); + const mintInfo = await getMint(rpc, mint, undefined, entry.tokenProgram); + entry.decimals = mintInfo.decimals; + } catch { + // Keep default decimals if mint fetch fails + } + }), + ); + + // 6. Hot balance from Light Token associated token account await Promise.allSettled( mintKeys.map(async (mintStr) => { try { @@ -112,7 +128,7 @@ export function useUnifiedBalance() { }), ); - // 6. Assemble TokenBalance[] + // 7. Assemble TokenBalance[] const result: TokenBalance[] = []; // SOL entry diff --git a/snippets/code-snippets/privy/transfer/nodejs.mdx b/snippets/code-snippets/privy/transfer/nodejs.mdx index 60b51ea7..4a0844e4 100644 --- a/snippets/code-snippets/privy/transfer/nodejs.mdx +++ b/snippets/code-snippets/privy/transfer/nodejs.mdx @@ -24,7 +24,7 @@ const transferLightTokens = async ( const fromPubkey = new PublicKey(fromAddress); const toPubkey = new PublicKey(toAddress); const mintPubkey = new PublicKey(tokenMintAddress); - const tokenAmount = Math.round(amount * Math.pow(10, decimals)); + const tokenAmount = Math.floor(amount * Math.pow(10, decimals)); // Loads cold (compressed), SPL, and Token 2022 balances into the Light Token associated token account before transfer. // Returns TransactionInstruction[][] — send [0..n-2] in parallel, then [n-1] last. diff --git a/snippets/code-snippets/privy/transfer/react.mdx b/snippets/code-snippets/privy/transfer/react.mdx index f8ae9b16..e5a3e715 100644 --- a/snippets/code-snippets/privy/transfer/react.mdx +++ b/snippets/code-snippets/privy/transfer/react.mdx @@ -40,7 +40,7 @@ export function useTransfer() { const owner = new PublicKey(ownerPublicKey); const mintPubkey = new PublicKey(mint); const recipient = new PublicKey(toAddress); - const tokenAmount = Math.round(amount * Math.pow(10, decimals)); + const tokenAmount = Math.floor(amount * Math.pow(10, decimals)); // Returns TransactionInstruction[][]. // Each inner array is one transaction. diff --git a/snippets/code-snippets/privy/unwrap/nodejs.mdx b/snippets/code-snippets/privy/unwrap/nodejs.mdx index f344b301..1bc631e2 100644 --- a/snippets/code-snippets/privy/unwrap/nodejs.mdx +++ b/snippets/code-snippets/privy/unwrap/nodejs.mdx @@ -23,7 +23,7 @@ const unwrapTokens = async ( const fromPubkey = new PublicKey(fromAddress); const mintPubkey = new PublicKey(tokenMintAddress); - const tokenAmount = BigInt(Math.round(amount * Math.pow(10, decimals))); + const tokenAmount = BigInt(Math.floor(amount * Math.pow(10, decimals))); // Auto-detect token program (SPL vs Token 2022) from mint account owner const mintAccountInfo = await connection.getAccountInfo(mintPubkey); diff --git a/snippets/code-snippets/privy/unwrap/react.mdx b/snippets/code-snippets/privy/unwrap/react.mdx index 0eeafb56..cf5def70 100644 --- a/snippets/code-snippets/privy/unwrap/react.mdx +++ b/snippets/code-snippets/privy/unwrap/react.mdx @@ -39,7 +39,7 @@ export function useUnwrap() { const owner = new PublicKey(ownerPublicKey); const mintPubkey = new PublicKey(mint); - const tokenAmount = BigInt(Math.round(amount * Math.pow(10, decimals))); + const tokenAmount = BigInt(Math.floor(amount * Math.pow(10, decimals))); // Auto-detect token program (SPL vs T22) from mint account owner const mintAccountInfo = await rpc.getAccountInfo(mintPubkey); diff --git a/snippets/code-snippets/privy/wrap/nodejs.mdx b/snippets/code-snippets/privy/wrap/nodejs.mdx index 20fe72f8..bf34ca1c 100644 --- a/snippets/code-snippets/privy/wrap/nodejs.mdx +++ b/snippets/code-snippets/privy/wrap/nodejs.mdx @@ -26,7 +26,7 @@ const wrapTokens = async ( const fromPubkey = new PublicKey(fromAddress); const mintPubkey = new PublicKey(tokenMintAddress); - const tokenAmount = BigInt(Math.round(amount * Math.pow(10, decimals))); + const tokenAmount = BigInt(Math.floor(amount * Math.pow(10, decimals))); // Get SPL interface info — determines whether mint uses SPL or Token 2022 const splInterfaceInfos = await getSplInterfaceInfos(connection, mintPubkey); diff --git a/snippets/code-snippets/privy/wrap/react.mdx b/snippets/code-snippets/privy/wrap/react.mdx index 8262a394..4cefafd5 100644 --- a/snippets/code-snippets/privy/wrap/react.mdx +++ b/snippets/code-snippets/privy/wrap/react.mdx @@ -41,7 +41,7 @@ export function useWrap() { const owner = new PublicKey(ownerPublicKey); const mintPubkey = new PublicKey(mint); - const tokenAmount = BigInt(Math.round(amount * Math.pow(10, decimals))); + const tokenAmount = BigInt(Math.floor(amount * Math.pow(10, decimals))); // Get SPL interface info — determines whether mint uses SPL or T22 const splInterfaceInfos = await getSplInterfaceInfos(rpc, mintPubkey); diff --git a/snippets/code-snippets/wallet-adapter/balances/react.mdx b/snippets/code-snippets/wallet-adapter/balances/react.mdx index 457b6972..0c9f60e8 100644 --- a/snippets/code-snippets/wallet-adapter/balances/react.mdx +++ b/snippets/code-snippets/wallet-adapter/balances/react.mdx @@ -1,6 +1,6 @@ ```typescript import { useState, useCallback } from 'react'; -import { PublicKey } from '@solana/web3.js'; +import { PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js'; import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, getMint } from '@solana/spl-token'; import { createRpc } from '@lightprotocol/stateless.js'; import { @@ -28,9 +28,7 @@ export function useUnifiedBalance() { setIsLoading(true); try { - const rpc = import.meta.env.VITE_LOCALNET === 'true' - ? createRpc() - : createRpc(import.meta.env.VITE_HELIUS_RPC_URL); + const rpc = createRpc(import.meta.env.VITE_HELIUS_RPC_URL); const owner = new PublicKey(ownerAddress); // Per-mint accumulator @@ -133,21 +131,16 @@ export function useUnifiedBalance() { // 7. Assemble TokenBalance[] const result: TokenBalance[] = []; - // Pull WSOL data from mintMap (if user has WSOL in token accounts) - const WSOL_MINT = 'So11111111111111111111111111111111111111112'; - const wsolEntry = mintMap.get(WSOL_MINT); - mintMap.delete(WSOL_MINT); // prevent duplicate row - - // SOL entry: native SOL + WSOL Light Token balances + // SOL entry result.push({ - mint: WSOL_MINT, + mint: 'So11111111111111111111111111111111111111112', decimals: 9, isNative: true, - hot: wsolEntry?.hot ?? 0n, - cold: wsolEntry?.cold ?? 0n, + hot: 0n, + cold: 0n, spl: BigInt(solLamports), - t22: wsolEntry?.t22 ?? 0n, - unified: (wsolEntry?.hot ?? 0n) + (wsolEntry?.cold ?? 0n), + t22: 0n, + unified: 0n, }); // Token entries diff --git a/snippets/code-snippets/wallet-adapter/transaction-history/react.mdx b/snippets/code-snippets/wallet-adapter/transaction-history/react.mdx index 17cfdca7..1c98218e 100644 --- a/snippets/code-snippets/wallet-adapter/transaction-history/react.mdx +++ b/snippets/code-snippets/wallet-adapter/transaction-history/react.mdx @@ -29,9 +29,7 @@ export function useTransactionHistory() { setError(null); try { - const rpc = import.meta.env.VITE_LOCALNET === 'true' - ? createRpc() - : createRpc(import.meta.env.VITE_HELIUS_RPC_URL); + const rpc = createRpc(import.meta.env.VITE_HELIUS_RPC_URL); const owner = new PublicKey(ownerAddress); const result = await rpc.getSignaturesForOwnerInterface(owner); diff --git a/snippets/code-snippets/wallet-adapter/transfer/react.mdx b/snippets/code-snippets/wallet-adapter/transfer/react.mdx index 59e55bd2..8b4deb42 100644 --- a/snippets/code-snippets/wallet-adapter/transfer/react.mdx +++ b/snippets/code-snippets/wallet-adapter/transfer/react.mdx @@ -1,16 +1,12 @@ ```typescript import { useState } from 'react'; -import { PublicKey, type TransactionInstruction } from '@solana/web3.js'; +import { PublicKey } from '@solana/web3.js'; import { createTransferInterfaceInstructions, } from '@lightprotocol/compressed-token/unified'; import { createRpc } from '@lightprotocol/stateless.js'; import { signAndSendBatches, type SignTransactionFn } from './signAndSendBatches'; -// For native SOL, the user must wrap SOL → WSOL first (via useWrap or -// the payments toolkit). createTransferInterfaceInstructions works with -// WSOL like any other SPL token once it exists in a token account. - export interface TransferParams { ownerPublicKey: string; mint: string; @@ -34,19 +30,19 @@ export function useTransfer() { const { params, signTransaction } = args; const { ownerPublicKey, mint, toAddress, amount, decimals = 9 } = params; - const rpc = import.meta.env.VITE_LOCALNET === 'true' - ? createRpc() - : createRpc(import.meta.env.VITE_HELIUS_RPC_URL); + const rpc = createRpc(import.meta.env.VITE_HELIUS_RPC_URL); const owner = new PublicKey(ownerPublicKey); const mintPubkey = new PublicKey(mint); const recipient = new PublicKey(toAddress); - const tokenAmount = Math.round(amount * Math.pow(10, decimals)); + const tokenAmount = Math.floor(amount * Math.pow(10, decimals)); - const instructions: TransactionInstruction[][] = - await createTransferInterfaceInstructions( - rpc, owner, mintPubkey, tokenAmount, owner, recipient, - ); + // Returns TransactionInstruction[][]. + // Each inner array is one transaction. + // Almost always returns just one. + const instructions = await createTransferInterfaceInstructions( + rpc, owner, mintPubkey, tokenAmount, owner, recipient, + ); const signature = await signAndSendBatches(instructions, { rpc, diff --git a/snippets/code-snippets/wallet-adapter/unwrap/react.mdx b/snippets/code-snippets/wallet-adapter/unwrap/react.mdx index cdfda000..6ffead4c 100644 --- a/snippets/code-snippets/wallet-adapter/unwrap/react.mdx +++ b/snippets/code-snippets/wallet-adapter/unwrap/react.mdx @@ -30,13 +30,11 @@ export function useUnwrap() { const { params, signTransaction } = args; const { ownerPublicKey, mint, amount, decimals = 9 } = params; - const rpc = import.meta.env.VITE_LOCALNET === 'true' - ? createRpc() - : createRpc(import.meta.env.VITE_HELIUS_RPC_URL); + const rpc = createRpc(import.meta.env.VITE_HELIUS_RPC_URL); const owner = new PublicKey(ownerPublicKey); const mintPubkey = new PublicKey(mint); - const tokenAmount = BigInt(Math.round(amount * Math.pow(10, decimals))); + const tokenAmount = BigInt(Math.floor(amount * Math.pow(10, decimals))); // Auto-detect token program (SPL vs T22) from mint account owner const mintAccountInfo = await rpc.getAccountInfo(mintPubkey); diff --git a/snippets/code-snippets/wallet-adapter/wrap/react.mdx b/snippets/code-snippets/wallet-adapter/wrap/react.mdx index 8eb738df..a249e19f 100644 --- a/snippets/code-snippets/wallet-adapter/wrap/react.mdx +++ b/snippets/code-snippets/wallet-adapter/wrap/react.mdx @@ -33,13 +33,11 @@ export function useWrap() { const { params, signTransaction } = args; const { ownerPublicKey, mint, amount, decimals = 9 } = params; - const rpc = import.meta.env.VITE_LOCALNET === 'true' - ? createRpc() - : createRpc(import.meta.env.VITE_HELIUS_RPC_URL); + const rpc = createRpc(import.meta.env.VITE_HELIUS_RPC_URL); const owner = new PublicKey(ownerPublicKey); const mintPubkey = new PublicKey(mint); - const tokenAmount = BigInt(Math.round(amount * Math.pow(10, decimals))); + const tokenAmount = BigInt(Math.floor(amount * Math.pow(10, decimals))); // Get SPL interface info — determines whether mint uses SPL or T22 const splInterfaceInfos = await getSplInterfaceInfos(rpc, mintPubkey); diff --git a/snippets/extensions-table.mdx b/snippets/extensions-table.mdx new file mode 100644 index 00000000..e3bcb041 --- /dev/null +++ b/snippets/extensions-table.mdx @@ -0,0 +1,18 @@ +| Extension | Restriction | Description | Links | +|-----------|-------------|-------------|-------| +| **MetadataPointer** | - | Points a mint to the account that stores its metadata. | [Solana Docs](https://solana.com/developers/guides/token-extensions/metadata-pointer) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/metadata-and-metadata-pointer.ts) | +| **TokenMetadata** | - | Stores token name, symbol, and URI directly on the mint. | [Solana Docs](https://solana.com/developers/guides/token-extensions/metadata-pointer) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/metadata-and-metadata-pointer.ts) | +| **TransferFeeConfig** | Fees must be zero | Withholds a percentage of each transfer as a fee. | [Solana Docs](https://solana.com/developers/guides/token-extensions/transfer-fee) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/transfer-fees.ts) | +| **TransferHook** | `program_id` must be nil | Invokes a custom program on every transfer via CPI. | [Solana Docs](https://solana.com/developers/guides/token-extensions/transfer-hook) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/transfer-hook.ts) | +| **InterestBearingConfig** | - | Displays a UI-adjusted balance that accrues interest over time. | [Solana Docs](https://solana.com/developers/guides/token-extensions/interest-bearing-tokens) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/interest-bearing-tokens.ts) | +| **DefaultAccountState** | Set `compression_only` flag on token accounts | Sets the initial state (e.g., frozen) for newly created token accounts. | [Solana Docs](https://solana.com/developers/guides/token-extensions/default-account-state) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/default-account-state.ts) | +| **PermanentDelegate** | Set `compression_only` flag on token accounts | Grants an authority unrestricted transfer and burn rights over all accounts for the mint. | [Solana Docs](https://solana.com/developers/guides/token-extensions/permanent-delegate) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/permanent-delegate.ts) | +| **MintCloseAuthority** | Set `compression_only` flag on token accounts | Allows a designated authority to close a mint account. | [Solana Docs](https://solana.com/developers/guides/token-extensions/mint-close-authority) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/close-mint.ts) | +| **GroupPointer** | - | Points a mint to the account that stores group configuration. | [Solana Docs](https://solana.com/developers/guides/token-extensions/group-member) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/token-groups-and-members.ts) | +| **GroupMemberPointer** | - | Points a mint to the account that stores group member configuration. | [Solana Docs](https://solana.com/developers/guides/token-extensions/group-member) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/token-groups-and-members.ts) | +| **TokenGroup** | - | Stores group configuration directly on the mint (e.g., NFT collections). | [Solana Docs](https://solana.com/developers/guides/token-extensions/group-member) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/token-groups-and-members.ts) | +| **TokenGroupMember** | - | Stores group member configuration directly on the mint. | [Solana Docs](https://solana.com/developers/guides/token-extensions/group-member) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/token-groups-and-members.ts) | +| **Pausable** | Set `compression_only` flag on token accounts | Allows an authority to pause all minting, burning, and transfers. | [Solana Docs](https://solana.com/docs/tokens/extensions/pausable) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/pausable-mint.ts) | +| **ConfidentialTransferMint** | Initialized but not enabled | Configures auditor keys for confidential (encrypted) transfers. | [Solana Docs](https://www.solana-program.com/docs/confidential-balances) · [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/confidential-transfer.ts) | +| **ConfidentialTransferFeeConfig** | Initialized but not enabled | Encrypts withheld transfer fees under an auditor's public key. | [Solana Docs](https://www.solana-program.com/docs/confidential-balances) | +| **ConfidentialMintBurn** | Initialized but not enabled | Allows minting and burning of tokens with encrypted amounts. | [Solana Docs](https://www.solana-program.com/docs/confidential-balances) | diff --git a/snippets/jsx/stats-widget.jsx b/snippets/jsx/stats-widget.jsx new file mode 100644 index 00000000..514f08ff --- /dev/null +++ b/snippets/jsx/stats-widget.jsx @@ -0,0 +1,90 @@ +export const StatsWidget = () => { + const API_URL = "http://localhost:3004/api/metrics"; + const REFRESH_MS = 30000; + const MONO = "ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, monospace"; + + const formatNumber = (value, digits = 2) => + new Intl.NumberFormat("en-US", { + maximumFractionDigits: digits, + }).format(value); + + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + useEffect(() => { + const controller = new AbortController(); + + async function fetchMetrics() { + try { + const res = await fetch(API_URL, { + signal: controller.signal, + cache: "no-store", + }); + if (!res.ok) throw new Error("fetch failed"); + const json = await res.json(); + setData(json); + setError(false); + } catch (err) { + if (err.name === "AbortError") return; + setError(true); + } finally { + setLoading(false); + } + } + + fetchMetrics(); + const interval = setInterval(fetchMetrics, REFRESH_MS); + return () => { + controller.abort(); + clearInterval(interval); + }; + }, []); + + if (loading) { + return ( +
+
+
+
+
+
+
+
+
+
+ ); + } + + if (error || !data) return null; + + const tps = data.tpsAverages?.tps1h ?? 0; + const txCount = data.totals?.txCountWindow ?? 0; + + return ( +
+
+
+ {formatNumber(tps, 2)} +
+
+ Transactions per Second +
+
+
+
+ {formatNumber(txCount, 0)} +
+
+ Transactions last 24 hours +
+
+
+ ); +}; diff --git a/snippets/migration-reference/ts/approve.mdx b/snippets/migration-reference/ts/approve.mdx index 5a7fcad1..3dbc3db2 100644 --- a/snippets/migration-reference/ts/approve.mdx +++ b/snippets/migration-reference/ts/approve.mdx @@ -1,12 +1,13 @@ ```typescript -import { approve } from "@lightprotocol/compressed-token"; +import { approveInterface } from "@lightprotocol/compressed-token"; -const tx = await approve( +const tx = await approveInterface( rpc, payer, + tokenAccount, mint, + delegate, amount, - owner, - delegate + owner ); ``` diff --git a/snippets/migration-reference/ts/revoke.mdx b/snippets/migration-reference/ts/revoke.mdx index 34f60282..1c0fe9a2 100644 --- a/snippets/migration-reference/ts/revoke.mdx +++ b/snippets/migration-reference/ts/revoke.mdx @@ -1,10 +1,11 @@ ```typescript -import { revoke } from "@lightprotocol/compressed-token"; +import { revokeInterface } from "@lightprotocol/compressed-token"; -const tx = await revoke( +const tx = await revokeInterface( rpc, payer, - delegatedAccounts, + tokenAccount, + mint, owner ); ``` diff --git a/snippets/overview-tables/compressed-pdas-guides-table.mdx b/snippets/overview-tables/compressed-pdas-guides-table.mdx index 5783b13d..32066e3c 100644 --- a/snippets/overview-tables/compressed-pdas-guides-table.mdx +++ b/snippets/overview-tables/compressed-pdas-guides-table.mdx @@ -42,7 +42,7 @@ - Nullifier PDAs + Nullifier PDAs Prevent replay attacks with one-time use accounts diff --git a/snippets/overview-tables/light-token-client-examples-table.mdx b/snippets/overview-tables/light-token-client-examples-table.mdx index df2a937c..81dc68e5 100644 --- a/snippets/overview-tables/light-token-client-examples-table.mdx +++ b/snippets/overview-tables/light-token-client-examples-table.mdx @@ -2,22 +2,27 @@ | | | | |---------|-------------|-------------| -| **payments-and-wallets** | All you need for wallet integrations and payment flows. Minimal API differences to SPL. | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets) \| [Docs](/light-token/payments/integration-guide) | +| **payments** | All you need for wallet integrations and payment flows. Minimal API differences to SPL. | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments) \| [Docs](/light-token/payments/integration-guide) | | **sign-with-privy** | Light-token operations signed with Privy wallets (Node.js + React) | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy) \| [Docs](/light-token/wallets/privy) | | **sign-with-wallet-adapter** | Light-token operations signed with Wallet Adapter (React) | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-wallet-adapter) \| [Docs](/light-token/wallets/wallet-adapter) | -| **sponsor-rent-top-ups** | Sponsor rent top-ups by setting your application as the fee payer | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sponsor-rent-top-ups) \| [Docs](/light-token/wallets/sponsor-top-ups) | +| **gasless-transactions** | Abstract SOL fees so users never hold SOL. Sponsor rent top-ups and transaction fees. | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/gasless-transactions) \| [Docs](/light-token/wallets/gasless-transactions) | | **spl-to-light** | Transfer from SPL to Light via TransferInterface | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/instructions/spl_to_light_transfer.rs) | ### TypeScript | | | | | |---------|--------|-------------|------| -| **approve** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-approve.ts) | — | [Docs](/light-token/cookbook/approve-revoke) | +| **approve** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-approve.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/delegate-approve.ts) | [Docs](/light-token/cookbook/approve-revoke) | | **create-ata** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/create-ata.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/create-ata.ts) | [Docs](/light-token/cookbook/create-ata) | +| **create-ata-explicit-rent-sponsor** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/create-ata-explicit-rent-sponsor.ts) | — | [Docs](/light-token/cookbook/create-ata) | | **create-mint** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/create-mint.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/create-mint.ts) | [Docs](/light-token/cookbook/create-mint) | +| **create-spl-interface** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/create-spl-interface.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/create-spl-interface.ts) | [Docs](light-token/cookbook/add-interface-pda) | +| **create-spl-mint** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/create-spl-mint.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/create-spl-mint.ts) | [Docs](/light-token/cookbook/create-mint#create-spl-mint-with-interface-pda) | +| **create-t22-mint** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/create-t22-mint.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/create-t22-mint.ts) | [Docs](/light-token/cookbook/create-mint#create-token-2022-mint-with-interface-pda) | +| **delegate-transfer** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-transfer.ts) | — | [Docs](/light-token/cookbook/transfer-delegated) | | **load-ata** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/load-ata.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/load-ata.ts) | [Docs](/light-token/cookbook/load-ata) | | **mint-to** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/mint-to.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/mint-to.ts) | [Docs](/light-token/cookbook/mint-to) | -| **revoke** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-revoke.ts) | — | [Docs](/light-token/cookbook/approve-revoke) | +| **revoke** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/delegate-revoke.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/delegate-revoke.ts) | [Docs](/light-token/cookbook/approve-revoke) | | **transfer-interface** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/transfer-interface.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/transfer-interface.ts) | [Docs](/light-token/cookbook/transfer-interface) | | **unwrap** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/unwrap.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/unwrap.ts) | [Docs](/light-token/cookbook/wrap-unwrap) | | **wrap** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/actions/wrap.ts) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/typescript-client/instructions/wrap.ts) | [Docs](/light-token/cookbook/wrap-unwrap) | @@ -42,3 +47,20 @@ | **transfer-interface** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/actions/transfer_interface.rs) | [Instruction](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/instructions/transfer_interface.rs) | [Docs](/light-token/cookbook/transfer-interface) | | **unwrap** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/actions/unwrap.rs) | — | [Docs](/light-token/cookbook/wrap-unwrap) | | **wrap** | [Action](https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/actions/wrap.rs) | — | [Docs](/light-token/cookbook/wrap-unwrap) | + +### Extensions + +See how [Light Token works with Token 2022 extensions](/light-token/extensions/overview). + +| | | +|---------|-------------| +| **close-mint** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/close-mint.ts) | +| **confidential-transfer** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/confidential-transfer.ts) | +| **default-account-state** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/default-account-state.ts) | +| **interest-bearing-tokens** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/interest-bearing-tokens.ts) | +| **metadata-and-metadata-pointer** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/metadata-and-metadata-pointer.ts) | +| **pausable-mint** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/pausable-mint.ts) | +| **permanent-delegate** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/permanent-delegate.ts) | +| **token-groups-and-members** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/token-groups-and-members.ts) | +| **transfer-fees** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/transfer-fees.ts) | +| **transfer-hook** | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/extensions/transfer-hook.ts) | diff --git a/snippets/overview-tables/light-token-program-examples-table.mdx b/snippets/overview-tables/light-token-program-examples-table.mdx index d96fe4dd..d59e6a54 100644 --- a/snippets/overview-tables/light-token-program-examples-table.mdx +++ b/snippets/overview-tables/light-token-program-examples-table.mdx @@ -15,7 +15,7 @@ | | Description | | |---------|-------------|------| | **counter** | Create PDA with sponsored rent-exemption | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/counter) \| [Docs](/pda/light-pda/overview) | -| **create-associated-token-account** | Create associated light-token account | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-ata) \| [Docs](/light-token/cookbook/create-ata) | +| **create-associated-token-account** | Create associated light-token account | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-associated-token-account) \| [Docs](/light-token/cookbook/create-ata) | | **create-mint** | Create light-token mint | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-mint) \| [Docs](/light-token/cookbook/create-mint) | | **create-token-account** | Create light-token account | [Example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-token-account) \| [Docs](/light-token/cookbook/create-token-account) | @@ -29,7 +29,7 @@ For existing programs, you can replace spl_token with light_token instructions a | **approve** | Approve delegate via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/approve/src/lib.rs) \| [Docs](/light-token/cookbook/approve-revoke) | | **burn** | Burn tokens via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/burn/src/lib.rs) \| [Docs](/light-token/cookbook/burn) | | **close** | Close token account via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/close/src/lib.rs) \| [Docs](/light-token/cookbook/close-token-account) | -| **create-associated-token-account** | Create associated light-token account via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-ata/src/lib.rs) \| [Docs](/light-token/cookbook/create-ata) | +| **create-associated-token-account** | Create associated light-token account via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-associated-token-account/src/lib.rs) \| [Docs](/light-token/cookbook/create-ata) | | **create-mint** | Create light-token mint via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-mint/src/lib.rs) \| [Docs](/light-token/cookbook/create-mint) | | **create-token-account** | Create light-token account via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/create-token-account/src/lib.rs) \| [Docs](/light-token/cookbook/create-token-account) | | **freeze** | Freeze token account via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/freeze/src/lib.rs) \| [Docs](/light-token/cookbook/freeze-thaw) | @@ -37,4 +37,4 @@ For existing programs, you can replace spl_token with light_token instructions a | **revoke** | Revoke delegate via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/revoke/src/lib.rs) \| [Docs](/light-token/cookbook/approve-revoke) | | **thaw** | Thaw token account via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/thaw/src/lib.rs) \| [Docs](/light-token/cookbook/freeze-thaw) | | **transfer-checked** | Transfer with mint validation via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/transfer-checked/src/lib.rs) \| [Docs](/light-token/cookbook/transfer-checked) | -| **transfer-interface** | Transfer between light-token, T22, and SPL accounts via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/transfer-interface/src/lib.rs) \| [Docs](/light-token/cookbook/transfer-interface) | +| **transfer-interface** | Transfer between light-token, Token 2022, and SPL accounts via CPI | [Example](https://github.com/Lightprotocol/examples-light-token/blob/main/programs/anchor/basic-instructions/transfer-interface/src/lib.rs) \| [Docs](/light-token/cookbook/transfer-interface) | diff --git a/snippets/setup/agent-skill-payments.mdx b/snippets/setup/agent-skill-payments.mdx index 80e5711f..46ce0660 100644 --- a/snippets/setup/agent-skill-payments.mdx +++ b/snippets/setup/agent-skill-payments.mdx @@ -2,7 +2,7 @@ import InstallAgentSkills from "/snippets/setup/install-agent-skills.mdx"; -Use the [payments-and-wallets](https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets) agent skill to add light-token payment support to your project: +Use the [payments](https://github.com/Lightprotocol/skills/tree/main/skills/payments) agent skill to add light-token payment support to your project: diff --git a/snippets/setup/register-spl-mint.mdx b/snippets/setup/register-spl-mint.mdx index 57ccead5..0e3688a6 100644 --- a/snippets/setup/register-spl-mint.mdx +++ b/snippets/setup/register-spl-mint.mdx @@ -3,7 +3,7 @@ For existing SPL mints (e.g. USDC), register the SPL interface once. This creates the omnibus PDA that holds SPL tokens when wrapped to light-token. -Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/register-spl-mint.ts). +Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/interop/register-spl-mint.ts). Check if the interface already exists: