ПлатежиРасширенные платежи

Права на расходование

Токен-программы Solana поддерживают делегирование — предоставление другому аккаунту разрешения переводить токены с вашего token account в пределах установленного лимита. Это позволяет реализовать такие сценарии, как автоматические платежи, эскроу-сервисы и обработку платежей третьими лицами без потери контроля над вашими средствами.

Как работает делегирование

Когда вы одобряете делегата, вы разрешаете определённому аккаунту переводить токены от вашего имени:

  • Владелец сохраняет контроль: Вы по-прежнему владеете токенами и можете перевести их или отозвать разрешение в любой момент
  • Ограничение по сумме: Делегат может переводить только сумму в пределах утверждённого лимита
  • Один делегат на аккаунт: Каждый token account может иметь только одного активного делегата
  • Новое разрешение заменяет старое: Одобрение нового делегата автоматически отзывает предыдущего

Делегирование не является кастодиальным. Делегат может расходовать токены только в пределах лимита, но не может получить доступ или вывести средства сверх утверждённой суммы. Владелец может отозвать разрешение в любой момент.

Бизнес-кейсы

Сценарий использованияКак помогает делегирование
Платёжные процессорыМерчант предоставляет процессору разрешение на проведение расчётов
Автоматическая зарплатаКазначейство одобряет сервис для выплаты зарплаты
Эскроу-сервисыПокупатель делегирует агенту эскроу условный перевод
Торговые платформыПользователь одобряет бирже выполнение сделок от его имени
Выпуск картПользователь разрешает эмитенту карты списывать покупки с его token account

Одобрение делегата

Предоставьте другому аккаунту разрешение тратить токены с вашего аккаунта:

import { getApproveCheckedInstruction } from "@solana-program/token";
// Approve delegate to spend up to 1,000 USDC (6 decimals)
const approveInstruction = getApproveCheckedInstruction({
source: tokenAccountAddress, // Your token account
mint: usdcMintAddress, // USDC mint
delegate: delegateAddress, // Account receiving permission
owner: ownerKeypair, // You (must sign)
amount: 1_000_000_000n, // 1,000 USDC in base units
decimals: 6
});

Параметры:

  • source: Тот token account, который предоставляет разрешение
  • delegate: Аккаунт, который получит право тратить токены
  • owner: Текущий владелец token account (должен подписать транзакцию)
  • amount: Максимальное количество токенов, которое делегат может перевести
  • decimals: Десятичные знаки токена для проверки (предотвращает ошибки округления)

Демонстрация

Approve Delegate
// Generate keypairs for sender and delegate
const 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 tokens
const { client, mint, senderAta } = await demoSetup(sender);
console.log("\nMint Address:", mint.address);
console.log("Sender ATA:", senderAta);
// =============================================================================
// Approve Delegate
// =============================================================================
// Create instruction to approve delegate
const approveInstruction = getApproveCheckedInstruction({
source: senderAta,
mint: mint.address,
delegate: delegate.address,
owner: sender,
amount: 1_000_000n, // 1.0 tokens with 6 decimals
decimals: 6
});
// Send approve transaction
const 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 set
const tokenData = await fetchToken(client.runtime.rpc, senderAta);
console.log("\nSender Token Account Data:", tokenData.data);
// =============================================================================
// Demo Setup Helper Function
// =============================================================================
Console
Click to execute the code.

Отзыв делегата

Удалите все права на расходование у текущего делегата:

import { getRevokeInstruction } from "@solana-program/token";
const revokeInstruction = getRevokeInstruction({
source: tokenAccountAddress, // Your token account
owner: ownerKeypair // You (must sign)
});

Отзыв удаляет все права делегата — частичного отзыва не существует. Если нужно уменьшить лимит, одобрите того же делегата с меньшей суммой.

Демонстрация

