Các Token Program của Solana hỗ trợ ủy quyền—cấp cho một tài khoản khác quyền chuyển token từ token account của bạn trong giới hạn được chỉ định. Điều này cho phép các trường hợp sử dụng như thanh toán tự động, dịch vụ ký quỹ và xử lý thanh toán qua bên thứ ba mà không cần từ bỏ quyền giữ quỹ của bạn.
Cách thức hoạt động của ủy quyền
Khi bạn phê duyệt một đại diện ủy quyền, bạn đang cho phép một tài khoản cụ thể chuyển token thay mặt bạn:
- Chủ sở hữu vẫn giữ quyền kiểm soát: Bạn vẫn sở hữu token và có thể chuyển hoặc thu hồi bất cứ lúc nào
- Giới hạn chi tiêu: Đại diện ủy quyền chỉ có thể chuyển tối đa số lượng đã được phê duyệt
- Một đại diện ủy quyền cho mỗi tài khoản: Mỗi token account chỉ có thể có một đại diện ủy quyền đang hoạt động
- Phê duyệt mới thay thế phê duyệt cũ: Phê duyệt đại diện ủy quyền mới sẽ tự động thu hồi đại diện trước đó
Ủy quyền không phải là hình thức giữ hộ. Đại diện ủy quyền có thể chi tiêu token trong giới hạn, nhưng không thể truy cập hoặc rút cạn tài khoản vượt quá số lượng đã được phê duyệt. Chủ sở hữu có thể thu hồi bất cứ lúc nào.
Các trường hợp sử dụng trong kinh doanh
| Trường hợp sử dụng | Cách ủy quyền hỗ trợ |
|---|---|
| Bộ xử lý thanh toán | Người bán cấp quyền cho bộ xử lý để thanh toán giao dịch |
| Bảng lương tự động | Kho bạc phê duyệt dịch vụ bảng lương để chi trả lương |
| Dịch vụ ký quỹ | Người mua ủy quyền cho đại lý ký quỹ để giải ngân có điều kiện |
| Nền tảng giao dịch | Người dùng phê duyệt sàn giao dịch để thực hiện giao dịch thay mặt họ |
| Phát hành thẻ | Người dùng phê duyệt nhà phát hành thẻ để tính phí mua hàng vào token account của họ |
Phê duyệt đại diện ủy quyền
Cấp quyền cho tài khoản khác để chi tiêu token từ tài khoản của bạn:
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});
Tham số:
source: Token account cấp quyềndelegate: Tài khoản sẽ có quyền chi tiêuowner: Chủ sở hữu hiện tại của token account (phải ký giao dịch)amount: Số lượng token tối đa mà delegate có thể chuyểndecimals: Số chữ số thập phân của token để xác thực (ngăn ngừa lỗi thập phân)
Demo
// 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// =============================================================================
Thu hồi quyền của người được ủy quyền
Xóa tất cả quyền chi tiêu khỏi người được ủy quyền hiện tại:
import { getRevokeInstruction } from "@solana-program/token";const revokeInstruction = getRevokeInstruction({source: tokenAccountAddress, // Your token accountowner: ownerKeypair // You (must sign)});
Thu hồi sẽ xóa tất cả quyền của người được ủy quyền—không có thu hồi một phần. Nếu bạn cần giảm hạn mức, hãy phê duyệt lại cùng người được ủy quyền với số lượng thấp hơn.
Demo
// 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// =============================================================================
Chuyển token với tư cách người được ủy quyền
Khi hoạt động với tư cách người được ủy quyền, sử dụng lệnh chuyển tiêu chuẩn nhưng ký bằng keypair của người được ủy quyền thay vì chủ sở hữu:
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});
Giao dịch chuyển sẽ thành công nếu:
- Tài khoản nguồn có đủ số dư
- Người được ủy quyền ký giao dịch
Mỗi lần chuyển sẽ giảm hạn mức còn lại. Khi hạn mức về không, người được ủy quyền không thể chuyển token nữa.
Demo
// 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// =============================================================================
Kiểm tra trạng thái ủy quyền
Truy vấn token account để xem delegate hiện tại và hạn mức còn lại:
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// =============================================================================
Các vấn đề bảo mật cần lưu ý
Đối với chủ sở hữu tài khoản:
- Chỉ phê duyệt các delegate đáng tin cậy
- Đặt hạn mức chi tiêu tối thiểu cần thiết
- Thu hồi ủy quyền khi không còn cần thiết nữa
- Giám sát tài khoản của bạn để phát hiện các giao dịch chuyển không mong muốn
Đối với nhà cung cấp dịch vụ (delegate):
- Thông báo rõ ràng hạn mức chi tiêu được yêu cầu cho người dùng
- Triển khai quản lý khóa phù hợp cho tài khoản delegate của bạn
- Theo dõi mức tiêu thụ hạn mức để yêu cầu phê duyệt lại trước khi hết hạn mức
Ủy quyền so với giữ hộ
| Khía cạnh | Ủy quyền | Giữ hộ toàn phần |
|---|---|---|
| Quyền sở hữu token | Người dùng giữ quyền | Người dùng chuyển cho bên giữ hộ |
| Kiểm soát chi tiêu | Giới hạn ở số tiền được phê duyệt | Toàn quyền truy cập vào số tiền đã chuyển |
| Thu hồi | Ngay lập tức, bởi chủ sở hữu | Yêu cầu sự hợp tác của bên giữ hộ |
| Rủi ro | Giới hạn ở số tiền được phê duyệt | Toàn bộ số dư |
| Mức độ tin tưởng cần | Hạn chế | Cao |
Ủy quyền cung cấp một giải pháp trung gian—cho phép thanh toán tự động trong khi giới hạn rủi ro ở mức số tiền được phê duyệt.
Tài nguyên liên quan
| Tài nguyên | Mô tả |
|---|---|
| Approve Delegate | Cách cấp quyền cho tài khoản khác để chi tiêu từ token account của bạn. |
| Revoke Delegate | Cách xóa delegate hiện có và thu hồi quyền chi tiêu của nó. |
| Transfer Token | Cách chuyển token giữa các token account. |
Is this page helpful?