> ## Documentation Index
> Fetch the complete documentation index at: https://www.zkcompression.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Light Token Account

> Client and program guide to create Light Token accounts. Includes step-by-step implementation and full code examples.

export const lightCreateTokenAccountCpiCode = ["use light_token::instruction::CreateTokenAccountCpi;", "", "CreateTokenAccountCpi {", "    payer: payer.clone(),", "    account: account.clone(),", "    mint: mint.clone(),", "    owner,", "}", ".rent_free(", "    compressible_config.clone(),", "    rent_sponsor.clone(),", "    system_program.clone(),", "    token_program.key,", ")", ".invoke()?"].join("\n");

export const splCreateTokenAccountCpiCode = ["use spl_token::instruction::initialize_account;", "", "let ix = initialize_account(", "    &spl_token::id(),", "    &account,", "    &mint,", "    &owner,", ")?;", "", "invoke(&ix, &[account, mint, owner])?;"].join("\n");

export const lightCreateTokenAccountMacroCode = ["#[account(", "    mut,", "    seeds = [VAULT_SEED, mint.key().as_ref()],", "    bump,", ")]", "#[light_account(init,", "    token::authority = [VAULT_SEED, self.mint.key()],", "    token::mint = mint,", "    token::owner = vault_authority,", "    token::bump = params.vault_bump", ")]", "pub vault: UncheckedAccount<'info>,"].join("\n");

export const splCreateTokenAccountMacroCode = ["#[account(", "    init,", "    payer = fee_payer,", "    token::mint = mint,", "    token::authority = authority,", ")]", "pub vault: Account<'info, TokenAccount>,"].join("\n");

export const lightCreateTokenAccountRustCode = ["use light_token::instruction::CreateTokenAccount;", "", "let ix = CreateTokenAccount::new(", "    payer.pubkey(),", "    account.pubkey(),", "    mint,", "    owner,", ")", ".instruction()?;"].join("\n");

export const splCreateTokenAccountRustCode = ["use spl_token::instruction::initialize_account;", "", "let ix = initialize_account(", "    &spl_token::id(),", "    &account,", "    &mint,", "    &owner,", ")?;"].join("\n");

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

<Accordion title="Light Rent Config Explained">
  1) A rent sponsor PDA by Light Protocol pays the rent-exemption cost for the account.
  2) Transaction fee payers bump a virtual rent balance when writing to the account, which keeps the account "hot".
  3) "Cold" accounts virtual rent balance below threshold (eg 24h without write bump) get auto-compressed.
  4) The cold account's state is cryptographically preserved on the Solana ledger.
     Users can load a cold account into hot state in-flight when using the account
     again.
</Accordion>

