Transferencias y Delegados
Las transferencias de SPL mueven saldos de tokens entre cuentas de token de la misma emisión. Los delegados permiten a un tercero transferir hasta una cantidad aprobada en nombre del propietario; este es el patrón detrás de los enrutadores DEX y los programas de depósito en garantía.
Receta
spl-token transfer <MINT> <CANTIDAD> <DESTINATARIO> --fund-recipient
spl-token approve <MINT> <CANTIDAD> <CLAVE_PÚBLICA_DELEGADO>use anchor_spl::token::{self, Transfer, Approve};
token::transfer(cpi_ctx, amount)?;
token::approve(cpi_ctx, amount)?;Cuándo usar esto:
- Pagos de usuario a usuario en tokens fungibles
- Aprobar que un programa de intercambio retire tokens
- Crear depósitos en garantía que mueven fondos al liquidarse
- Revocar aprobaciones DeFi obsoletas
Ejemplo Funcional
# Alice envía 2.5 tokens (decimales=6 -> 2500000 en crudo)
spl-token transfer "$MINT" 2500000 <BOB_WALLET> --fund-recipient
# Alice aprueba al enrutador para gastar hasta 1_000_000 unidades en crudo
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(), // reemplazar con la cuenta del delegado en producción
authority: ctx.accounts.owner.to_account_info(),
},
);
token::approve(cpi, amount)?;
Ok(())
}Lo que esto demuestra:
- El origen y el destino deben compartir la misma emisión
- El propietario firma la transferencia; el delegado puede firmar después de
approve revokeestablece la cantidad delegada a cero
Análisis Profundo
Cómo Funciona
- La transferencia debita
source.amounty acreditadestination.amountatómicamente - Campos de delegado en la cuenta de token:
delegate: Option<Pubkey>,delegated_amount: u64 - Propietarios de multisig compatibles a través de cuentas multisig de SPL (patrón heredado)
- Las cuentas congeladas rechazan transferencias si la autoridad de congelación las ha congelado
Ciclo de Vida del Delegado
| Acción | Efecto |
|---|---|
approve | Establece el delegado + límite de delegated_amount |
transfer por delegado | Reduce delegated_amount y el saldo |
revoke | Borra el delegado |
Notas de TypeScript
import { createSolanaRpc, address } from "@solana/kit";
const rpc = createSolanaRpc("https://api.devnet.solana.com");
// Construir instrucciones de transferencia a través de los ayudantes de token de @solana-program/
// Siempre derivar el ATA del destinatario; financiar si faltaErrores Comunes
- Desajuste de emisión - la transferencia entre diferentes emisiones falla. Solución: validar
from.mint == to.minten la cadena y en los clientes. - Cantidad delegada insuficiente - la transferencia del delegado excede la aprobación. Solución: volver a aprobar o dividir entre transacciones.
- Aprobaciones ilimitadas obsoletas - riesgo de seguridad en DeFi. Solución: aprobar un gasto exacto; revocar después del intercambio.
- Transferencia a una dirección que no es ATA - funciona pero confunde a los usuarios. Solución: derivar y mostrar direcciones ATA.
- Cuenta de origen congelada - todas las transferencias bloqueadas. Solución: verificar el campo
stateantes de hacer promesas en la UX.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
transfer_checked | Los programas en cadena necesitan protección de decimales | Transferencias simples desde la CLI |
| Custodia de PDA de depósito en garantía | Intercambio/liquidación atómica | Envío simple desde billetera |
| Gancho de transferencia de Token-2022 | Política en cada movimiento | Pagos fungibles simples |
Preguntas Frecuentes
¿Qué es un delegado?
Una clave pública autorizada para transferir hasta delegated_amount desde tu cuenta de token sin poseer la cuenta.
¿Puede un delegado transferir más de lo aprobado?
No. El programa SPL aplica el límite de delegated_amount.
¿Cómo revoco una aprobación?
spl-token revoke <MINT> o CPI token::revoke desde el propietario.
¿Cuesta SOL la aprobación?
Solo una pequeña tarifa de transacción - ningún cambio de alquiler en la cuenta de token.
¿Pueden los programas ser delegados?
Sí. Las PDAs de programas enrutadores o las bóvedas comúnmente tienen el rol de delegado.
¿Qué es transfer_checked?
Incluye los decimales esperados en los datos de la instrucción; los programas lo usan para prevenir ataques de confusión de decimales.
¿Puedo transferir saldos parciales?
Sí. Cualquier cantidad hasta el saldo de origen (o el techo delegado para los delegados).
¿Las transferencias cambian el suministro total?
No. El suministro no cambia - solo redistribuye los tokens existentes.
¿Qué pasa si el destinatario no tiene ATA?
La transacción falla a menos que agregues una instrucción para crear ATA o uses --fund-recipient.
¿Los delegados son por emisión?
Los delegados son por cuenta de token (por billetera + ATA de emisión), no globales por billetera.
¿Puede el propietario seguir transfiriendo después de aprobar?
Sí. El propietario conserva el control total; el delegado tiene una autoridad limitada adicional.
¿Cómo muestran las billeteras las aprobaciones?
Leen los campos de delegado de la cuenta de token a través de la codificación jsonParsed de RPC.
Relacionado
- Cuentas de Token Asociadas (ATA) - direcciones de destinatario
- Autoridades - congelación que bloquea transferencias
- SPL Token en Programas - seguridad de CPI
Versiones de la pila: Esta página fue 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, y LiteSVM 0.6.x.