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

# Transfer Checked

> Program CPI guide for Light Token transfer with decimal validation. Includes step-by-step implementation and full code examples.

1. TransferChecked validates that the decimals parameter matches the mint's decimals.
2. Use `TransferCheckedCpi` for Light-to-Light transfers in on-chain programs when you need decimal verification without interface routing.
3. For transfers involving SPL or Token 2022 accounts, use [Transfer Interface](/light-token/cookbook/transfer-interface) instead. It uses `transferChecked` under the hood.

<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="TypeScript Client">
    `transferChecked` is called automatically under the hood by [`transferInterface`](/light-token/cookbook/transfer-interface) and `createTransferInterfaceInstructions`. You do not need to call it directly.
  </Tab>

  <Tab title="Rust Client">
    `TransferChecked` is called automatically under the hood by [`TransferInterface`](/light-token/cookbook/transfer-interface). You do not need to call it directly.
  </Tab>

  <Tab title="Program">
    <Tabs>
      <Tab title="Guide">
        <Note>
          Find [a full code example at the end](#full-code-example).
        </Note>

        <Steps>
          <Step>
            ### Transfer Checked with CPI

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

                TransferCheckedCpi {
                    source: source.clone(),
                    mint: mint.clone(),
                    destination: destination.clone(),
                    amount,
                    decimals,
                    authority: authority.clone(),
                    system_program: system_program.clone(),
                    max_top_up: None,
                    fee_payer: None,
                }
                .invoke()?;
                ```
              </Tab>

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

                let signer_seeds = authority_seeds!(bump);

                TransferCheckedCpi {
                    source: source.clone(),
                    mint: mint.clone(),
                    destination: destination.clone(),
                    amount,
                    decimals,
                    authority: authority.clone(),
                    system_program: system_program.clone(),
                    max_top_up: None,
                    fee_payer: None,
                }
                .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/transfer_checked.rs) and [full example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/transfer-checked) with shared test utilities.
        </Info>

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

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

          declare_id!("HXmfewpozFdxhM8BayL9v5541gwoGMXTrUoip5KySs2f");

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

              pub fn transfer_checked(
                  ctx: Context<TransferCheckedAccounts>,
                  amount: u64,
                  decimals: u8,
              ) -> Result<()> {
                  TransferCheckedCpi {
                      source: ctx.accounts.source.to_account_info(),
                      mint: ctx.accounts.mint.to_account_info(),
                      destination: ctx.accounts.destination.to_account_info(),
                      amount,
                      decimals,
                      authority: ctx.accounts.authority.to_account_info(),
                      system_program: ctx.accounts.system_program.to_account_info(),
                      fee_payer: ctx.accounts.fee_payer.to_account_info(),
                  }
                  .invoke()?;
                  Ok(())
              }
          }

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

          ```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_transfer_checked::{accounts, instruction::TransferChecked, ID};
          use anchor_lang::system_program;
          use solana_sdk::{instruction::Instruction, signature::Keypair, signer::Signer};
          use test_utils::{create_associated_token_account_for_owner, mint_tokens, setup_test_env};

          #[tokio::test]
          async fn test_transfer_checked() {
              let mut env = setup_test_env("light_token_anchor_transfer_checked", ID).await;
              mint_tokens(&mut env.rpc, &env.payer, env.mint_pda, env.associated_token_account, 1_000_000).await;

              // Create destination associated token account for recipient
              let recipient = Keypair::new();
              let dest_associated_token_account =
                  create_associated_token_account_for_owner(&mut env.rpc, &env.payer, &recipient.pubkey(), &env.mint_pda).await;

              // TransferChecked validates decimals match the mint's decimals.
              // Only use for Light->Light transfers.
              // Use TransferInterface for all other transfers (Light, SPL or Token-2022).
              let transfer_amount = 100_000u64;
              let decimals = 9u8;

              let ix = Instruction {
                  program_id: ID,
                  accounts: accounts::TransferCheckedAccounts {
                      light_token_program: LIGHT_TOKEN_PROGRAM_ID,
                      source: env.associated_token_account,
                      mint: env.mint_pda,
                      destination: dest_associated_token_account,
                      authority: env.payer.pubkey(),
                      fee_payer: env.payer.pubkey(),
                      system_program: system_program::ID,
                  }
                  .to_account_metas(Some(true)),
                  data: TransferChecked {
                      amount: transfer_amount,
                      decimals,
                  }
                  .data(),
              };

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

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

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

                    Context:
                    - Guide: https://zkcompression.com/light-token/cookbook/transfer-checked
                    - 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 (TransferCheckedCpi)
                    - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/transfer-checked

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

                    Note: TransferInterface uses TransferChecked under the hood for Light-to-Light transfers.
                    Use TransferChecked/TransferCheckedCpi directly only when you need Light-to-Light without interface routing.

                    ### 1. Index project
                    - Grep \`declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|transfer|decimals|amount\` across src/
                    - Glob \`**/*.rs\` and \`**/Cargo.toml\` for project structure
                    - Identify: program ID, existing instructions, account structs, token accounts
                    - 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 Program tab 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 transfer-checked to existing program, new program from scratch, migrate from SPL transfer_checked)
                    - AskUserQuestion: should the authority 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 TransferCheckedCpi struct → 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 transfer-checked 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 transfer-checked CPI to an Anchor program

        Context:
        - Guide: https://zkcompression.com/light-token/cookbook/transfer-checked
        - 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 (TransferCheckedCpi)
        - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/transfer-checked

        Key CPI struct: `light_token::instruction::TransferCheckedCpi`

        Note: TransferInterface uses TransferChecked under the hood for Light-to-Light transfers.
        Use TransferChecked/TransferCheckedCpi directly only when you need Light-to-Light without interface routing.

        ### 1. Index project
        - Grep `declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|transfer|decimals|amount` across src/
        - Glob `**/*.rs` and `**/Cargo.toml` for project structure
        - Identify: program ID, existing instructions, account structs, token accounts
        - 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 Program tab 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 transfer-checked to existing program, new program from scratch, migrate from SPL transfer_checked)
        - AskUserQuestion: should the authority 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 TransferCheckedCpi struct → 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="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>
