Permanent Delegate là gì?
Extension PermanentDelegate của Token Extension Program gán một quyền ủy
quyền cho chính mint.
Permanent delegate là một quyền hạn cấp mint có thể cho phép chuyển token và đốt token cho bất kỳ token account nào của mint đó. Nó hoạt động như một delegate toàn cục cho tất cả token account của mint, và chủ sở hữu token account không thể thu hồi permanent delegate khỏi token account của họ.
Quyền hạn mint cũng có thể thay đổi permanent delegate sau này bằng
SetAuthority sử dụng AuthorityType::PermanentDelegate.
Cách tạo và sử dụng Permanent Delegate
Để tạo và sử dụng permanent delegate:
- Tính toán kích thước mint account và rent cần thiết cho mint và extension
PermanentDelegate. - Tạo mint account với
CreateAccount, khởi tạoPermanentDelegate, và khởi tạo mint vớiInitializeMint. - Gửi
TransferCheckedhoặcBurnCheckedđược ký bởi permanent delegate. Token program xử lý các lệnh đó giống như khi chủ sở hữu token account ký chúng.
Tính toán kích thước tài khoản
Tính toán kích thước mint account cho base mint cộng với extension
PermanentDelegate. Đây là kích thước được sử dụng trong
CreateAccount.
Tính toán rent
Tính toán rent sử dụng kích thước cần thiết cho mint cộng với extension
PermanentDelegate.
Tạo mint account
Tạo mint account với dung lượng và lamport đã tính toán.
Khởi tạo PermanentDelegate
Khởi tạo tiện ích mở rộng PermanentDelegate trên mint.
Khởi tạo mint
Khởi tạo mint với InitializeMint trong cùng một giao dịch.
Tạo token account và mint token
Tạo token account cho chủ sở hữu và người nhận, sau đó mint token vào token account của chủ sở hữu.
Chuyển token với permanent delegate
Chuyển token với TransferChecked được ký bởi permanent delegate.
Thứ tự lệnh
InitializePermanentDelegate phải được thực hiện trước
InitializeMint. CreateAccount, InitializePermanentDelegate,
và InitializeMint phải được bao gồm trong cùng một giao dịch.
Tài liệu tham khảo nguồn
| Mục | Mô tả | Nguồn |
|---|---|---|
PermanentDelegate | Tiện ích mở rộng mint lưu trữ delegate được ủy quyền trên mọi token account của mint. | Nguồn |
InitializePermanentDelegate | Lệnh khởi tạo permanent delegate trước InitializeMint. | Nguồn |
SetAuthority | Lệnh token cơ bản được sử dụng với AuthorityType::PermanentDelegate để xoay delegate. | Nguồn |
AuthorityType::PermanentDelegate | Bộ phân biệt quyền được sử dụng với SetAuthority để xoay permanent delegate trên mint. | Nguồn |
process_initialize_permanent_delegate | Logic xử lý ghi tiện ích mở rộng permanent delegate vào mint. | Nguồn |
process_set_authority | Logic xử lý xác thực và xoay permanent delegate khi sử dụng AuthorityType::PermanentDelegate. | Nguồn |
Typescript
Ví dụ Kit dưới đây sử dụng trực tiếp các lệnh được tạo. Các ví dụ cũ sử dụng
@solana/web3.js và @solana/spl-token được bao gồm để tham khảo.
Kit
import {lamports,createClient,generateKeyPairSigner,unwrapOption} from "@solana/kit";import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";import { getCreateAccountInstruction } from "@solana-program/system";import {extension,fetchMint,fetchToken,findAssociatedTokenPda,getCreateAssociatedTokenInstructionAsync,getInitializeMintInstruction,getInitializePermanentDelegateInstruction,getMintSize,getMintToCheckedInstruction,getTransferCheckedInstruction,isExtension,TOKEN_2022_PROGRAM_ADDRESS} from "@solana-program/token-2022";const client = await createClient().use(generatedPayer()).use(solanaRpc({rpcUrl: "http://localhost:8899",rpcSubscriptionsUrl: "ws://localhost:8900"})).use(rpcAirdrop()).use(airdropPayer(lamports(1_000_000_000n)));const mint = await generateKeyPairSigner();const owner = await generateKeyPairSigner();const delegate = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();const permanentDelegateExtension = extension("PermanentDelegate", {delegate: delegate.address});const mintSpace = BigInt(getMintSize([permanentDelegateExtension]));const mintRent = await client.rpc.getMinimumBalanceForRentExemption(mintSpace).send();await client.sendTransaction([getCreateAccountInstruction({payer: client.payer, // Account funding the new mint account.newAccount: mint, // New mint account to create.lamports: mintRent, // Lamports funding the mint account rent.space: mintSpace, // Account size in bytes for the mint plus PermanentDelegate.programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.}),getInitializePermanentDelegateInstruction({mint: mint.address, // Mint account that stores the PermanentDelegate extension.delegate: delegate.address // Permanent delegate authorized for all token accounts for the mint.}),getInitializeMintInstruction({mint: mint.address, // Mint account to initialize.decimals: 0, // Number of decimals for the token.mintAuthority: client.payer.address, // Authority allowed to mint new tokens.freezeAuthority: client.payer.address // Authority allowed to freeze token accounts.})]);const [sourceToken] = await findAssociatedTokenPda({mint: mint.address,owner: owner.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const [destinationToken] = await findAssociatedTokenPda({mint: mint.address,owner: recipient.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});await client.sendTransaction([await getCreateAssociatedTokenInstructionAsync({payer: client.payer, // Account funding the associated token account creation.mint: mint.address, // Mint for the associated token account.owner: owner.address // Owner of the source token account.}),await getCreateAssociatedTokenInstructionAsync({payer: client.payer, // Account funding the associated token account creation.mint: mint.address, // Mint for the associated token account.owner: recipient.address // Owner of the destination token account.}),getMintToCheckedInstruction({mint: mint.address, // Mint account that issues the tokens.token: sourceToken, // Token account receiving the newly minted tokens.mintAuthority: client.payer, // Signer authorized to mint new tokens.amount: 1n, // Token amount in base units.decimals: 0 // Decimals defined on the mint.})]);await client.sendTransaction([getTransferCheckedInstruction({source: sourceToken, // Token account sending the transfer.mint: mint.address, // Mint with the permanent delegate configuration.destination: destinationToken, // Token account receiving the transfer.authority: delegate, // Permanent delegate signing the transfer.amount: 1n, // Token amount in base units.decimals: 0 // Decimals defined on the mint.})]);const destinationAccount = await fetchToken(client.rpc, destinationToken);const mintAccount = await fetchMint(client.rpc, mint.address);const permanentDelegate = (unwrapOption(mintAccount.data.extensions) ?? []).find((item) => isExtension("PermanentDelegate", item));console.log("Mint Address:", mint.address);console.log("Destination Amount:", destinationAccount.data.amount);console.log("Permanent Delegate Extension:", permanentDelegate);
Web3.js
import {Connection,Keypair,sendAndConfirmTransaction,SystemProgram,Transaction,LAMPORTS_PER_SOL} from "@solana/web3.js";import {ASSOCIATED_TOKEN_PROGRAM_ID,createAssociatedTokenAccountInstruction,createInitializeMintInstruction,createInitializePermanentDelegateInstruction,createMintToCheckedInstruction,createTransferCheckedInstruction,ExtensionType,getAccount,getAssociatedTokenAddressSync,getMint,getMintLen,getPermanentDelegate,TOKEN_2022_PROGRAM_ID} from "@solana/spl-token";const connection = new Connection("http://localhost:8899", "confirmed");const latestBlockhash = await connection.getLatestBlockhash();const feePayer = Keypair.generate();const owner = Keypair.generate();const delegate = Keypair.generate();const recipient = Keypair.generate();const airdropSignature = await connection.requestAirdrop(feePayer.publicKey,5 * LAMPORTS_PER_SOL);await connection.confirmTransaction({blockhash: latestBlockhash.blockhash,lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,signature: airdropSignature});const extensions = [ExtensionType.PermanentDelegate];const mint = Keypair.generate();const mintLength = getMintLen(extensions);const mintRent = await connection.getMinimumBalanceForRentExemption(mintLength);const sourceToken = getAssociatedTokenAddressSync(mint.publicKey,owner.publicKey,false,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID);const destinationToken = getAssociatedTokenAddressSync(mint.publicKey,recipient.publicKey,false,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID);const createMintAccountInstruction = SystemProgram.createAccount({fromPubkey: feePayer.publicKey, // Account funding the new mint account.newAccountPubkey: mint.publicKey, // New mint account to create.space: mintLength, // Account size in bytes for the mint plus PermanentDelegate.lamports: mintRent, // Lamports funding the mint account rent.programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.});const initializePermanentDelegateInstruction =createInitializePermanentDelegateInstruction(mint.publicKey, // Mint account that stores the PermanentDelegate extension.delegate.publicKey, // Permanent delegate authorized for all token accounts for the mint.TOKEN_2022_PROGRAM_ID // Token program that owns the mint.);const initializeMintInstruction = createInitializeMintInstruction(mint.publicKey, // Mint account to initialize.0, // Number of decimals for the token.feePayer.publicKey, // Authority allowed to mint new tokens.feePayer.publicKey, // Authority allowed to freeze token accounts.TOKEN_2022_PROGRAM_ID // Program that owns the mint account.);await sendAndConfirmTransaction(connection,new Transaction().add(createMintAccountInstruction,initializePermanentDelegateInstruction,initializeMintInstruction),[feePayer, mint],{commitment: "confirmed"});await sendAndConfirmTransaction(connection,new Transaction().add(createAssociatedTokenAccountInstruction(feePayer.publicKey, // Account funding the associated token account creation.sourceToken, // Associated token account address to create.owner.publicKey, // Owner of the source token account.mint.publicKey, // Mint for the associated token account.TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.ASSOCIATED_TOKEN_PROGRAM_ID // Associated Token Program that creates the account.),createAssociatedTokenAccountInstruction(feePayer.publicKey, // Account funding the associated token account creation.destinationToken, // Associated token account address to create.recipient.publicKey, // Owner of the destination token account.mint.publicKey, // Mint for the associated token account.TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.ASSOCIATED_TOKEN_PROGRAM_ID // Associated Token Program that creates the account.),createMintToCheckedInstruction(mint.publicKey, // Mint account that issues the tokens.sourceToken, // Token account receiving the newly minted tokens.feePayer.publicKey, // Signer authorized to mint new tokens.1, // Token amount in base units.0, // Decimals defined on the mint.[], // Additional multisig signers.TOKEN_2022_PROGRAM_ID // Token program that owns the mint and token account.)),[feePayer],{commitment: "confirmed"});await sendAndConfirmTransaction(connection,new Transaction().add(createTransferCheckedInstruction(sourceToken, // Token account sending the transfer.mint.publicKey, // Mint with the permanent delegate configuration.destinationToken, // Token account receiving the transfer.delegate.publicKey, // Permanent delegate signing the transfer.1, // Token amount in base units.0, // Decimals defined on the mint.[], // Additional multisig signers.TOKEN_2022_PROGRAM_ID // Token program that processes the transfer.)),[feePayer, delegate],{commitment: "confirmed"});const destinationAccount = await getAccount(connection,destinationToken,"confirmed",TOKEN_2022_PROGRAM_ID);const mintAccount = await getMint(connection,mint.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);const permanentDelegate = getPermanentDelegate(mintAccount);console.log("Mint Address:", mint.publicKey.toBase58());console.log("Destination Amount:", destinationAccount.amount.toString());console.log("Permanent Delegate Extension:", permanentDelegate);
Rust
use anyhow::Result;use solana_client::nonblocking::rpc_client::RpcClient;use solana_commitment_config::CommitmentConfig;use solana_sdk::{signature::{Keypair, Signer},transaction::Transaction,};use solana_system_interface::instruction::create_account;use spl_associated_token_account_interface::{address::get_associated_token_address_with_program_id,instruction::create_associated_token_account,};use spl_token_2022_interface::{extension::{permanent_delegate::PermanentDelegate, BaseStateWithExtensions, ExtensionType,StateWithExtensions,},instruction::{initialize_mint, initialize_permanent_delegate, mint_to_checked, transfer_checked,},state::{Account, Mint},ID as TOKEN_2022_PROGRAM_ID,};#[tokio::main]async fn main() -> Result<()> {let client = RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),);let fee_payer = Keypair::new();let owner = Keypair::new();let delegate = Keypair::new();let recipient = Keypair::new();let airdrop_signature = client.request_airdrop(&fee_payer.pubkey(), 5_000_000_000).await?;loop {let confirmed = client.confirm_transaction(&airdrop_signature).await?;if confirmed {break;}}let mint = Keypair::new();let mint_space =ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::PermanentDelegate])?;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space).await?;let create_mint_account_instruction = create_account(&fee_payer.pubkey(), // Account funding the new mint account.&mint.pubkey(), // New mint account to create.mint_rent, // Lamports funding the mint account rent.mint_space as u64, // Account size in bytes for the mint plus PermanentDelegate.&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.);let initialize_permanent_delegate_ix = initialize_permanent_delegate(&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.&mint.pubkey(), // Mint account that stores the PermanentDelegate extension.&delegate.pubkey(), // Permanent delegate authorized for all token accounts for the mint.)?;let initialize_mint_instruction = initialize_mint(&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.&mint.pubkey(), // Mint account to initialize.&fee_payer.pubkey(), // Authority allowed to mint new tokens.Some(&fee_payer.pubkey()), // Authority allowed to freeze token accounts.0, // Number of decimals for the token.)?;let create_mint_transaction = Transaction::new_signed_with_payer(&[create_mint_account_instruction,initialize_permanent_delegate_ix,initialize_mint_instruction,],Some(&fee_payer.pubkey()),&[&fee_payer, &mint],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&create_mint_transaction).await?;let source_token = get_associated_token_address_with_program_id(&owner.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let destination_token = get_associated_token_address_with_program_id(&recipient.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let create_token_accounts_transaction = Transaction::new_signed_with_payer(&[create_associated_token_account(&fee_payer.pubkey(), // Account funding the associated token account creation.&owner.pubkey(), // Owner of the source token account.&mint.pubkey(), // Mint for the associated token account.&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.),create_associated_token_account(&fee_payer.pubkey(), // Account funding the associated token account creation.&recipient.pubkey(), // Owner of the destination token account.&mint.pubkey(), // Mint for the associated token account.&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.),mint_to_checked(&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint and token account.&mint.pubkey(), // Mint account that issues the tokens.&source_token, // Token account receiving the newly minted tokens.&fee_payer.pubkey(), // Signer authorized to mint new tokens.&[&fee_payer.pubkey()], // Additional multisig signers.1, // Token amount in base units.0, // Decimals defined on the mint.)?,],Some(&fee_payer.pubkey()),&[&fee_payer],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&create_token_accounts_transaction).await?;let transfer_transaction = Transaction::new_signed_with_payer(&[transfer_checked(&TOKEN_2022_PROGRAM_ID, // Token program that processes the transfer.&source_token, // Token account sending the transfer.&mint.pubkey(), // Mint with the permanent delegate configuration.&destination_token, // Token account receiving the transfer.&delegate.pubkey(), // Permanent delegate signing the transfer.&[&delegate.pubkey()], // Additional multisig signers.1, // Token amount in base units.0, // Decimals defined on the mint.)?,],Some(&fee_payer.pubkey()),&[&fee_payer, &delegate],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&transfer_transaction).await?;let destination_account = client.get_account(&destination_token).await?;let destination_state =StateWithExtensions::<Account>::unpack(&destination_account.data)?;let mint_account = client.get_account(&mint.pubkey()).await?;let mint_state = StateWithExtensions::<Mint>::unpack(&mint_account.data)?;let permanent_delegate = mint_state.get_extension::<PermanentDelegate>()?;println!("Mint Address: {}", mint.pubkey());println!("Destination Amount: {}",u64::from(destination_state.base.amount));println!("Permanent Delegate Extension: {:?}", permanent_delegate);Ok(())}
Is this page helpful?