Revoke Delegate
// Generate keypairs for sender and delegate
const 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 tokens
const { 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 delegate
const approveInstruction = getApproveCheckedInstruction({
source: senderAta,
mint: mint.address,
delegate: delegate.address,
owner: sender,
amount: 1_000_000n, // 1.0 tokens with 6 decimals
decimals: 6
});
// Send approve transaction
const 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 set
const tokenDataAfterApprove = await fetchToken(client.runtime.rpc, senderAta);
console.log("\nSender Token Account Data:", tokenDataAfterApprove.data);
// =============================================================================
// Transaction 2: Revoke Delegate
// =============================================================================
// Create instruction to revoke delegate
const revokeInstruction = getRevokeInstruction({
source: senderAta,
owner: sender
});
// Send revoke transaction
const 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 revoked
const tokenDataAfterRevoke = await fetchToken(client.runtime.rpc, senderAta);
console.log("\nSender Token Account Data:", tokenDataAfterRevoke.data);
// =============================================================================
// Demo Setup Helper Function
// =============================================================================
Console
Click to execute the code.

Перевод от имени делегата

Действуя как делегат, используйте стандартный перевод, но подпишите его keypair делегата вместо владельца:

Transfer as Delegate
import { getTransferCheckedInstruction } from "@solana-program/token";
const transferInstruction = getTransferCheckedInstruction({
source: ownerTokenAccount, // The account you have permission to spend from
mint: usdcMintAddress,
destination: recipientTokenAccount,
authority: delegateKeypair, // You (the delegate) sign, not the owner
amount: 100_000_000n, // 100 USDC
decimals: 6
});

Перевод будет успешным, если:

  • На исходном аккаунте достаточно средств
  • Делегат подписал транзакцию

Каждый перевод уменьшает оставшийся лимит. Когда лимит достигнет нуля, делегат больше не сможет переводить токены.

Демонстрация

Transfer as Delegate
// Generate keypairs for sender, delegate, and recipient
const 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 tokens
const { 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 delegate
const approveInstruction = getApproveCheckedInstruction({
source: senderAta,
mint: mint.address,
delegate: delegate.address,
owner: sender,
amount: 1_000_000n, // 1.0 tokens with 6 decimals
decimals: 6
});
// Send approve transaction
const 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 owner
const transferInstruction = getTransferCheckedInstruction({
source: senderAta,
mint: mint.address,
destination: recipientAta,
authority: delegate, // Delegate signs this transaction
amount: 500_000n, // 0.5 tokens with 6 decimals
decimals: 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 signs
instructions: [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
// =============================================================================
Console
Click to execute the code.

Проверка статуса делегирования

Запросите token account, чтобы узнать текущего делегата и оставшийся лимит:

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");
}

Демонстрация

Check Delegation Status
// 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 delegate
const tokenWithDelegate = await fetchToken(client.runtime.rpc, ataWithDelegate);
console.log("Token Account with Delegate:", tokenWithDelegate);
// Fetch token account without delegate
const tokenWithoutDelegate = await fetchToken(
client.runtime.rpc,
ataWithoutDelegate
);
console.log("\nToken Account without Delegate:", tokenWithoutDelegate);
// =============================================================================
// Demo Setup Helper Function
// =============================================================================
Console
Click to execute the code.

Вопросы безопасности

Для владельцев аккаунтов:

  • Одобряйте только доверенных делегатов
  • Устанавливайте минимально необходимый лимит расходов
  • Отзывайте делегирование, когда оно больше не требуется
  • Следите за своими аккаунтами на предмет неожиданных переводов

Для сервис-провайдеров (делегатов):

  • Четко сообщайте пользователям запрашиваемый лимит расходов
  • Обеспечьте правильное управление ключами для вашего аккаунта делегата
  • Отслеживайте расход лимита, чтобы запросить повторное одобрение до его исчерпания

Делегирование против кастодиального хранения

АспектДелегированиеПолное кастодиальное хранение
Владение токенамиПользователь сохраняетПользователь передает кастодиану
Контроль расходовОграничен одобренной суммойПолный доступ к переведённым средствам
ОтзывМгновенно, владельцемТребует участия кастодиана
РискТолько в пределах лимитаНа всю сумму баланса
Необходимое довериеМинимальноеВысокое

Делегирование — это компромисс: оно позволяет автоматизировать платежи, ограничивая риск только одобренной суммой.

Связанные ресурсы

РесурсОписание
Approve DelegateКак предоставить другому аккаунту разрешение тратить средства с вашего token account.
Revoke DelegateКак удалить существующего делегата и отозвать его права на траты.
Transfer TokenКак переводить токены между token account.

Is this page helpful?

Управляется

© 2026 Фонд Solana.
Все права защищены.
Подключиться