Solana’s memo program lets you attach invoice numbers, order IDs, or custom references to any payment. These memos are permanently recorded on-chain and visible in transaction logs, making it easy to match payments to your internal systems. Use just like with SPL token:
| SPL | Light | |
|---|---|---|
| Transfer | createTransferInstruction() | createTransferInterfaceInstructions() |
Agent skill
Agent skill
Use the payments agent skill to add light-token payment support to your project:For orchestration, install the general skill:
Report incorrect code
Copy
Ask AI
npx skills add Lightprotocol/skills
Report incorrect code
Copy
Ask AI
npx skills add https://zkcompression.com
- Guide
- AI Prompt
Setup
Installation
Installation
- npm
- yarn
- pnpm
Install packages in your working directory:Install the CLI globally:
Report incorrect code
Copy
Ask AI
npm install @lightprotocol/stateless.js@beta \
@lightprotocol/compressed-token@beta
Report incorrect code
Copy
Ask AI
npm install -g @lightprotocol/zk-compression-cli@beta
Install packages in your working directory:Install the CLI globally:
Report incorrect code
Copy
Ask AI
yarn add @lightprotocol/stateless.js@beta \
@lightprotocol/compressed-token@beta
Report incorrect code
Copy
Ask AI
yarn global add @lightprotocol/zk-compression-cli@beta
Install packages in your working directory:Install the CLI globally:
Report incorrect code
Copy
Ask AI
pnpm add @lightprotocol/stateless.js@beta \
@lightprotocol/compressed-token@beta
Report incorrect code
Copy
Ask AI
pnpm add -g @lightprotocol/zk-compression-cli@beta
- Localnet
- Devnet
Report incorrect code
Copy
Ask AI
# start local test-validator in a separate terminal
light test-validator
In the code examples, use
createRpc() without arguments for localnet.Get an API key from Helius and add to
.env:.env
Report incorrect code
Copy
Ask AI
API_KEY=<your-helius-api-key>
In the code examples, use
createRpc(RPC_URL) with the devnet URL.Report incorrect code
Copy
Ask AI
npm install @solana/spl-memo
Report incorrect code
Copy
Ask AI
import { createRpc } from "@lightprotocol/stateless.js";
const rpc = createRpc(RPC_ENDPOINT);
Create a Token Helper for Setup
Create a Token Helper for Setup
setup.ts
Report incorrect code
Copy
Ask AI
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!}`;
// export const rpc = createRpc(RPC_URL);
// localnet:
export const rpc = createRpc();
export const payer = Keypair.fromSecretKey(
new Uint8Array(
JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
)
);
/** Create SPL mint, fund payer, wrap into light-token ATA. */
export async function setup(amount = 1_000_000_000) {
const { mint } = await createMintInterface(
rpc,
payer,
payer,
null,
9,
undefined,
undefined,
TOKEN_PROGRAM_ID
);
const splAta = await createAssociatedTokenAccount(
rpc,
payer,
mint,
payer.publicKey,
undefined,
TOKEN_PROGRAM_ID
);
await mintTo(rpc, payer, mint, splAta, payer, amount);
await createAtaInterface(rpc, payer, mint, payer.publicKey);
const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey);
await wrap(rpc, payer, splAta, senderAta, payer, mint, BigInt(amount));
return { mint, senderAta, splAta };
}
Send a Payment with Memo
- Create the transfer instructions with
createTransferInterfaceInstructions(). Load transactions are handled under the hood. - Create a memo instruction with a message. This message will be visible in the program logs of the transaction.
- Both instructions execute atomically in the same transaction.
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.Report incorrect code
Copy
Ask AI
import {
Keypair,
Transaction,
sendAndConfirmTransaction,
} from "@solana/web3.js";
import { createTransferInterfaceInstructions } from "@lightprotocol/compressed-token/unified";
import { createMemoInstruction } from "@solana/spl-memo";
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
);
// Append memo to the transfer transaction (last batch)
const memoIx = createMemoInstruction("INV-2024-001");
instructions[instructions.length - 1].push(memoIx);
let signature;
for (const ixs of instructions) {
const tx = new Transaction().add(...ixs);
signature = await sendAndConfirmTransaction(rpc, tx, [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);
})();
Compare to SPL
Compare to SPL
Report incorrect code
Copy
Ask AI
import { getAddMemoInstruction } from "@solana-program/memo";
import { getTransferInstruction } from "@solana-program/token";
const transferInstruction = getTransferInstruction({
source: senderAta,
destination: recipientAta,
authority: sender.address,
amount: 250_000n
});
const memoInstruction = getAddMemoInstruction({
memo: "Payment for services rendered - Invoice #12345"
});
const signature = await client.transaction.prepareAndSend({
authority: sender,
instructions: [transferInstruction, memoInstruction],
version: 0
});
const transaction = await client.runtime.rpc
.getTransaction(signature, {
encoding: "jsonParsed",
maxSupportedTransactionVersion: 0
})
.send();
console.log("Transaction logs with Memo:");
console.log(transaction?.meta?.logMessages);
Read Memo from Transaction Logs
After the transaction confirms, the memo appears in the transaction logs:Example Transaction Logs
Report incorrect code
Copy
Ask AI
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [1]",
"Program log: Instruction: Transfer",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1682 of 200000 compute units",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success",
"Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr invoke [1]",
'Program log: Memo (len 46): "Payment for services rendered - Invoice #12345"',
"Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr consumed 18097 of 198318 compute units",
"Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr success",
"Program ComputeBudget111111111111111111111111111111 invoke [1]",
"Program ComputeBudget111111111111111111111111111111 success"
Integrate light-token APIs for stablecoin payments
Report incorrect code
Copy
Ask AI
---
description: Integrate light-token APIs for stablecoin payments
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
---
## Integrate light-token APIs for stablecoin payments
Context:
- Guide: https://zkcompression.com/light-token/payments/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
- Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js
- Import from @lightprotocol/compressed-token/unified for all interface APIs
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() |
| Unwrap | N/A | createUnwrapInstructions() / unwrap() |
### 1. Index project
- Grep `@solana/spl-token|@lightprotocol|createTransferInstruction|getAccount|Connection|Keypair|stablecoin|payment` across src/
- Glob `**/*.ts` and `**/*.tsx` for project structure
- Identify: RPC setup, existing token operations, payment flow, wallet signing pattern
- Check package.json for existing @lightprotocol/* or @solana/spl-token dependencies
- Task subagent (Grep/Read/WebFetch) if project has multiple packages to scan in parallel
### 2. Read references
- WebFetch the guide above — review Instruction and Action tabs for each operation
- WebFetch skill.md — check for a dedicated skill and resources matching this task
- TaskCreate one todo per phase below to track progress
### 3. Clarify intention
- AskUserQuestion: what is the goal? (new payment integration, migrate existing SPL payment flow, add alongside existing SPL)
- AskUserQuestion: which operations? (receive, send, balance, history, wrap, unwrap — or all)
- AskUserQuestion: instruction-level API (build your own transactions) or action-level API (high-level, fewer lines)?
- Summarize findings and wait for user confirmation before implementing
### 4. Create plan
- Based on steps 1–3, draft an implementation plan: which files to modify, what code to add, dependency changes
- Verify existing connection/signer setup is compatible (createRpc with ZK Compression endpoint)
- Key pattern: import from `@lightprotocol/compressed-token/unified` for all interface APIs
- If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
- Present the plan to the user for approval before proceeding
### 5. Implement
- Add deps if missing: Bash `npm install @lightprotocol/compressed-token @lightprotocol/stateless.js`
- Set up RPC: `createRpc(RPC_ENDPOINT)` with a ZK Compression endpoint (Helius, Triton)
- Import from `@lightprotocol/compressed-token/unified` for the interface APIs
- Follow the guide and the approved plan
- Write/Edit to create or modify files
- TaskUpdate to mark each step done
### 6. Verify
- Bash `tsc --noEmit`
- Bash run existing test suite if present
- TaskUpdate to mark complete
### Tools
- mcp__zkcompression__SearchLightProtocol("<query>") for API details
- mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
- Task subagent with Grep/Read/WebFetch for parallel lookups
- TaskList to check remaining work
### Resources
Guides:
- Basic payment: https://zkcompression.com/light-token/payments/basic-payment
- Batch payments: https://zkcompression.com/light-token/payments/batch-payments
- Payment with memo: https://zkcompression.com/light-token/payments/payment-with-memo
- Receive payments: https://zkcompression.com/light-token/payments/receive-payments
- Verify payments: https://zkcompression.com/light-token/payments/verify-payments
- Verify recipient address: https://zkcompression.com/light-token/payments/verify-recipient-address
- Spend permissions: https://zkcompression.com/light-token/payments/spend-permissions
- Wrap and unwrap: https://zkcompression.com/light-token/payments/wrap-unwrap
- Production readiness: https://zkcompression.com/light-token/payments/production-readiness
- FAQ: https://zkcompression.com/faq
- Sign with Privy: https://zkcompression.com/light-token/wallets/privy
- Sign with Wallet Adapter: https://zkcompression.com/light-token/wallets/wallet-adapter
- Gasless transactions: https://zkcompression.com/light-token/wallets/gasless-transactions
Examples:
- All payments: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments
- Send (instruction): https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/basic-send-instruction.ts
- Send (action): https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/basic-send-action.ts
- Batch send: https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/batch-send.ts
- Payment with memo: https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/send/payment-with-memo.ts
- Receive: https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/receive/receive.ts
- 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: https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments/verify/verify-address.ts
- 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
- Privy (Node.js): https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy/nodejs
- Privy (React): https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-privy/react
- Wallet Adapter (React): https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/sign-with-wallet-adapter/react
- Gasless transfer: https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/gasless-transactions/typescript/gasless-transfer.ts
Related guides
Basic payment
Send a single transfer without memo.
Verify payments
Query balances and transaction history.
Batch payments
Pack multiple transfers into one transaction.