Skip to main content
  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 hold and receive tokens of the same mints, just stored more efficiently.
Creation CostSPLLight-token
Token Account~2,000,000 lamports~11,000 lamports

What you will implement

SPLLight
ReceivegetOrCreateAssociatedTokenAccount()createLoadAtaInstructions() / loadAta()
TransfercreateTransferInstruction()createTransferInterfaceInstructions() / transferInterface()
Get BalancegetAccount()getAtaInterface()
Tx HistorygetSignaturesForAddress()rpc.getSignaturesForOwnerInterface()
Wrap from SPLN/Awrap() / createWrapInstruction()
Unwrap to SPLN/AcreateUnwrapInstructions() / unwrap()
Find full runnable code examples here.
Use the payments-and-wallets agent skill to add light-token payment support to your project:
Add the marketplace and install:
/plugin marketplace add Lightprotocol/skills
/plugin install solana-rent-free-dev
For orchestration, install the general skill:
npx skills add https://zkcompression.com

Setup

npm install @lightprotocol/compressed-token@beta \
            @lightprotocol/stateless.js@beta
Snippets below assume rpc, payer, mint, owner, recipient, and amount are defined. See the full examples for runnable setup.
import { createRpc } from "@lightprotocol/stateless.js";

import {
  createLoadAtaInstructions,
  loadAta,
  createTransferInterfaceInstructions,
  transferInterface,
  createUnwrapInstructions,
  unwrap,
  getAssociatedTokenAddressInterface,
  getAtaInterface,
  wrap,
} from "@lightprotocol/compressed-token/unified";

const rpc = createRpc(RPC_ENDPOINT);
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.

Receive Payments

Find a full code example here.
Load creates the associated token account (ATA) if needed and loads any compressed state into it. Share the ATA address with the sender.
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]);
}
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
);

Send Payments

Find a full code example: instruction | action.
import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
import {
  createTransferInterfaceInstructions,
  getOrCreateAtaInterface,
  getAssociatedTokenAddressInterface,
} from "@lightprotocol/compressed-token/unified";

await getOrCreateAtaInterface(rpc, payer, mint, recipient);
const destination = getAssociatedTokenAddressInterface(mint, recipient);

const instructions = await createTransferInterfaceInstructions(
  rpc,
  payer.publicKey,
  mint,
  amount,
  owner.publicKey,
  destination
);

for (const ixs of instructions) {
  const tx = new Transaction().add(...ixs);
  await sendAndConfirmTransaction(rpc, tx, [payer, owner]);
}
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)
);

Show Balance

Find a full code example here.
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);
import { getAccount } from "@solana/spl-token";

const account = await getAccount(connection, ata);

console.log(account.amount);

Transaction History

Find a full code example here.
const result = await rpc.getSignaturesForOwnerInterface(owner);

console.log(result.signatures); // Merged + deduplicated
console.log(result.solana); // On-chain txs only
console.log(result.compressed); // Compressed txs only
Use getSignaturesForAddressInterface(address) if you want address-specific rather than owner-wide history.
const signatures = await connection.getSignaturesForAddress(ata);

Wrap from SPL

Wrap tokens from SPL/Token-2022 accounts to light-token ATA.
Find a full code example here.
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
  )
);

Unwrap to SPL

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.
import { Transaction } 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]);
}

More wallet guides


Didn’t find what you were looking for?

Reach out! Telegram | email | Discord