> ## Documentation Index
> Fetch the complete documentation index at: https://luminouslabs-cc5545c6-docs-main-reorder.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Toolkit for Stablecoin Payments

> Guide to integrate light-token APIs for stablecoin payments with comparison to SPL.

export const CodeCompare = ({firstCode = "", secondCode = "", firstLabel = "Light Token", secondLabel = "SPL", language = "javascript"}) => {
  const [sliderPercent, setSliderPercent] = useState(100);
  const [isDragging, setIsDragging] = useState(false);
  const [isAnimating, setIsAnimating] = useState(false);
  const [copied, setCopied] = useState(false);
  const containerRef = useRef(null);
  const animationRef = useRef(null);
  const firstPreRef = useRef(null);
  const secondPreRef = useRef(null);
  const [containerHeight, setContainerHeight] = useState(null);
  const showingFirst = sliderPercent > 50;
  const handleCopy = async () => {
    const codeToCopy = showingFirst ? firstCode : secondCode;
    await navigator.clipboard.writeText(codeToCopy);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };
  const highlightCode = code => {
    let escaped = code.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    if (language === "rust") {
      const rustPattern = /(\/\/.*$)|(["'])(?:(?!\2)[^\\]|\\.)*?\2|\b(use|let|mut|pub|fn|struct|impl|enum|mod|const|static|trait|type|where|for|in|if|else|match|loop|while|return|self|Self|true|false|Some|None|Ok|Err|Result|Option|vec!)\b|::([a-zA-Z_][a-zA-Z0-9_]*)|&amp;([a-zA-Z_][a-zA-Z0-9_]*)|\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()|(\?)/gm;
      return escaped.replace(rustPattern, (match, comment, stringQuote, keyword, pathSegment, reference, func, questionMark) => {
        if (comment) return `<span style="color:#6b7280;font-style:italic">${match}</span>`;
        if (stringQuote) return `<span style="color:#059669">${match}</span>`;
        if (keyword) return `<span style="color:#db2777">${match}</span>`;
        if (pathSegment) return `::<span style="color:#0891b2">${pathSegment}</span>`;
        if (reference) return `&amp;<span style="color:#6366f1">${reference}</span>`;
        if (func) return `<span style="color:#2563eb">${match}</span>`;
        if (questionMark) return `<span style="color:#db2777">?</span>`;
        return match;
      });
    }
    const pattern = /(\/\/.*$)|(["'`])(?:(?!\2)[^\\]|\\.)*?\2|\b(const|let|var|await|async|import|from|export|return|if|else|function|class|new|throw|try|catch)\b|\.([a-zA-Z_][a-zA-Z0-9_]*)\b|\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/gm;
    return escaped.replace(pattern, (match, comment, stringQuote, keyword, property, func) => {
      if (comment) return `<span style="color:#6b7280;font-style:italic">${match}</span>`;
      if (stringQuote) return `<span style="color:#059669">${match}</span>`;
      if (keyword) return `<span style="color:#db2777">${match}</span>`;
      if (property) return `.<span style="color:#0891b2">${property}</span>`;
      if (func) return `<span style="color:#2563eb">${match}</span>`;
      return match;
    });
  };
  const animateTo = target => {
    if (animationRef.current) cancelAnimationFrame(animationRef.current);
    setIsAnimating(true);
    const start = sliderPercent;
    const startTime = performance.now();
    const duration = 400;
    const animate = currentTime => {
      const elapsed = currentTime - startTime;
      const progress = Math.min(elapsed / duration, 1);
      const eased = 1 - Math.pow(1 - progress, 3);
      const current = start + (target - start) * eased;
      setSliderPercent(current);
      if (progress < 1) {
        animationRef.current = requestAnimationFrame(animate);
      } else {
        setSliderPercent(target);
        setIsAnimating(false);
        animationRef.current = null;
      }
    };
    animationRef.current = requestAnimationFrame(animate);
  };
  const handleToggle = () => {
    animateTo(showingFirst ? 0 : 100);
  };
  const handleMouseDown = e => {
    if (isAnimating) {
      cancelAnimationFrame(animationRef.current);
      setIsAnimating(false);
    }
    e.preventDefault();
    setIsDragging(true);
  };
  const handleMouseUp = () => {
    setIsDragging(false);
  };
  const handleMouseMove = e => {
    if (!isDragging || !containerRef.current) return;
    const rect = containerRef.current.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const percent = Math.max(0, Math.min(100, x / rect.width * 100));
    setSliderPercent(percent);
  };
  const handleTouchMove = e => {
    if (!containerRef.current) return;
    if (isAnimating) {
      cancelAnimationFrame(animationRef.current);
      setIsAnimating(false);
    }
    const rect = containerRef.current.getBoundingClientRect();
    const x = e.touches[0].clientX - rect.left;
    const percent = Math.max(0, Math.min(100, x / rect.width * 100));
    setSliderPercent(percent);
  };
  const handleKeyDown = e => {
    if (e.key === "ArrowLeft") {
      setSliderPercent(p => Math.max(0, p - 5));
    } else if (e.key === "ArrowRight") {
      setSliderPercent(p => Math.min(100, p + 5));
    }
  };
  useEffect(() => {
    if (isDragging) {
      document.addEventListener("mousemove", handleMouseMove);
      document.addEventListener("mouseup", handleMouseUp);
      return () => {
        document.removeEventListener("mousemove", handleMouseMove);
        document.removeEventListener("mouseup", handleMouseUp);
      };
    }
  }, [isDragging]);
  useEffect(() => {
    return () => {
      if (animationRef.current) cancelAnimationFrame(animationRef.current);
    };
  }, []);
  useEffect(() => {
    const activeRef = showingFirst ? firstPreRef : secondPreRef;
    if (activeRef.current) {
      setContainerHeight(activeRef.current.scrollHeight);
    }
  }, [showingFirst]);
  return <>
      <div className="rounded-3xl not-prose mt-4 backdrop-blur-xl border overflow-hidden border-zinc-300 dark:border-zinc-700" style={{
    fontFamily: "Inter, sans-serif"
  }}>
        {}
        <div className="flex items-center justify-between px-4 py-3 border-b border-zinc-200 dark:border-zinc-700 bg-gray-50 dark:bg-zinc-900">
          <span className="text-sm font-medium text-zinc-600 dark:text-zinc-300">
            {showingFirst ? firstLabel : secondLabel}
          </span>

          <div className="flex items-center gap-3">
            {}
            <button onClick={handleCopy} className="p-1.5 rounded hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors text-zinc-500 dark:text-zinc-400" title="Copy code" style={{
    background: "transparent",
    border: "none",
    cursor: "pointer"
  }}>
              {copied ? <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2">
                  <path d="M20 6L9 17l-5-5" />
                </svg> : <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
                  <path d="M14.25 5.25H7.25C6.14543 5.25 5.25 6.14543 5.25 7.25V14.25C5.25 15.3546 6.14543 16.25 7.25 16.25H14.25C15.3546 16.25 16.25 15.3546 16.25 14.25V7.25C16.25 6.14543 15.3546 5.25 14.25 5.25Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
                  <path d="M2.80103 11.998L1.77203 5.07397C1.61003 3.98097 2.36403 2.96397 3.45603 2.80197L10.38 1.77297C11.313 1.63397 12.19 2.16297 12.528 3.00097" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
                </svg>}
            </button>

            {}
            <div onClick={handleToggle} className="bg-zinc-200 dark:bg-zinc-600" style={{
    position: "relative",
    width: "56px",
    height: "28px",
    borderRadius: "14px",
    boxShadow: "inset -2px -2px 4px rgba(255,255,255,0.3), inset 2px 2px 4px rgba(0,0,0,0.1)",
    cursor: "pointer",
    transition: "all 0.3s ease"
  }}>
              {}
              <div className="bg-white dark:bg-zinc-300" style={{
    position: "absolute",
    width: "24px",
    height: "24px",
    borderRadius: "12px",
    top: "2px",
    left: showingFirst ? "30px" : "2px",
    boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
    transition: "all 0.3s ease-in-out",
    display: "flex",
    alignItems: "center",
    justifyContent: "center"
  }}>
                {}
                <div style={{
    width: "6px",
    height: "6px",
    background: showingFirst ? "#0066ff" : "#999",
    borderRadius: "50%",
    boxShadow: showingFirst ? "0 0 5px 1px rgba(0, 102, 255, 0.6)" : "0 0 4px 1px rgba(0, 0, 0, 0.1)",
    transition: "all 0.3s ease-in-out"
  }} />
              </div>
            </div>
          </div>
        </div>

        {}
        <div ref={containerRef} className="p-0" style={{
    cursor: isDragging ? "grabbing" : "default"
  }} onTouchMove={handleTouchMove} tabIndex={0} onKeyDown={handleKeyDown} role="slider" aria-valuenow={sliderPercent} aria-valuemin={0} aria-valuemax={100} aria-label="Code comparison slider">
          <div className="relative" style={{
    minHeight: "140px",
    overflow: "hidden",
    height: containerHeight ? `${containerHeight}px` : "auto",
    transition: "height 0.3s ease"
  }}>
            <div style={{
    position: "relative"
  }}>
              {}
              <pre ref={secondPreRef} className="m-0 p-4 text-zinc-700 dark:text-white/80 bg-transparent" style={{
    position: showingFirst ? "absolute" : "relative",
    top: 0,
    left: 0,
    right: 0,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: "13px",
    lineHeight: "1.6",
    whiteSpace: "pre",
    zIndex: 1
  }} dangerouslySetInnerHTML={{
    __html: highlightCode(secondCode)
  }} />

              {}
              <pre ref={firstPreRef} className="m-0 p-4 text-zinc-700 dark:text-white/80 bg-white dark:bg-zinc-900" style={{
    position: showingFirst ? "relative" : "absolute",
    top: 0,
    left: 0,
    right: 0,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: "13px",
    lineHeight: "1.6",
    whiteSpace: "pre",
    zIndex: 2,
    clipPath: `inset(0 ${100 - sliderPercent}% 0 0)`
  }} dangerouslySetInnerHTML={{
    __html: highlightCode(firstCode)
  }} />
            </div>

            {}
            <div className="absolute top-0 bottom-0 flex items-center justify-center pointer-events-none" style={{
    left: `${sliderPercent}%`,
    transform: "translateX(-50%)",
    zIndex: 30
  }}>
              <div className="absolute top-0 bottom-0 w-px bg-zinc-400 dark:bg-white/30" />

              <div className="absolute top-0 bottom-0" style={{
    right: "50%",
    width: "60px",
    background: "linear-gradient(to left, rgba(0, 102, 255, 0.15) 0%, transparent 100%)"
  }} />

              {}
              <div onMouseDown={handleMouseDown} className="pointer-events-auto cursor-grab flex items-center justify-center gap-px transition-transform" style={{
    width: "20px",
    height: "32px",
    borderRadius: "4px",
    background: "#f8fafc",
    border: "1px solid #d1d5db",
    boxShadow: "0 1px 2px rgba(0,0,0,0.05)",
    transform: isDragging ? "scale(1.08)" : "scale(1)"
  }}>
                <div className="flex flex-col gap-0.5">
                  {[0, 1, 2].map(i => <div key={i} style={{
    width: "3px",
    height: "3px",
    borderRadius: "50%",
    background: "#0066ff"
  }} />)}
                </div>
                <div className="flex flex-col gap-0.5">
                  {[0, 1, 2].map(i => <div key={i} style={{
    width: "3px",
    height: "3px",
    borderRadius: "50%",
    background: "#0066ff"
  }} />)}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </>;
};

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.

| Creation Cost     | SPL                  | light-token           |
| :---------------- | :------------------- | :-------------------- |
| **Token Account** | \~2,000,000 lamports | \~**11,000** lamports |

### What you will implement

<table>
  <thead>
    <tr>
      <th style={{ width: "15%" }} />

      <th>SPL</th>
      <th>Light</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>**Receive**</td>
      <td>getOrCreateAssociatedTokenAccount()</td>
      <td>createLoadAtaInstructions()</td>
    </tr>

    <tr>
      <td>**Transfer**</td>
      <td>createTransferInstruction()</td>
      <td>createTransferInterfaceInstructions()</td>
    </tr>

    <tr>
      <td>**Get Balance**</td>
      <td>getAccount()</td>
      <td>getAtaInterface()</td>
    </tr>

    <tr>
      <td>**Tx History**</td>
      <td>getSignaturesForAddress()</td>
      <td>rpc.getSignaturesForOwnerInterface()</td>
    </tr>

    <tr>
      <td>**Wrap from SPL**</td>
      <td>N/A</td>
      <td>createWrapInstruction()</td>
    </tr>

    <tr>
      <td>**Unwrap to SPL**</td>
      <td>N/A</td>
      <td>createUnwrapInstructions() / unwrap()</td>
    </tr>
  </tbody>
</table>

<Note>
  Find full runnable code examples
  [here](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets).
</Note>

<Accordion title="Agent skill">
  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:

  <Tabs>
    <Tab title="Claude Code">
      Add the marketplace and install:

      ```
      /plugin marketplace add Lightprotocol/skills
      /plugin install solana-rent-free-dev
      ```
    </Tab>

    <Tab title="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`
    </Tab>

    <Tab title="Any Agent">
      ```
      npx skills add Lightprotocol/skills
      ```
    </Tab>
  </Tabs>

  For orchestration, install the [general skill](https://zkcompression.com/skill.md):

  ```bash theme={null}
  npx skills add https://zkcompression.com
  ```
</Accordion>

<Tabs>
  <Tab title="Guide">
    ## Setup

    ```bash theme={null}
    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](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets) for runnable setup.

    ```typescript theme={null}
    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);
    ```

    ## One-time: Create interface PDA to existing SPL Mint

    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.

    <Note>
      Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/register-spl-mint.ts).
    </Note>

    Check if the interface already exists:

    ```typescript theme={null}
    import { getSplInterfaceInfos } from "@lightprotocol/compressed-token";

    try {
      const infos = await getSplInterfaceInfos(rpc, mint);
      const exists = infos.some((i) => i.isInitialized);
      console.log("Interface exists:", exists);
    } catch {
      console.log("No interface registered for this mint.");
    }
    ```

    Register:

    <Tabs>
      <Tab title="Instruction">
        ```typescript theme={null}
        import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
        import { LightTokenProgram } from "@lightprotocol/compressed-token";
        import { TOKEN_PROGRAM_ID } from "@solana/spl-token";

        const ix = await LightTokenProgram.createSplInterface({
          feePayer: payer.publicKey,
          mint,
          tokenProgramId: TOKEN_PROGRAM_ID,
        });

        const tx = new Transaction().add(ix);
        await sendAndConfirmTransaction(rpc, tx, [payer]);
        ```
      </Tab>

      <Tab title="Action">
        ```typescript theme={null}
        import { createSplInterface } from "@lightprotocol/compressed-token";

        await createSplInterface(rpc, payer, mint);
        ```
      </Tab>
    </Tabs>

    <Accordion title="Or: Create a new SPL mint with interface">
      Use `createMintInterface` with `TOKEN_PROGRAM_ID` to create a new SPL mint and register the interface in one transaction:

      ```typescript theme={null}
      import { createMintInterface } from "@lightprotocol/compressed-token/unified";
      import { TOKEN_PROGRAM_ID } from "@solana/spl-token";

      const { mint } = await createMintInterface(
        rpc, payer, payer, null, 9, undefined, undefined, TOKEN_PROGRAM_ID
      );
      ```
    </Accordion>

    ### Receive Payments

    <Note>
      Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/receive.ts).
    </Note>

    Load creates the associated token account (ATA) if needed and loads any compressed state into it. Share the ATA address with the sender.

    <Info>
      **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.
    </Info>

    <Tabs>
      <Tab title="Instruction">
        ```typescript theme={null}
        import { Transaction } 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);

          // sign and send ...
        }
        ```
      </Tab>

      <Tab title="Action">
        ```typescript theme={null}
        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);
        ```
      </Tab>
    </Tabs>

    <Accordion title="Compare to SPL">
      ```typescript theme={null}
      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
      );
      ```
    </Accordion>

    ### Send Payments

    <Note>
      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).
    </Note>

    <Tabs>
      <Tab title="Instruction">
        ```typescript theme={null}
        import { Transaction } 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);

          // sign and send ...
        }
        ```

        Your app logic may require you to create a single sign request for your user. Here's how to do this:

        <Accordion title="Sign all transactions together">
          ```typescript theme={null}
          const transactions = instructions.map((ixs) => new Transaction().add(...ixs));

          // One approval for all
          const signed = await wallet.signAllTransactions(transactions);

          for (const tx of signed) {
            // send...
            await sendAndConfirmTransaction(rpc, tx);
          }
          ```
        </Accordion>

        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.

        <Accordion title="Optimize sending (parallel conditional loads, then transfer)">
          ```typescript theme={null}
          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);
          ```
        </Accordion>

        <Accordion title="Compare to SPL">
          ```typescript theme={null}
          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)
          );
          ```
        </Accordion>
      </Tab>

      <Tab title="Action">
        ```typescript theme={null}
        import {
          getAssociatedTokenAddressInterface,
          transferInterface,
        } from "@lightprotocol/compressed-token/unified";

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

        // Handles loading, creates recipient ATA, transfers.
        await transferInterface(rpc, payer, sourceAta, mint, recipient, owner, amount);
        ```

        <Accordion title="Compare to SPL">
          ```typescript theme={null}
          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);
          ```
        </Accordion>
      </Tab>
    </Tabs>

    ### Show Balance

    <Note>
      Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/get-balance.ts).
    </Note>

    ```typescript theme={null}
    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);
    ```

    <Accordion title="Compare to SPL">
      ```typescript theme={null}
      import { getAccount } from "@solana/spl-token";

      const account = await getAccount(connection, ata);

      console.log(account.amount);
      ```
    </Accordion>

    ### Transaction History

    <Note>
      Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/get-history.ts).
    </Note>

    ```typescript theme={null}
    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.

    <Accordion title="Compare to SPL">
      ```typescript theme={null}
      const signatures = await connection.getSignaturesForAddress(ata);
      ```
    </Accordion>

    ### Wrap from SPL

    Wrap tokens from SPL/Token-2022 accounts to light-token ATA.

    <Note>
      Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/wrap.ts).
    </Note>

    <Tabs>
      <Tab title="Instruction">
        ```typescript theme={null}
        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
          )
        );
        ```
      </Tab>

      <Tab title="Action">
        ```typescript theme={null}
        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);
        ```
      </Tab>
    </Tabs>

    ### 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.

    <Note>
      Find a full code example [here](https://github.com/Lightprotocol/examples-light-token/blob/main/toolkits/payments-and-wallets/unwrap.ts).
    </Note>

    <Tabs>
      <Tab title="Instruction">
        ```typescript theme={null}
        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]);
        }
        ```
      </Tab>

      <Tab title="Action">
        ```typescript theme={null}
        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);
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="AI Prompt">
    <Prompt description="Integrate light-token APIs for stablecoin payments" actions={["copy", "cursor"]}>
      {`---
            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/toolkits/for-payments
            - Skills and resources index: https://zkcompression.com/skill.md
            - Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets
            - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js
            - Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets

            SPL → Light Token API mapping:
            | Operation    | SPL                               | Light Token                              |
            | Receive      | getOrCreateAssociatedTokenAccount() | createLoadAtaInstructions() / loadAta()  |
            | Transfer     | createTransferInstruction()        | createTransferInterfaceInstructions()    |
            | 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`}
    </Prompt>

    ```text theme={null}
    ---
    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/toolkits/for-payments
    - Skills and resources index: https://zkcompression.com/skill.md
    - Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/payments-and-wallets
    - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js
    - Full examples: https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/payments-and-wallets

    SPL → Light Token API mapping:
    | Operation    | SPL                               | Light Token                              |
    | Receive      | getOrCreateAssociatedTokenAccount() | createLoadAtaInstructions() / loadAta()  |
    | Transfer     | createTransferInstruction()        | createTransferInterfaceInstructions()    |
    | 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
    ```
  </Tab>
</Tabs>
