Токенові програми Solana підтримують делегування — надання іншому обліковому запису дозволу на переказ токенів з вашого токенового облікового запису до вказаного ліміту. Це дозволяє реалізувати такі сценарії, як автоматизовані платежі, ескроу-сервіси та обробка платежів третіми сторонами без втрати контролю над вашими коштами.
Як працює делегування
Коли ви схвалюєте делегата, ви надаєте певному обліковому запису право переказувати токени від вашого імені:
- Власник зберігає контроль: ви як і раніше володієте токенами і можете переказувати їх або відкликати делегування в будь-який момент
- Обмежене витрачання: делегат може переказати лише до схваленої суми
- Один делегат на обліковий запис: кожен токеновий обліковий запис може мати лише одного активного делегата
- Нове схвалення замінює попереднє: схвалення нового делегата автоматично відкликає попереднього
Делегування є некастодіальним. Делегат може витрачати токени до встановленого ліміту, але не може отримати доступ до облікового запису або вивести кошти понад схвалену суму. Власник може відкликати делегування в будь-який момент.
Бізнес-сценарії використання
| Сценарій використання | Як допомагає делегування |
|---|---|
| Платіжні процесори | Продавець надає процесору дозвіл на проведення транзакцій |
| Автоматизована зарплата | Казначейство схвалює зарплатний сервіс для виплати заробітних плат |
| Ескроу-сервіси | Покупець делегує ескроу-агенту право на умовне вивільнення коштів |
| Торгові платформи | Користувач схвалює біржу для виконання угод від його імені |
| Випуск карток | Користувач схвалює емітента картки для списання покупок з його токенового облікового запису |
Схвалення делегата
Надайте іншому обліковому запису дозвіл витрачати токени з вашого облікового запису:
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});
Параметри:
source: обліковий запис токенів, який надає дозвілdelegate: обліковий запис, який отримає дозвіл на витрачанняowner: поточний власник облікового запису токенів (має підписати транзакцію)amount: максимальна кількість токенів, яку делегат може переказатиdecimals: кількість десяткових знаків токена для валідації (запобігає помилкам з десятковими знаками)
Демонстрація
// 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// =============================================================================
Відкликання делегата
Видалити всі дозволи на витрачання від поточного делегата:
import { getRevokeInstruction } from "@solana-program/token";const revokeInstruction = getRevokeInstruction({source: tokenAccountAddress, // Your token accountowner: ownerKeypair // You (must sign)});
Відкликання видаляє всі дозволи делегата — часткове відкликання неможливе. Якщо потрібно зменшити ліміт, схваліть того самого делегата з меншою сумою.
Демонстрація
// 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// =============================================================================
Переказ як делегат
Діючи як делегат, використовуйте звичайний переказ, але підписуйте keypair делегата замість власника:
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});
Переказ буде успішним, якщо:
- На вихідному рахунку достатньо коштів
- Делегат підписує транзакцію
Кожен переказ зменшує залишок дозволеної суми. Коли дозволена сума досягає нуля, делегат більше не може переказувати токени.
Демонстрація
// 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// =============================================================================
Перевірка статусу делегування
Запитайте обліковий запис токенів, щоб побачити його поточного делегата та залишок дозволу:
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 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// =============================================================================
Міркування безпеки
Для власників облікових записів:
- Схвалюйте лише перевірених делегатів
- Встановлюйте мінімально необхідний ліміт витрат
- Відкликайте делегування, коли воно більше не потрібне
- Відстежуйте свої облікові записи на предмет несподіваних переказів
Для постачальників послуг (делегатів):
- Чітко повідомляйте користувачам про запитуваний ліміт витрат
- Впроваджуйте належне управління ключами для вашого облікового запису делегата
- Відстежуйте використання дозволеної суми, щоб запитувати повторне схвалення до вичерпання лімітів
Делегування проти зберігання
| Аспект | Делегування | Повне зберігання |
|---|---|---|
| Власність токенів | Залишається у користувача | Користувач передає зберігачу |
| Контроль витрат | Обмежений схваленою сумою | Повний доступ до переданих коштів |
| Відкликання | Миттєве, власником | Потребує співпраці зберігача |
| Ризик | Обмежений схваленою сумою | Весь баланс |
| Необхідна довіра | Обмежена | Висока |
Делегування забезпечує золоту середину — дозволяє автоматизовані платежі, обмежуючи ризик схваленою сумою.
Пов'язані ресурси
| Ресурс | Опис |
|---|---|
| Схвалення делегата | Як надати іншому обліковому запису дозвіл витрачати з вашого облікового запису токенів. |
| Відкликання делегата | Як видалити існуючого делегата та відкликати його дозволи на витрачання. |
| Переказ токенів | Як переказувати токени між обліковими записами токенів. |
Is this page helpful?