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

# Freeze and Thaw Light Token Accounts

> Rust client guide to freeze and thaw Light Token accounts. Includes step-by-step implementation and full code examples.

export const lightThawRustCode = ["use light_token::instruction::Thaw;", "", "let ix = Thaw {", "    token_account: ata,", "    mint,", "    freeze_authority: payer.pubkey(),", "}", ".instruction()?;"].join("\n");

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

export const lightFreezeRustCode = ["use light_token::instruction::Freeze;", "", "let ix = Freeze {", "    token_account: ata,", "    mint,", "    freeze_authority: payer.pubkey(),", "}", ".instruction()?;"].join("\n");

export const splFreezeRustCode = ["use spl_token::instruction::freeze_account;", "", "let ix = freeze_account(", "    &spl_token::id(),", "    &account,", "    &mint,", "    &freeze_authority,", "    &[],", ")?;"].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. Freeze prevents all transfers or token burns from a specific Light Token account.
2. Once frozen, the account cannot send tokens, receive tokens, or be closed until it is thawed.
3. Thaw re-enables transfers on a frozen Light Token account.
4. Only the freeze authority (set at mint creation) can freeze or thaw accounts.
5. If the freeze authority is revoked (set to null) on the mint account, tokens can never be frozen.

<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">
    <Tabs>
      <Tab title="Freeze">
        <CodeCompare firstCode={lightFreezeRustCode} secondCode={splFreezeRustCode} firstLabel="light-token" secondLabel="SPL" language="rust" />
      </Tab>

      <Tab title="Thaw">
        <CodeCompare firstCode={lightThawRustCode} secondCode={splThawRustCode} firstLabel="light-token" secondLabel="SPL" language="rust" />
      </Tab>
    </Tabs>

    <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>
            ### Freeze or thaw Light Token accounts

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

            <Tabs>
              <Tab title="Freeze">
                <Tabs>
                  <Tab title="Instruction">
                    ```rust theme={null}
                    use borsh::BorshDeserialize;
                    use light_client::rpc::Rpc;
                    use light_token::instruction::Freeze;
                    use rust_client::{setup, SetupContext};
                    use solana_sdk::signer::Signer;

                    #[tokio::main]
                    async fn main() -> Result<(), Box<dyn std::error::Error>> {
                        // Setup creates mint, associated token account with tokens, and approves delegate
                        let SetupContext {
                            mut rpc,
                            payer,
                            mint,
                            associated_token_account,
                            ..
                        } = setup().await;

                        // freeze_authority must match what was set during mint creation.
                        let freeze_instruction = Freeze {
                            token_account: associated_token_account,
                            mint,
                            freeze_authority: payer.pubkey(),
                        }
                        .instruction()?;

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

                        let data = rpc.get_account(associated_token_account).await?.ok_or("Account not found")?;
                        let token = light_token_interface::state::Token::deserialize(&mut &data.data[..])?;
                        println!("State: {:?} Tx: {sig}", token.state);

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

              <Tab title="Thaw">
                <Tabs>
                  <Tab title="Instruction">
                    ```rust theme={null}
                    use borsh::BorshDeserialize;
                    use light_client::rpc::Rpc;
                    use light_token::instruction::Thaw;
                    use rust_client::{setup_frozen, SetupContext};
                    use solana_sdk::signer::Signer;

                    #[tokio::main]
                    async fn main() -> Result<(), Box<dyn std::error::Error>> {
                        // Setup creates mint, associated token account with tokens, and freezes account
                        let SetupContext {
                            mut rpc,
                            payer,
                            mint,
                            associated_token_account,
                            ..
                        } = setup_frozen().await;

                        let thaw_instruction = Thaw {
                            token_account: associated_token_account,
                            mint,
                            freeze_authority: payer.pubkey(),
                        }
                        .instruction()?;

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

                        let data = rpc.get_account(associated_token_account).await?.ok_or("Account not found")?;
                        let token = light_token_interface::state::Token::deserialize(&mut &data.data[..])?;
                        println!("State: {:?} Tx: {sig}", token.state);

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

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

                    ## Freeze and thaw Light Token accounts

                    Context:
                    - Guide: https://zkcompression.com/light-token/cookbook/freeze-thaw
                    - 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)

                    - Freeze: prevents all transfers or token burns from a specific Light Token account
                    - Thaw: re-enables transfers on a frozen Light Token account
                    - Only the freeze authority (set at mint creation) can freeze or thaw accounts

                    SPL equivalent: spl_token::instruction::freeze_account / thaw_account → Light Token: Freeze / Thaw

                    ### 1. Index project
                    - Grep \`light_token::|light_token_client::|solana_sdk|Keypair|async|Freeze|Thaw|freeze_account|thaw_account\` across src/
                    - Glob \`**/*.rs\` for project structure
                    - Identify: RPC setup, existing token ops, entry point for freeze/thaw
                    - 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 freeze/thaw 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: Freeze and thaw Light Token accounts
        allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
        ---

        ## Freeze and thaw Light Token accounts

        Context:
        - Guide: https://zkcompression.com/light-token/cookbook/freeze-thaw
        - 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)

        - Freeze: prevents all transfers or token burns from a specific Light Token account
        - Thaw: re-enables transfers on a frozen Light Token account
        - Only the freeze authority (set at mint creation) can freeze or thaw accounts

        SPL equivalent: spl_token::instruction::freeze_account / thaw_account → Light Token: Freeze / Thaw

        ### 1. Index project
        - Grep `light_token::|light_token_client::|solana_sdk|Keypair|async|Freeze|Thaw|freeze_account|thaw_account` across src/
        - Glob `**/*.rs` for project structure
        - Identify: RPC setup, existing token ops, entry point for freeze/thaw
        - 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 freeze/thaw 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="Guide">
        <Note>
          Find [a full code example at the end](#full-code-example).
        </Note>

        <Tabs>
          <Tab title="Freeze">
            <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::FreezeCpi;

                    FreezeCpi {
                        token_account: ctx.accounts.token_account.to_account_info(),
                        mint: ctx.accounts.mint.to_account_info(),
                        freeze_authority: ctx.accounts.freeze_authority.to_account_info(),
                    }
                    .invoke()?;
                    ```
                  </Tab>

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

                    let signer_seeds = authority_seeds!(bump);

                    FreezeCpi {
                        token_account: token_account.clone(),
                        mint: mint.clone(),
                        freeze_authority: freeze_authority.clone(),
                    }
                    .invoke_signed(&[signer_seeds])
                    ```
                  </Tab>
                </Tabs>
              </Step>
            </Steps>
          </Tab>

          <Tab title="Thaw">
            <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::ThawCpi;

                    ThawCpi {
                        token_account: ctx.accounts.token_account.to_account_info(),
                        mint: ctx.accounts.mint.to_account_info(),
                        freeze_authority: ctx.accounts.freeze_authority.to_account_info(),
                    }
                    .invoke()?;
                    ```
                  </Tab>

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

                    let signer_seeds = authority_seeds!(bump);

                    ThawCpi {
                        token_account: token_account.clone(),
                        mint: mint.clone(),
                        freeze_authority: freeze_authority.clone(),
                    }
                    .invoke_signed(&[signer_seeds])
                    ```
                  </Tab>
                </Tabs>
              </Step>
            </Steps>
          </Tab>
        </Tabs>

        # Full Code Example

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

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

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

              declare_id!("JBMzMJX4sqCQfNVbosP2oqP1KZ5ZDWiwYTrupk687qXZ");

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

                  pub fn freeze(ctx: Context<FreezeAccounts>) -> Result<()> {
                      FreezeCpi {
                          token_account: ctx.accounts.token_account.to_account_info(),
                          mint: ctx.accounts.mint.to_account_info(),
                          freeze_authority: ctx.accounts.freeze_authority.to_account_info(),
                      }
                      .invoke()?;
                      Ok(())
                  }
              }

              #[derive(Accounts)]
              pub struct FreezeAccounts<'info> {
                  /// CHECK: Light token program for CPI
                  pub light_token_program: AccountInfo<'info>,
                  /// CHECK: Validated by light-token CPI
                  #[account(mut)]
                  pub token_account: AccountInfo<'info>,
                  /// CHECK: Validated by light-token CPI
                  pub mint: AccountInfo<'info>,
                  pub freeze_authority: Signer<'info>,
              }
              ```

              ```rust test.rs theme={null}
              use anchor_lang::{InstructionData, ToAccountMetas};
              use light_program_test::Rpc;
              use light_token::instruction::LIGHT_TOKEN_PROGRAM_ID;
              use light_token_anchor_freeze::{accounts, instruction::Freeze, ID};
              use solana_sdk::{instruction::Instruction, signer::Signer};
              use test_utils::{mint_tokens, setup_test_env_with_freeze};

              #[tokio::test]
              async fn test_freeze() {
                  let mut env = setup_test_env_with_freeze("light_token_anchor_freeze", ID).await;

                  // Mint tokens first
                  mint_tokens(&mut env.rpc, &env.payer, env.mint_pda, env.associated_token_account, 1_000_000).await;

                  // Call the anchor program to freeze account
                  let ix = Instruction {
                      program_id: ID,
                      accounts: accounts::FreezeAccounts {
                          light_token_program: LIGHT_TOKEN_PROGRAM_ID,
                          token_account: env.associated_token_account,
                          mint: env.mint_pda,
                          freeze_authority: env.freeze_authority,
                      }
                      .to_account_metas(Some(true)),
                      data: Freeze {}.data(),
                  };

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

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

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

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

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

              declare_id!("7j94EF5hSkDLf7R26bjrd8Qc6s3oLAQpcKiF3re8JYw9");

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

                  pub fn thaw(ctx: Context<ThawAccounts>) -> Result<()> {
                      ThawCpi {
                          token_account: ctx.accounts.token_account.to_account_info(),
                          mint: ctx.accounts.mint.to_account_info(),
                          freeze_authority: ctx.accounts.freeze_authority.to_account_info(),
                      }
                      .invoke()?;
                      Ok(())
                  }
              }

              #[derive(Accounts)]
              pub struct ThawAccounts<'info> {
                  /// CHECK: Light token program for CPI
                  pub light_token_program: AccountInfo<'info>,
                  /// CHECK: Validated by light-token CPI
                  #[account(mut)]
                  pub token_account: AccountInfo<'info>,
                  /// CHECK: Validated by light-token CPI
                  pub mint: AccountInfo<'info>,
                  pub freeze_authority: Signer<'info>,
              }
              ```

              ```rust test.rs theme={null}
              use anchor_lang::{InstructionData, ToAccountMetas};
              use light_program_test::Rpc;
              use light_token::instruction::{Freeze, LIGHT_TOKEN_PROGRAM_ID};
              use light_token_anchor_thaw::{accounts, instruction::Thaw, ID};
              use solana_sdk::{instruction::Instruction, signer::Signer};
              use test_utils::{mint_tokens, setup_test_env_with_freeze};

              #[tokio::test]
              async fn test_thaw() {
                  let mut env = setup_test_env_with_freeze("light_token_anchor_thaw", ID).await;

                  // Mint tokens first
                  mint_tokens(&mut env.rpc, &env.payer, env.mint_pda, env.associated_token_account, 1_000_000).await;

                  // Freeze account first using SDK
                  let freeze_ix = Freeze {
                      token_account: env.associated_token_account,
                      mint: env.mint_pda,
                      freeze_authority: env.freeze_authority,
                  }
                  .instruction()
                  .unwrap();

                  env.rpc
                      .create_and_send_transaction(&[freeze_ix], &env.payer.pubkey(), &[&env.payer])
                      .await
                      .unwrap();

                  // Call the anchor program to thaw account
                  let ix = Instruction {
                      program_id: ID,
                      accounts: accounts::ThawAccounts {
                          light_token_program: LIGHT_TOKEN_PROGRAM_ID,
                          token_account: env.associated_token_account,
                          mint: env.mint_pda,
                          freeze_authority: env.freeze_authority,
                      }
                      .to_account_metas(Some(true)),
                      data: Thaw {}.data(),
                  };

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

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

      <Tab title="AI Prompt">
        <Prompt description="Add freeze and thaw CPI to an Anchor program" actions={["copy", "cursor"]}>
          {`---
                    description: Add freeze and thaw 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 freeze and thaw CPI to an Anchor program

                    Context:
                    - Guide: https://zkcompression.com/light-token/cookbook/freeze-thaw
                    - 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 (FreezeCpi, ThawCpi)
                    - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/freeze

                    Key CPI structs: \`light_token::instruction::FreezeCpi\`, \`light_token::instruction::ThawCpi\`

                    ### 1. Index project
                    - Grep \`declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|freeze|thaw|authority\` across src/
                    - Glob \`**/*.rs\` and \`**/Cargo.toml\` for project structure
                    - Identify: program ID, existing instructions, account structs, freeze authority
                    - 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 both Freeze and Thaw CPI code samples
                    - 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 freeze/thaw to existing program, new program from scratch, migrate from SPL freeze/thaw)
                    - AskUserQuestion: should the freeze authority be an external signer or a PDA? (determines invoke vs invoke_signed)
                    - AskUserQuestion: do you need both freeze and thaw, or just one?
                    - 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 FreezeCpi/ThawCpi structs → call .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 freeze and thaw 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 freeze and thaw CPI to an Anchor program

        Context:
        - Guide: https://zkcompression.com/light-token/cookbook/freeze-thaw
        - 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 (FreezeCpi, ThawCpi)
        - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/freeze

        Key CPI structs: `light_token::instruction::FreezeCpi`, `light_token::instruction::ThawCpi`

        ### 1. Index project
        - Grep `declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|freeze|thaw|authority` across src/
        - Glob `**/*.rs` and `**/Cargo.toml` for project structure
        - Identify: program ID, existing instructions, account structs, freeze authority
        - 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 both Freeze and Thaw CPI code samples
        - 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 freeze/thaw to existing program, new program from scratch, migrate from SPL freeze/thaw)
        - AskUserQuestion: should the freeze authority be an external signer or a PDA? (determines invoke vs invoke_signed)
        - AskUserQuestion: do you need both freeze and thaw, or just one?
        - 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 FreezeCpi/ThawCpi structs → call .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>
</Tabs>

## Related Guides

<CardGroup cols={2}>
  <Card title="Approve and revoke" icon="key" href="/light-token/cookbook/approve-revoke" horizontal />

  <Card title="Delegated transfer" icon="user-check" href="/light-token/cookbook/transfer-delegated" 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>
