Operações de token passam pelo programa SPL Token através de construtores de instrução. O crate construtor deve corresponder ao ID do programa que você está chamando: spl_token::instruction::* para o Token clássico (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) e spl_token_2022::instruction::* para o Token-2022 (TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb). Prefira os construtores *_checked e valide mints, autoridades e decimais antes de qualquer CPI.
// transfer_checked passa o mint e afirma os decimais na chainlet ix = spl_token::instruction::transfer_checked( token_program.key, source.key, mint.key, dest.key, authority.key, &[], amount, decimals,)?;invoke(&ix, accounts)?;
use spl_token::state::Account as TokenAccount;use solana_program::program_pack::Pack;pub fn cpi_transfer(amount: u64, decimals: u8, token_program: &AccountInfo, mint: &AccountInfo, source: &AccountInfo, dest: &AccountInfo, auth: &AccountInfo, bump: u8) -> ProgramResult { // 1. O crate construtor e a conta do programa devem concordar. spl_token::check_program_account(token_program.key)?; // 2. Ambas as contas de token devem ser de propriedade desse programa de token. if source.owner != token_program.key || dest.owner != token_program.key { return Err(ProgramError::IncorrectProgramId); } // 3. Mesmo mint em ambos os lados, e é o mint para o qual estamos afirmando os decimais. let src = TokenAccount::unpack(&source.try_borrow_data()?)?; let dst = TokenAccount::unpack(&dest.try_borrow_data()?)?; if src.mint != dst.mint || &src.mint != mint.key || &src.owner != auth.key { return Err(ProgramError::InvalidAccountData); } let ix = spl_token::instruction::transfer_checked( token_program.key, source.key, mint.key, dest.key, auth.key, &[], amount, decimals, )?; invoke_signed( &ix, &[source.clone(), mint.clone(), dest.clone(), auth.clone(), token_program.clone()], &[&[b"auth", &[bump]]], )}
O que isso demonstra:
transfer_checked constrói dados + metadados e afirma os decimais na chain.
Verificações de ID do programa, proprietário e mint são executadas antes da CPI.
Autoridade PDA usa invoke_signed.
A quantidade são unidades de token brutas (decimais).
Token-2022 é um programa separado com um crate separado. Passar seu ID de programa para um construtor spl_token::instruction::* falha no momento da construção da instrução: esses construtores começam com check_program_account(token_program_id)?, que retorna IncorrectProgramId para qualquer coisa que não seja spl_token::ID. Não há um caminho de "apenas troque a conta do programa".
Use spl_token_2022::instruction::transfer_checked(...) para mints do Token-2022, ou anchor_spl::token_interface (Anchor 0.32.1), que despacha para ambos os programas. Token-2022 também rejeita a instrução Transfer não verificada em mints que carregam uma extensão de taxa de transferência, então transfer_checked é a única escolha portátil.
ID incorreto do programa de token - Token-2022 vs clássico. Correção: Valide a chave da conta do programa e use o crate construtor correspondente; os construtores spl_token rejeitam o ID do Token-2022.
transfer não verificado - Depreciado, e Token-2022 o rejeita em mints com taxa de transferência. Correção: Use transfer_checked; trate transfer simples como legado apenas.
Incompatibilidade de mint - Drena o pool errado. Correção: Desempacote e compare as chaves públicas do mint.
Confusão de decimais - Erro de 10^n. Correção: Use quantidades brutas consistentemente.
Verificação de delegado ausente - Transferência não autorizada. Correção: Valide o campo de autoridade.
Congelar conta - Transferência falha. Correção: Trate o código de erro.