<Accordion title="Agent skill">
  Install or view [dedicated agent skills](/ai-tools/overview#agent-skills).

  ```
  npx skills add Lightprotocol/skills
  ```

  Install orchestrator agent skill or view [skill.md](https://www.zkcompression.com/skill.md):

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

<Tabs>
  <Tab title="Rust Client">
    `CreateTokenAccount` creates an on-chain token account to store token balances of light, SPL, or Token 2022 mints.

    Compare to SPL:

    <CodeCompare firstCode={lightCreateTokenAccountRustCode} secondCode={splCreateTokenAccountRustCode} firstLabel="light-token" secondLabel="SPL" language="rust" />

    <Tabs>
      <Tab title="Guide">
        <Steps>
          <Step>
            ### Prerequisites

            <Accordion title="Dependencies">
              ```toml Cargo.toml theme={null}
              [dependencies]
              light-token = "0.23.0"
              light-client = { version = "0.23.0", features = ["v2"] }
              solana-sdk = "2"
              borsh = "0.10.4"
              tokio = { version = "1", features = ["full"] }
              ```
            </Accordion>

            <Accordion title="Developer Environment">
              <Tabs>
                <Tab title="In-Memory (LightProgramTest)">
                  Test with Lite-SVM (...)

                  ```bash theme={null}
                  # Initialize project
                  cargo init my-light-project
                  cd my-light-project

                  # Run tests
                  cargo test
                  ```

                  ```rust theme={null}
                  use light_program_test::{LightProgramTest, ProgramTestConfig};
                  use solana_sdk::signer::Signer;

                  #[tokio::test]
                  async fn test_example() {
                      // In-memory test environment 
                      let mut rpc = LightProgramTest::new(ProgramTestConfig::default())
                          .await
                          .unwrap();

                      let payer = rpc.get_payer().insecure_clone();
                      println!("Payer: {}", payer.pubkey());
                  }
                  ```
                </Tab>

                <Tab title="Localnet (LightClient)">
                  Connects to a local test validator.

                  <Tabs>
                    <Tab title="npm">
                      ```bash theme={null}
                      npm install -g @lightprotocol/zk-compression-cli
                      ```
                    </Tab>

                    <Tab title="yarn">
                      ```bash theme={null}
                      yarn global add @lightprotocol/zk-compression-cli
                      ```
                    </Tab>

                    <Tab title="pnpm">
                      ```bash theme={null}
                      pnpm add -g @lightprotocol/zk-compression-cli
                      ```
                    </Tab>
                  </Tabs>

                  ```bash theme={null}
                  # Initialize project
                  cargo init my-light-project
                  cd my-light-project

                  # Start local test validator (in separate terminal)
                  light test-validator
                  ```

                  ```rust theme={null}
                  use light_client::rpc::{LightClient, LightClientConfig, Rpc};

                  #[tokio::main]
                  async fn main() -> Result<(), Box<dyn std::error::Error>> {
                      // Connects to http://localhost:8899
                      let rpc = LightClient::new(LightClientConfig::local()).await?;

                      let slot = rpc.get_slot().await?;
                      println!("Current slot: {}", slot);

                      Ok(())
                  }
                  ```
                </Tab>

                <Tab title="Devnet (LightClient)">
                  Replace `<your-api-key>` with your actual API key. [Get your API key here](https://www.helius.dev/zk-compression).

                  ```rust theme={null}
                  use light_client::rpc::{LightClient, LightClientConfig, Rpc};

                  #[tokio::main]
                  async fn main() -> Result<(), Box<dyn std::error::Error>> {
                      let rpc_url = "https://devnet.helius-rpc.com?api-key=<your_api_key>";
                      let rpc = LightClient::new(
                          LightClientConfig::new(rpc_url.to_string(), None, None)
                      ).await?;

                      println!("Connected to Devnet");
                      Ok(())
                  }
                  ```
                </Tab>
              </Tabs>
            </Accordion>
          </Step>

          <Step>
            ### Create Token Account

            <Info>
              Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-libs/token-sdk/src/instruction/create_associated_token_account.rs).
            </Info>

            <Tabs>
              <Tab title="Instruction">
                ```rust theme={null}
                use light_client::rpc::Rpc;
                use light_token::instruction::CreateTokenAccount;
                use rust_client::{setup_spl_mint_context, SplMintContext};
                use solana_sdk::{signature::Keypair, signer::Signer};

                #[tokio::main]
                async fn main() -> Result<(), Box<dyn std::error::Error>> {
                    // Setup creates mint
                    // You can use Light, SPL, or Token-2022 mints to create a light token account.
                    let SplMintContext {
                        mut rpc,
                        payer,
                        mint,
                    } = setup_spl_mint_context().await;

                    let account = Keypair::new();

                    let create_token_account_instruction =
                        CreateTokenAccount::new(payer.pubkey(), account.pubkey(), mint, payer.pubkey())
                            .instruction()?;

                    let sig = rpc
                        .create_and_send_transaction(&[create_token_account_instruction], &payer.pubkey(), &[&payer, &account])
                        .await?;

                    let data = rpc.get_account(account.pubkey()).await?;
                    println!(
                        "Account: {} exists: {} Tx: {sig}",
                        account.pubkey(),
                        data.is_some()
                    );

                    Ok(())
                }
                ```
              </Tab>
            </Tabs>
          </Step>
        </Steps>
      </Tab>

      <Tab title="AI Prompt">
        <Prompt description="Create Light Token account" actions={["copy", "cursor"]}>
          {`---
                    description: Create Light Token account
                    allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
                    ---

                    ## Create Light Token account

                    Context:
                    - Guide: https://zkcompression.com/light-token/cookbook/create-token-account
                    - Skills and resources index: https://zkcompression.com/skill.md
                    - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison
                    - Crates: light-token-client (actions), light-token (instructions), light-client (RPC)

                    SPL equivalent: spl_token::instruction::initialize_account → Light Token: CreateTokenAccount

                    ### 1. Index project
                    - Grep \`light_token::|light_token_client::|solana_sdk|Keypair|async|CreateTokenAccount|initialize_account\` across src/
                    - Glob \`**/*.rs\` for project structure
                    - Identify: RPC setup, existing token ops, entry point for token account creation
                    - Check Cargo.toml for existing light-* dependencies and solana-sdk version
                    - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

                    ### 2. Read references
                    - WebFetch the guide above — follow the Rust Client tab
                    - 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 feature, migrate existing SPL code, add alongside existing)
                    - AskUserQuestion: does the project already have token account operations to extend, or is this greenfield?
                    - AskUserQuestion: action-level API (high-level, fewer lines) or instruction-level API (low-level, full control)?
                    - 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 Rpc/signer setup is compatible with the cookbook prerequisites (light_client::rpc::Rpc, solana_sdk::signature::Keypair)
                    - 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 \`cargo add light-token-client light-token light-client --features light-client/v2\`
                    - Follow the cookbook guide and the approved plan
                    - Write/Edit to create or modify files
                    - TaskUpdate to mark each step done

                    ### 6. Verify
                    - Bash \`cargo check\`
                    - Bash \`cargo test\` if tests exist
                    - 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: Create Light Token account
        allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
        ---

        ## Create Light Token account

        Context:
        - Guide: https://zkcompression.com/light-token/cookbook/create-token-account
        - Skills and resources index: https://zkcompression.com/skill.md
        - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison
        - Crates: light-token-client (actions), light-token (instructions), light-client (RPC)

        SPL equivalent: spl_token::instruction::initialize_account → Light Token: CreateTokenAccount

        ### 1. Index project
        - Grep `light_token::|light_token_client::|solana_sdk|Keypair|async|CreateTokenAccount|initialize_account` across src/
        - Glob `**/*.rs` for project structure
        - Identify: RPC setup, existing token ops, entry point for token account creation
        - Check Cargo.toml for existing light-* dependencies and solana-sdk version
        - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

        ### 2. Read references
        - WebFetch the guide above — follow the Rust Client tab
        - 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 feature, migrate existing SPL code, add alongside existing)
        - AskUserQuestion: does the project already have token account operations to extend, or is this greenfield?
        - AskUserQuestion: action-level API (high-level, fewer lines) or instruction-level API (low-level, full control)?
        - 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 Rpc/signer setup is compatible with the cookbook prerequisites (light_client::rpc::Rpc, solana_sdk::signature::Keypair)
        - 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 `cargo add light-token-client light-token light-client --features light-client/v2`
        - Follow the cookbook guide and the approved plan
        - Write/Edit to create or modify files
        - TaskUpdate to mark each step done

        ### 6. Verify
        - Bash `cargo check`
        - Bash `cargo test` if tests exist
        - 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>
  </Tab>

  <Tab title="Program">
    <Tabs>
      <Tab title="CPI">
        <Tabs>
          <Tab title="Guide">
            Compare to SPL:

            <CodeCompare firstCode={lightCreateTokenAccountCpiCode} secondCode={splCreateTokenAccountCpiCode} firstLabel="light-token" secondLabel="SPL" language="rust" />

            <Note>
              Find [a full code example at the end](#full-code-example).
            </Note>

            <Steps>
              <Step>
                ### Build Account Infos and CPI the Light Token Program

                Use `invoke` for external signers or `invoke_signed` when the authority is a PDA.

                <Tabs>
                  <Tab title="invoke (External signer)">
                    ```rust theme={null}
                    use light_token::instruction::CreateTokenAccountCpi;

                    CreateTokenAccountCpi {
                        payer: payer.clone(),
                        account: account.clone(),
                        mint: mint.clone(),
                        owner,
                    }
                    .rent_free(
                        compressible_config.clone(),
                        rent_sponsor.clone(),
                        system_program.clone(),
                        token_program.key, // light token program
                    )
                    .invoke()
                    ```

                    <table>
                      <colgroup>
                        <col style={{width: '25%', textAlign: 'left'}} />

                        <col style={{width: '55%'}} />
                      </colgroup>

                      <thead>
                        <tr>
                          <th style={{textAlign: 'left'}} />

                          <th style={{textAlign: 'left'}} />

                          <th style={{textAlign: 'left'}} />
                        </tr>
                      </thead>

                      <tbody>
                        <tr>
                          <td style={{textAlign: 'left'}}><strong>Payer</strong></td>
                          <td>signer, mutable</td>

                          <td>
                            * Pays initial rent per epoch, transaction fee and compression incentive.<br />
                            * Does NOT pay rent exemption (paid by the light token program, `rent_sponsor`).
                          </td>
                        </tr>

                        <tr>
                          <td style={{textAlign: 'left'}}><strong>light-token Account</strong></td>
                          <td>signer\*, mutable</td>

                          <td>
                            * The light-token account being created.<br />
                            * \*Must be signer for `invoke()`. For `invoke_signed()`, program signs via PDA seeds.
                          </td>
                        </tr>

                        <tr>
                          <td style={{textAlign: 'left'}}><strong>Mint</strong></td>
                          <td>-</td>
                          <td>The SPL or light-mint token mint.</td>
                        </tr>

                        <tr>
                          <td style={{textAlign: 'left'}}><strong>Owner</strong></td>
                          <td>Pubkey</td>
                          <td>The owner of the token account. Controls transfers and other operations.</td>
                        </tr>
                      </tbody>
                    </table>
                  </Tab>

                  <Tab title="invoke_signed (PDA signer)">
                    ```rust theme={null}
                    use light_token::instruction::CreateTokenAccountCpi;

                    let signer_seeds = authority_seeds!(authority_bump);

                    CreateTokenAccountCpi {
                        payer: payer.clone(),
                        account: account.clone(),
                        mint: mint.clone(),
                        owner,
                    }
                    .rent_free(
                        compressible_config.clone(),
                        rent_sponsor.clone(),
                        system_program.clone(),
                        program_id,
                    )
                    .invoke_signed(signer_seeds)
                    ```
                  </Tab>
                </Tabs>
              </Step>
            </Steps>

            # Full Code Example

            <Info>
              View the [source code](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-libs/token-sdk/src/instruction/create_associated_token_account.rs) and [full example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/create-token-account) with shared test utilities.
            </Info>

            <CodeGroup>
              ```rust lib.rs theme={null}
              #![allow(unexpected_cfgs, deprecated)]

              use anchor_lang::prelude::*;
              use light_token::instruction::CreateTokenAccountCpi;

              declare_id!("zXK1CnWj4WFfFHCArxxr4sh3Qqx2p3oui8ahqpjArgS");

              #[program]
              pub mod light_token_anchor_create_token_account {
                  use super::*;

                  pub fn create_token_account(ctx: Context<CreateTokenAccountAccounts>, owner: Pubkey) -> Result<()> {
                      CreateTokenAccountCpi {
                          payer: ctx.accounts.payer.to_account_info(),
                          account: ctx.accounts.account.to_account_info(),
                          mint: ctx.accounts.mint.to_account_info(),
                          owner,
                      }
                      .rent_free(
                          ctx.accounts.compressible_config.to_account_info(),
                          ctx.accounts.rent_sponsor.to_account_info(),
                          ctx.accounts.system_program.to_account_info(),
                          &ctx.accounts.light_token_program.key(),
                      )
                      .invoke()?;
                      Ok(())
                  }
              }

              #[derive(Accounts)]
              pub struct CreateTokenAccountAccounts<'info> {
                  #[account(mut)]
                  pub payer: Signer<'info>,
                  /// CHECK: Validated by light-token CPI
                  #[account(mut)]
                  pub account: Signer<'info>,
                  /// CHECK: Validated by light-token CPI
                  pub mint: AccountInfo<'info>,
                  /// CHECK: Validated by light-token CPI
                  pub compressible_config: AccountInfo<'info>,
                  /// CHECK: Validated by light-token CPI
                  #[account(mut)]
                  pub rent_sponsor: AccountInfo<'info>,
                  pub system_program: Program<'info, System>,
                  /// CHECK: Light token program for CPI
                  pub light_token_program: AccountInfo<'info>,
              }
              ```

              ```rust test.rs theme={null}
              use anchor_lang::{InstructionData, ToAccountMetas};
              use light_client::indexer::AddressWithTree;
              use light_program_test::{Indexer, LightProgramTest, ProgramTestConfig, Rpc};
              use light_token_anchor_create_token_account::{accounts, instruction::CreateTokenAccount, ID};
              use light_token::instruction::{
                  CreateMint, CreateMintParams, config_pda, derive_mint_compressed_address,
                  find_mint_address, rent_sponsor_pda, LIGHT_TOKEN_PROGRAM_ID,
                  DEFAULT_RENT_PAYMENT, DEFAULT_WRITE_TOP_UP,
              };
              use anchor_lang::system_program;
              use solana_sdk::{
                  instruction::Instruction,
                  signature::Keypair,
                  signer::Signer,
              };

              #[tokio::test]
              async fn test_create_token_account() {
                  let config = ProgramTestConfig::new_v2(
                      true,
                      Some(vec![("light_token_anchor_create_token_account", ID)]),
                  );
                  let mut rpc = LightProgramTest::new(config).await.unwrap();
                  let payer = rpc.get_payer().insecure_clone();

                  // Create a mint first
                  let mint_seed = Keypair::new();
                  let mint_authority = payer.pubkey();
                  let decimals = 9u8;

                  let address_tree = rpc.get_address_tree_v2();
                  let output_queue = rpc.get_random_state_tree_info().unwrap().queue;

                  let compression_address =
                      derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree.tree);
                  let (mint_pda, bump) = find_mint_address(&mint_seed.pubkey());

                  let rpc_result = rpc
                      .get_validity_proof(
                          vec![],
                          vec![AddressWithTree {
                              address: compression_address,
                              tree: address_tree.tree,
                          }],
                          None,
                      )
                      .await
                      .unwrap()
                      .value;

                  let params = CreateMintParams {
                      decimals,
                      address_merkle_tree_root_index: rpc_result.addresses[0].root_index,
                      mint_authority,
                      proof: rpc_result.proof.0.unwrap(),
                      compression_address,
                      mint: mint_pda,
                      bump,
                      freeze_authority: None,
                      extensions: None,
                      rent_payment: DEFAULT_RENT_PAYMENT,
                      write_top_up: DEFAULT_WRITE_TOP_UP,
                  };

                  let create_mint_ix = CreateMint::new(
                      params,
                      mint_seed.pubkey(),
                      payer.pubkey(),
                      address_tree.tree,
                      output_queue,
                  )
                  .instruction()
                  .unwrap();

                  rpc.create_and_send_transaction(&[create_mint_ix], &payer.pubkey(), &[&payer, &mint_seed])
                      .await
                      .unwrap();

                  // You can use light, spl, t22 mints to create a light token account.
                  // Create a token account
                  let token_account = Keypair::new();
                  let owner = payer.pubkey();
                  let compressible_config = config_pda();
                  let rent_sponsor = rent_sponsor_pda();

                  let ix = Instruction {
                      program_id: ID,
                      accounts: accounts::CreateTokenAccountAccounts {
                          payer: payer.pubkey(),
                          account: token_account.pubkey(),
                          mint: mint_pda,
                          compressible_config,
                          rent_sponsor,
                          system_program: system_program::ID,
                          light_token_program: LIGHT_TOKEN_PROGRAM_ID,
                      }
                      .to_account_metas(Some(true)),
                      data: CreateTokenAccount { owner }.data(),
                  };

                  let sig = rpc
                      .create_and_send_transaction(&[ix], &payer.pubkey(), &[&payer, &token_account])
                      .await
                      .unwrap();

                  println!("Tx: {}", sig);
              }
              ```
            </CodeGroup>
          </Tab>

          <Tab title="AI Prompt">
            <Prompt description="Add create-token-account CPI to an Anchor program" actions={["copy", "cursor"]}>
              {`---
                            description: Add create-token-account CPI to an Anchor program
                            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
                            ---

                            ## Add create-token-account CPI to an Anchor program

                            Context:
                            - Guide: https://zkcompression.com/light-token/cookbook/create-token-account
                            - Skills and resources index: https://zkcompression.com/skill.md
                            - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison
                            - Crate: light-token (CreateTokenAccountCpi)
                            - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/create-token-account

                            Key CPI struct: \`light_token::instruction::CreateTokenAccountCpi\`

                            ### 1. Index project
                            - Grep \`declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|token_account|vault|owner|mint\` across src/
                            - Glob \`**/*.rs\` and \`**/Cargo.toml\` for project structure
                            - Identify: program ID, existing instructions, account structs, vault/token account patterns
                            - Read Cargo.toml — note existing dependencies and framework version
                            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

                            ### 2. Read references
                            - WebFetch the guide above — review the CPI tab under Program: token account creation with .rent_free() chain
                            - 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? (add create-token-account to existing program, new program from scratch, migrate from SPL token account init)
                            - AskUserQuestion: should the payer be an external signer or a PDA? (determines invoke vs invoke_signed)
                            - 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
                            - Follow the guide's step order: Build CreateTokenAccountCpi → .rent_free() → .invoke() or .invoke_signed()
                            - 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 \`cargo add light-token anchor-lang@0.31\`
                            - Follow the guide and the approved plan
                            - Write/Edit to create or modify files
                            - TaskUpdate to mark each step done

                            ### 6. Verify
                            - Bash \`anchor build\`
                            - Bash \`anchor test\` if tests exist
                            - 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: Add create-token-account CPI to an Anchor program
            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
            ---

            ## Add create-token-account CPI to an Anchor program

            Context:
            - Guide: https://zkcompression.com/light-token/cookbook/create-token-account
            - Skills and resources index: https://zkcompression.com/skill.md
            - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison
            - Crate: light-token (CreateTokenAccountCpi)
            - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/create-token-account

            Key CPI struct: `light_token::instruction::CreateTokenAccountCpi`

            ### 1. Index project
            - Grep `declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|token_account|vault|owner|mint` across src/
            - Glob `**/*.rs` and `**/Cargo.toml` for project structure
            - Identify: program ID, existing instructions, account structs, vault/token account patterns
            - Read Cargo.toml — note existing dependencies and framework version
            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

            ### 2. Read references
            - WebFetch the guide above — review the CPI tab under Program: token account creation with .rent_free() chain
            - 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? (add create-token-account to existing program, new program from scratch, migrate from SPL token account init)
            - AskUserQuestion: should the payer be an external signer or a PDA? (determines invoke vs invoke_signed)
            - 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
            - Follow the guide's step order: Build CreateTokenAccountCpi → .rent_free() → .invoke() or .invoke_signed()
            - 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 `cargo add light-token anchor-lang@0.31`
            - Follow the guide and the approved plan
            - Write/Edit to create or modify files
            - TaskUpdate to mark each step done

            ### 6. Verify
            - Bash `anchor build`
            - Bash `anchor test` if tests exist
            - 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>
      </Tab>

      <Tab title="Anchor Macros">
        <Tabs>
          <Tab title="Guide">
            Compare to SPL:

            <CodeCompare firstCode={lightCreateTokenAccountMacroCode} secondCode={splCreateTokenAccountMacroCode} firstLabel="light-token" secondLabel="SPL" language="rust" />

            <Note>
              Find [a full code example at the end](#full-code-example-1).
            </Note>

            <Steps>
              <Step>
                ### Dependencies

                ```toml theme={null}
                [dependencies]
                light-sdk = { version = "0.23.0", features = ["anchor", "v2", "cpi-context"] }
                light-sdk-macros = "0.23.0"
                light-compressible = "0.6.0"
                anchor-lang = "0.31"
                ```
              </Step>

              <Step>
                ### Program

                Add `#[light_program]` above `#[program]`:

                ```rust theme={null}
                use light_sdk_macros::light_program;

                #[light_program]
                #[program]
                pub mod light_token_macro_create_token_account {
                    use super::*;

                    pub fn create_token_account<'info>(
                        ctx: Context<'_, '_, '_, 'info, CreateTokenAccount<'info>>,
                        params: CreateTokenAccountParams,
                    ) -> Result<()> {
                        Ok(())
                    }
                }
                ```
              </Step>

              <Step>
                ### Accounts struct

                Derive `LightAccounts` on your `Accounts` struct and add `#[light_account(...)]` next to `#[account(...)]`.

                ```rust theme={null}
                /// CHECK: Validated by light-token CPI
                #[account(
                    mut,
                    seeds = [VAULT_SEED, mint.key().as_ref()],
                    bump,
                )]
                #[light_account(init,
                    token::authority = [VAULT_SEED, self.mint.key()],
                    token::mint = mint,
                    token::owner = vault_authority,
                    token::bump = params.vault_bump
                )]
                pub vault: UncheckedAccount<'info>,
                ```
              </Step>
            </Steps>

            # Full code example

            <Info>
              View the [source code](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-libs/token-sdk/src/instruction/create_associated_token_account.rs) and [full example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-token-account) with shared test utilities.
            </Info>

            <CodeGroup>
              ```rust lib.rs theme={null}
              #![allow(deprecated)]

              use anchor_lang::prelude::*;
              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");

              pub const LIGHT_CPI_SIGNER: CpiSigner =
                  derive_light_cpi_signer!("9p5BUDtVmRRJqp2sN73ZUZDbaYtYvEWuxzrHH3A2ni9y");

              pub const VAULT_AUTH_SEED: &[u8] = b"vault_auth";
              pub const VAULT_SEED: &[u8] = b"vault";

              #[derive(AnchorSerialize, AnchorDeserialize, Clone)]
              pub struct CreateTokenVaultParams {
                  pub create_accounts_proof: CreateAccountsProof,
                  pub vault_bump: u8,
              }

              #[derive(Accounts, LightAccounts)]
              #[instruction(params: CreateTokenVaultParams)]
              pub struct CreateTokenVault<'info> {
                  #[account(mut)]
                  pub fee_payer: Signer<'info>,

                  /// CHECK: Token mint for the vault
                  pub mint: AccountInfo<'info>,

                  /// CHECK: Validated by seeds constraint
                  #[account(
                      seeds = [VAULT_AUTH_SEED],
                      bump,
                  )]
                  pub vault_authority: UncheckedAccount<'info>,

                  /// CHECK: Validated by seeds constraint and light_account macro
                  #[account(
                      mut,
                      seeds = [VAULT_SEED, mint.key().as_ref()],
                      bump,
                  )]
                  #[light_account(
                      init,
                      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 = LIGHT_TOKEN_CONFIG)]
                  pub light_token_config: AccountInfo<'info>,

                  /// CHECK: Validated by address constraint
                  #[account(mut, address = LIGHT_TOKEN_RENT_SPONSOR)]
                  pub light_token_rent_sponsor: AccountInfo<'info>,

                  /// CHECK: Light token CPI authority
                  pub light_token_cpi_authority: AccountInfo<'info>,

                  /// CHECK: Light token program for CPI
                  pub light_token_program: AccountInfo<'info>,

                  pub system_program: Program<'info, System>,
              }

              #[light_program]
              #[program]
              pub mod light_token_macro_create_token_account {
                  use super::*;

                  #[allow(unused_variables)]
                  pub fn create_token_vault<'info>(
                      ctx: Context<'_, '_, '_, 'info, CreateTokenVault<'info>>,
                      params: CreateTokenVaultParams,
                  ) -> Result<()> {
                      Ok(())
                  }
              }
              ```

              ```rust test.rs theme={null}
              use anchor_lang::{InstructionData, ToAccountMetas};
              use light_client::interface::get_create_accounts_proof;
              use light_program_test::{LightProgramTest, ProgramTestConfig, Rpc};
              use light_token::instruction::LIGHT_TOKEN_PROGRAM_ID;
              use solana_instruction::Instruction;
              use solana_pubkey::Pubkey;
              use solana_signer::Signer;
              use test_utils::create_mint;

              /// Test creating a token vault using the `#[light_account(init, token, ...)]` macro.
              ///
              /// This test verifies:
              /// 1. The macro-annotated program compiles correctly
              /// 2. A token vault can be created via the generated CPI
              /// 3. The vault has the correct owner and mint
              #[tokio::test]
              async fn test_create_token_vault() {
                  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::constants::LIGHT_TOKEN_CPI_AUTHORITY;

                  let program_id = light_token_macro_create_token_account::ID;
                  let config = ProgramTestConfig::new_v2(
                      true,
                      Some(vec![("light_token_macro_create_token_account", program_id)]),
                  );

                  let mut rpc = LightProgramTest::new(config).await.unwrap();
                  let payer = rpc.get_payer().insecure_clone();

                  let (mint, _mint_seed) = create_mint(&mut rpc, &payer, None).await;

                  // Derive PDAs
                  let (vault_authority, _auth_bump) =
                      Pubkey::find_program_address(&[VAULT_AUTH_SEED], &program_id);
                  let (vault, vault_bump) =
                      Pubkey::find_program_address(&[VAULT_SEED, mint.as_ref()], &program_id);

                  // Get proof for token-only instruction (empty inputs)
                  let proof_result = get_create_accounts_proof(&rpc, &program_id, vec![])
                      .await
                      .unwrap();

                  // Build instruction accounts
                  let accounts = light_token_macro_create_token_account::accounts::CreateTokenVault {
                      fee_payer: payer.pubkey(),
                      mint,
                      vault_authority,
                      vault,
                      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,
                  };

                  // Build instruction data
                  let instruction_data = light_token_macro_create_token_account::instruction::CreateTokenVault {
                      params: CreateTokenVaultParams {
                          create_accounts_proof: proof_result.create_accounts_proof,
                          vault_bump,
                      },
                  };

                  let instruction = Instruction {
                      program_id,
                      accounts: [
                          accounts.to_account_metas(None),
                          proof_result.remaining_accounts,
                      ]
                      .concat(),
                      data: instruction_data.data(),
                  };

                  // Execute the instruction
                  // Note: This may fail without InitializeRentFreeConfig setup.
                  // The full test requires rent-free config initialization.
                  let result = rpc
                      .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer])
                      .await;

                  // For now, we verify the instruction builds correctly.
                  // Full execution requires additional setup (InitializeRentFreeConfig, etc.)
                  println!("Transaction result: {:?}", result);
              }
              ```
            </CodeGroup>
          </Tab>

          <Tab title="AI Prompt">
            <Prompt description="Create a rent-free token account with Anchor macros" actions={["copy", "cursor"]}>
              {`---
                            description: Create a rent-free token account with Anchor macros
                            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
                            ---

                            ## Create a rent-free token account with Anchor macros

                            Context:
                            - Guide: https://zkcompression.com/light-token/cookbook/create-token-account
                            - Skills and resources index: https://zkcompression.com/skill.md
                            - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison
                            - Crates: light-sdk, light-sdk-macros, light-compressible, anchor-lang
                            - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-token-account

                            Key macros: \`#[light_program]\`, \`LightAccounts\`, \`#[light_account(init, token::...)]\`

                            ### 1. Index project
                            - Grep \`#\[program\]|anchor_lang|Account<|Accounts|seeds|init|payer|vault|token_account\` across src/
                            - Glob \`**/*.rs\` and \`**/Cargo.toml\` for project structure
                            - Identify: existing program module, account structs, vault/token account patterns
                            - Read Cargo.toml — note existing dependencies and framework version
                            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

                            ### 2. Read references
                            - WebFetch the guide above — review the Anchor Macros tab under Program
                            - 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 program from scratch, add rent-free token account to existing program, migrate from SPL token account init)
                            - Summarize findings and wait for user confirmation before implementing

                            ### 4. Create plan
                            - Based on steps 1–3, draft an implementation plan
                            - Follow the guide's step order: Dependencies → Program Module (#[light_program]) → Accounts Struct (#[light_account(init, token::...)])
                            - 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 \`cargo add light-sdk@0.23 --features anchor,v2,cpi-context\` and \`cargo add light-sdk-macros@0.23 light-compressible@0.6 anchor-lang@0.31\`
                            - Follow the guide and the approved plan
                            - Write/Edit to create or modify files
                            - TaskUpdate to mark each step done

                            ### 6. Verify
                            - Bash \`anchor build\`
                            - Bash \`anchor test\` if tests exist
                            - 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: Create a rent-free token account with Anchor macros
            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
            ---

            ## Create a rent-free token account with Anchor macros

            Context:
            - Guide: https://zkcompression.com/light-token/cookbook/create-token-account
            - Skills and resources index: https://zkcompression.com/skill.md
            - SPL to Light reference: https://zkcompression.com/api-reference/solana-to-light-comparison
            - Crates: light-sdk, light-sdk-macros, light-compressible, anchor-lang
            - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-token-account

            Key macros: `#[light_program]`, `LightAccounts`, `#[light_account(init, token::...)]`

            ### 1. Index project
            - Grep `#\[program\]|anchor_lang|Account<|Accounts|seeds|init|payer|vault|token_account` across src/
            - Glob `**/*.rs` and `**/Cargo.toml` for project structure
            - Identify: existing program module, account structs, vault/token account patterns
            - Read Cargo.toml — note existing dependencies and framework version
            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

            ### 2. Read references
            - WebFetch the guide above — review the Anchor Macros tab under Program
            - 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 program from scratch, add rent-free token account to existing program, migrate from SPL token account init)
            - Summarize findings and wait for user confirmation before implementing

            ### 4. Create plan
            - Based on steps 1–3, draft an implementation plan
            - Follow the guide's step order: Dependencies → Program Module (#[light_program]) → Accounts Struct (#[light_account(init, token::...)])
            - 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 `cargo add light-sdk@0.23 --features anchor,v2,cpi-context` and `cargo add light-sdk-macros@0.23 light-compressible@0.6 anchor-lang@0.31`
            - Follow the guide and the approved plan
            - Write/Edit to create or modify files
            - TaskUpdate to mark each step done

            ### 6. Verify
            - Bash `anchor build`
            - Bash `anchor test` if tests exist
            - 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>
      </Tab>
    </Tabs>
  </Tab>
</Tabs>

## Related Guides

<CardGroup cols={2}>
  <Card title="Transfer interface" icon="arrow-right-left" href="/light-token/cookbook/transfer-interface" horizontal />

  <Card title="Wrap and unwrap" icon="rotate" href="/light-token/cookbook/wrap-unwrap" horizontal />
</CardGroup>

***

## Didn't find what you were looking for?

<Callout type="info">
  Reach out! [Telegram](https://t.me/swen_light) | [email](mailto:support@lightprotocol.com) | [Discord](https://discord.com/invite/7cJ8BhAXhu)
</Callout>
