Los programas de tokens de Solana admiten delegación: otorgar a otra cuenta permiso para transferir tokens desde tu cuenta de tokens hasta un límite especificado. Esto habilita casos de uso como pagos automatizados, servicios de custodia y procesamiento de pagos por terceros sin renunciar a la custodia de tus fondos.
Cómo funciona la delegación
Cuando apruebas un delegado, estás autorizando a una cuenta específica a transferir tokens en tu nombre:
- El propietario mantiene la custodia: Sigues siendo dueño de los tokens y puedes transferir o revocar en cualquier momento
- Gasto limitado: El delegado solo puede transferir hasta el monto aprobado
- Un solo delegado por cuenta: Cada cuenta de tokens solo puede tener un delegado activo
- La nueva aprobación reemplaza la anterior: Aprobar un nuevo delegado revoca automáticamente el anterior
La delegación es no custodial. El delegado puede gastar tokens hasta el límite, pero no puede acceder ni vaciar la cuenta más allá del monto aprobado. El propietario puede revocar en cualquier momento.
Casos de uso empresariales
| Caso de uso | Cómo ayuda la delegación |
|---|---|
| Procesadores de pagos | El comerciante otorga permiso al procesador para liquidar transacciones |
| Nómina automatizada | La tesorería aprueba al servicio de nómina para desembolsar salarios |
| Servicios de custodia | El comprador delega al agente de custodia para la liberación condicional |
| Plataformas de trading | El usuario aprueba al exchange para ejecutar operaciones en su nombre |
| Emisión de tarjetas | El usuario aprueba al emisor de tarjetas para cargar compras a su cuenta de tokens |
Aprobar un delegado
Otorga permiso a otra cuenta para gastar tokens desde tu cuenta:
import { getApproveCheckedInstruction } from "@solana-program/token";// Approve delegate to spend up to 1,000 USDC (6 decimals)const approveInstruction = getApproveCheckedInstruction({source: tokenAccountAddress, // Your token accountmint: usdcMintAddress, // USDC mintdelegate: delegateAddress, // Account receiving permissionowner: ownerKeypair, // You (must sign)amount: 1_000_000_000n, // 1,000 USDC in base unitsdecimals: 6});
Parámetros:
source: La cuenta de tokens que otorga el permisodelegate: La cuenta que tendrá permiso de gastoowner: Propietario actual de la cuenta de tokens (debe firmar la transacción)amount: Cantidad máxima de tokens que el delegado puede transferirdecimals: Decimales del token para validación (previene errores decimales)
Demostración
// Generate keypairs for sender and delegateconst sender = (await generateKeypair()).signer;const delegate = (await generateKeypair()).signer;console.log("Sender Address:", sender.address);console.log("Delegate Address:", delegate.address);// Demo Setup: Create client, mint account, token account, and fund with initial tokensconst { client, mint, senderAta } = await demoSetup(sender);console.log("\nMint Address:", mint.address);console.log("Sender ATA:", senderAta);// =============================================================================// Approve Delegate// =============================================================================// Create instruction to approve delegateconst approveInstruction = getApproveCheckedInstruction({source: senderAta,mint: mint.address,delegate: delegate.address,owner: sender,amount: 1_000_000n, // 1.0 tokens with 6 decimalsdecimals: 6});// Send approve transactionconst signature = await client.transaction.prepareAndSend({authority: sender,instructions: [approveInstruction]});console.log("\n=== Approve Delegate ===");console.log("Transaction Signature:", signature);// Fetch token account data to show delegate is setconst tokenData = await fetchToken(client.runtime.rpc, senderAta);console.log("\nSender Token Account Data:", tokenData.data);// =============================================================================// Demo Setup Helper Function// =============================================================================
Revocar un delegado
Eliminar todos los permisos de gasto del delegado actual:
import { getRevokeInstruction } from "@solana-program/token";const revokeInstruction = getRevokeInstruction({source: tokenAccountAddress, // Your token accountowner: ownerKeypair // You (must sign)});
Revocar elimina todos los permisos del delegado—no existe una revocación parcial. Si necesitas reducir el límite, aprueba al mismo delegado con una cantidad menor.
Demostración
// Generate keypairs for sender and delegateconst sender = (await generateKeypair()).signer;const delegate = (await generateKeypair()).signer;console.log("Sender Address:", sender.address);console.log("Delegate Address:", delegate.address);// Demo Setup: Create client, mint account, token account, and fund with initial tokensconst { client, mint, senderAta } = await demoSetup(sender);console.log("\nMint Address:", mint.address);console.log("Sender ATA:", senderAta);// =============================================================================// Transaction 1: Approve Delegate// =============================================================================// Create instruction to approve delegateconst approveInstruction = getApproveCheckedInstruction({source: senderAta,mint: mint.address,delegate: delegate.address,owner: sender,amount: 1_000_000n, // 1.0 tokens with 6 decimalsdecimals: 6});// Send approve transactionconst approveSignature = await client.transaction.prepareAndSend({authority: sender,instructions: [approveInstruction]});console.log("\n=== Transaction 1: Approve Delegate ===");console.log("Transaction Signature:", approveSignature);// Fetch token account data to show delegate is setconst tokenDataAfterApprove = await fetchToken(client.runtime.rpc, senderAta);console.log("\nSender Token Account Data:", tokenDataAfterApprove.data);// =============================================================================// Transaction 2: Revoke Delegate// =============================================================================// Create instruction to revoke delegateconst revokeInstruction = getRevokeInstruction({source: senderAta,owner: sender});// Send revoke transactionconst revokeSignature = await client.transaction.prepareAndSend({authority: sender,instructions: [revokeInstruction]});console.log("\n=== Transaction 2: Revoke Delegate ===");console.log("Transaction Signature:", revokeSignature);// Fetch token account data to show delegate is revokedconst tokenDataAfterRevoke = await fetchToken(client.runtime.rpc, senderAta);console.log("\nSender Token Account Data:", tokenDataAfterRevoke.data);// =============================================================================// Demo Setup Helper Function// =============================================================================
Transferir como delegado
Al actuar como delegado, usa una transferencia estándar pero firma con el keypair del delegado en lugar del propietario:
import { getTransferCheckedInstruction } from "@solana-program/token";const transferInstruction = getTransferCheckedInstruction({source: ownerTokenAccount, // The account you have permission to spend frommint: usdcMintAddress,destination: recipientTokenAccount,authority: delegateKeypair, // You (the delegate) sign, not the owneramount: 100_000_000n, // 100 USDCdecimals: 6});
La transferencia tendrá éxito si:
- La cuenta de origen tiene saldo suficiente
- El delegado firma la transacción
Cada transferencia reduce la asignación restante. Cuando la asignación llega a cero, el delegado ya no puede transferir tokens.
Demostración
// Generate keypairs for sender, delegate, and recipientconst sender = (await generateKeypair()).signer;const delegate = (await generateKeypair()).signer;const recipient = (await generateKeypair()).signer;console.log("Sender Address:", sender.address);console.log("Delegate Address:", delegate.address);console.log("Recipient Address:", recipient.address);// Demo Setup: Create client, mint account, token accounts, and fund with initial tokensconst { client, mint, senderAta, recipientAta } = await demoSetup(sender,delegate,recipient);console.log("\nMint Address:", mint.address);console.log("Sender ATA:", senderAta);console.log("Recipient ATA:", recipientAta);// =============================================================================// Transaction 1: Approve Delegate// =============================================================================// Create instruction to approve delegateconst approveInstruction = getApproveCheckedInstruction({source: senderAta,mint: mint.address,delegate: delegate.address,owner: sender,amount: 1_000_000n, // 1.0 tokens with 6 decimalsdecimals: 6});// Send approve transactionconst approveSignature = await client.transaction.prepareAndSend({authority: sender,instructions: [approveInstruction]});console.log("\n=== Transaction 1: Approve Delegate ===");console.log("Delegate Address:", delegate.address);console.log("Transaction Signature:", approveSignature);// =============================================================================// Fetch Token Account Data to Demonstrate Delegate is Set// =============================================================================const tokenAccountData = await fetchToken(client.runtime.rpc, senderAta);console.log("\nSender Token Account Data:", tokenAccountData.data);// =============================================================================// Transaction 2: Transfer Using Delegate// =============================================================================// Create instruction to transfer tokens using delegate// Note: delegate is the authority here, not the ownerconst transferInstruction = getTransferCheckedInstruction({source: senderAta,mint: mint.address,destination: recipientAta,authority: delegate, // Delegate signs this transactionamount: 500_000n, // 0.5 tokens with 6 decimalsdecimals: 6});// Send transfer transaction// Delegate pays for the transaction and authorizes the transfer (sender not needed)const transferSignature = await client.transaction.prepareAndSend({authority: delegate, // Delegate pays fee and signsinstructions: [transferInstruction]});// =============================================================================// Fetch Final Token Account Balances// =============================================================================const finalSenderToken = await fetchToken(client.runtime.rpc, senderAta);const finalRecipientToken = await fetchToken(client.runtime.rpc, recipientAta);console.log("\n=== Transaction 2: Transfer Using Delegate ===");console.log("Transaction Signature:", transferSignature);console.log("\nSender Token Account Data:", finalSenderToken.data);console.log("\nRecipient Token Account Data:", finalRecipientToken.data);// =============================================================================// Demo Setup Helper Function// =============================================================================
Verificar el estado de delegación
Consulta una cuenta de tokens para ver su delegado actual y el saldo restante permitido:
import { fetchToken } from "@solana-program/token";const tokenAccount = await fetchToken(rpc, tokenAccountAddress);if (tokenAccount.data.delegate) {console.log("Delegate:", tokenAccount.data.delegate);console.log("Remaining allowance:", tokenAccount.data.delegatedAmount);} else {console.log("No delegate set");}
Demo
// Demo Setup: Create client, mint, two token accounts (one with delegate, one without)const { client, ataWithDelegate, ataWithoutDelegate } = await demoSetup();// =============================================================================// Fetch Token Accounts// =============================================================================// Fetch token account with delegateconst tokenWithDelegate = await fetchToken(client.runtime.rpc, ataWithDelegate);console.log("Token Account with Delegate:", tokenWithDelegate);// Fetch token account without delegateconst tokenWithoutDelegate = await fetchToken(client.runtime.rpc,ataWithoutDelegate);console.log("\nToken Account without Delegate:", tokenWithoutDelegate);// =============================================================================// Demo Setup Helper Function// =============================================================================
Consideraciones de seguridad
Para propietarios de cuentas:
- Solo aprueba delegados de confianza
- Establece el límite de gasto mínimo necesario
- Revoca las delegaciones cuando ya no sean necesarias
- Monitorea tus cuentas para detectar transferencias inesperadas
Para proveedores de servicios (delegados):
- Comunica claramente el límite de gasto solicitado a los usuarios
- Implementa una gestión adecuada de claves para tu cuenta de delegado
- Rastrea el consumo de asignación para solicitar una nueva aprobación antes de que se agoten los límites
Delegación vs. custodia
| Aspecto | Delegación | Custodia completa |
|---|---|---|
| Propiedad de tokens | El usuario la retiene | El usuario la transfiere al custodio |
| Control de gasto | Limitado al monto aprobado | Acceso completo a los fondos transferidos |
| Revocación | Instantánea, por el propietario | Requiere cooperación del custodio |
| Exposición al riesgo | Limitada al monto aprobado | Saldo completo |
| Confianza requerida | Limitada | Alta |
La delegación proporciona un punto intermedio, permitiendo pagos automatizados mientras se limita la exposición al riesgo al monto aprobado.
Recursos relacionados
| Recurso | Descripción |
|---|---|
| Aprobar delegado | Cómo otorgar a otra cuenta permiso para gastar desde tu cuenta de tokens. |
| Revocar delegado | Cómo eliminar un delegado existente y revocar sus permisos de gasto. |
| Transferir token | Cómo transferir tokens entre cuentas de tokens. |
Is this page helpful?