Transferências e Delegados
As transferências SPL movem saldos de tokens entre contas de tokens da mesma casa da moeda. Delegados permitem que terceiros transfiram até um valor aprovado em nome do proprietário – o padrão por trás de roteadores DEX e programas de garantia.
Receita
spl-token transfer <MINT> <AMOUNT> <RECIPIENT> --fund-recipient
spl-token approve <MINT> <AMOUNT> <DELEGATE_PUBKEY>
use anchor_spl::token::{self, Transfer, Approve};
token::transfer(cpi_ctx, amount)?;
token::approve(cpi_ctx, amount)?;
Quando fazer isso:
- Pagamentos de usuário para usuário em tokens fungíveis
- Aprovação de um programa de swap para extrair tokens
- Construção de depósito que movimenta fundos na liquidação
- Revogação de aprovações obsoletas de DeFi
Exemplo de trabalho
# Alice sends 2.5 tokens (decimals=6 -> 2500000 raw)
spl-token transfer "$MINT" 2500000 <BOB_WALLET> --fund-recipient
# Alice approves router to spend up to 1_000_000 raw units
spl-token approve "$MINT" 1000000 <ROUTER_VAULT_PUBKEY>
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, TokenAccount, Transfer, Approve, Revoke};
#[derive(Accounts)]
pub struct TransferPayment<'info> {
#[account(mut)]
pub from: Account<'info, TokenAccount>,
#[account(mut)]
pub to: Account<'info, TokenAccount>,
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
}
pub fn transfer_payment(ctx: Context<TransferPayment>, amount: u64) -> Result<()> {
require_keys_eq!(ctx.accounts.from.mint, ctx.accounts.to.mint, ErrorCode::MintMismatch);
let cpi = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Transfer {
from: ctx.accounts.from.to_account_info(),
to: ctx.accounts.to.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
},
);
token::transfer(cpi, amount)?;
Ok(())
}
#[derive(Accounts)]
pub struct ApproveRouter<'info> {
#[account(mut)]
pub owner_token: Account<'info, TokenAccount>,
pub owner: Signer<'info>,
pub token_program: Program<'info, Token>,
}
pub fn approve_router(ctx: Context<ApproveRouter>, delegate: Pubkey, amount: u64) -> Result<()> {
let cpi = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Approve {
to: ctx.accounts.owner_token.to_account_info(),
delegate: ctx.accounts.token_program.to_account_info(), // replace with delegate account in production
authority: ctx.accounts.owner.to_account_info(),
},
);
token::approve(cpi, amount)?;
Ok(())
}
O que isso demonstra:
- Origem e destino devem compartilhar a mesma casa da moeda
- Proprietário assina transferência; delegado pode assinar depois de
approve - Revogar define o valor delegado de volta a zero
Aprofundamento
Como funciona
- Transferir débitos
source.amounte créditosdestination.amountatomicamente - Delegar campos na conta token:
delegate: Option<Pubkey>,delegated_amount: u64 - Proprietários multisig suportados por contas multisig SPL (padrão legado)
- Contas congeladas rejeitam transferências quando a autoridade de congelamento as congelou
Ciclo de vida do delegado
| Ação | Efeito |
|---|---|
approve | Define delegado + limite de quantidade_delegada |
transfer por delegado | Reduz o valor delegado e o saldo |
revoke | Limpa delegado |
Notas TypeScript
import { createSolanaRpc, address } from "@solana/kit";
const rpc = createSolanaRpc("https://api.devnet.solana.com");
// Build transfer instructions via @solana-program/token helpers
// Always derive recipient ATA; fund if missing
Pegadinhas
- Incompatibilidade de casas da moeda - falha na transferência entre casas da moeda diferentes. Correção: validar
from.mint == to.mintna rede e em clientes. - Valor delegado insuficiente - a transferência de delegado excede a aprovação. Correção: reaprovar ou dividir entre transações.
- Aprovações obsoletas e ilimitadas - risco de segurança no DeFi. Correção: aprovar gasto exato; revogar após troca.
- Transferência para endereço não ATA - funciona, mas confunde os usuários. Correção: derivar e exibir endereços ATA.
- Conta de origem congelada - todas as transferências bloqueadas. Correção: verifique o campo
stateantes das promessas de UX.
Alternativas
| Alternativa | Usar quando | Não use quando |
|---|---|---|
transfer_checked | Programas on-chain precisam de proteção contra decimais | Transferências CLI simples |
| Custódia de PDA de garantia | Troca/liquidação atômica | Envio de carteira simples |
| Gancho de transferência Token-2022 | Política em cada movimento | Pagamentos fungíveis simples |
Perguntas frequentes
O que é um delegado?
Um pubkey autorizado a transferir até delegated_amount da sua conta de token sem possuir a conta.
Um delegado pode transferir mais do que o aprovado?
Não. O programa SPL impõe o limite de quantidade delegada.
Como posso revogar uma aprovação?
spl-token revoke <MINT> ou token::revoke CPI do proprietário.
O custo SOL é aprovado?
Apenas pequena taxa de transação - sem alteração de aluguel na conta simbólica.
Os programas podem ser delegados?
Sim. Os PDAs ou cofres do programa roteador geralmente desempenham a função de delegado.
O que é transfer_checked?
Inclui decimais esperados nos dados de instrução - os programas os utilizam para evitar ataques de confusão decimal.
Posso transferir saldos parciais?
Sim. Qualquer valor até o saldo da fonte (ou limite máximo delegado para delegados).
As transferências alteram a oferta total?
Não. Fornecimento inalterado - apenas redistribui tokens existentes.
E se o destinatário não tiver ATA?
A transação falha a menos que você adicione a instrução create-ATA ou --fund-recipient.
Os delegados são permitidos?
Os delegados são por conta de token (por carteira+mint ATA), e não globais por carteira.
O proprietário ainda pode transferir após a aprovação?
Sim. O proprietário mantém o controle total; delegado tem autoridade limitada adicional.
Como as carteiras mostram as aprovações?
Leia os campos delegados da conta de token por meio da codificação RPC jsonParsed.
Relacionado
- Contas de Token Associadas (ATAs) - endereços de destinatários
- Authorities - congelar transferências de bloqueio
- Token SPL em Programas - Segurança CPI
Versões Stack: Esta página foi escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0 e LiteSVM 0.6.x.