영구 위임자란 무엇인가요?
Token Extensions Program의 PermanentDelegate 민트 확장은 민트 자체에 위임
권한을 할당합니다.
영구 위임자는 해당 민트의 모든 token account에 대한 전송 및 소각을 승인할 수 있는 민트 수준의 권한입니다. 민트의 모든 token account에 대한 전역 위임자 역할을 하며, token account 소유자는 자신의 token account에서 영구 위임자를 취소할 수 없습니다.
민트 권한은 나중에 *rsAuthorityType::PermanentDelegate*를 사용하여
*rsSetAuthority*로 영구 위임자를 교체할 수도 있습니다.
영구 위임자를 생성하고 사용하는 방법
영구 위임자를 생성하고 사용하려면:
- 민트 계정 크기와 민트 및
PermanentDelegate확장에 필요한 rent를 계산합니다. - *rs
CreateAccount*로 민트 계정을 생성하고, *rsPermanentDelegate*를 초기화한 다음, *rsInitializeMint*로 민트를 초기화합니다. - 영구 위임자가 서명한
TransferChecked또는 *rsBurnChecked*를 제출합니다. Token Program은 token account 소유자가 서명한 것과 동일한 방식으로 해당 명령을 처리합니다.
계정 크기 계산
기본 민트에 PermanentDelegate 확장을 더한 민트 계정 크기를 계산합니다.
이것이 *rsCreateAccount*에서 사용되는 크기입니다.
rent 계산
민트에 필요한 크기와 PermanentDelegate 확장을 사용하여 rent를 계산합니다.
민트 계정 생성
계산된 공간과 lamport로 mint account를 생성합니다.
PermanentDelegate 초기화
민트에서 PermanentDelegate 확장을 초기화합니다.
민트 초기화
동일한 트랜잭션에서 *rsInitializeMint*로 민트를 초기화합니다.
token account 생성 및 토큰 발행
소유자와 수신자를 위한 token account를 생성한 다음, 소유자 token account로 토큰을 발행합니다.
영구 위임자로 전송
영구 위임자가 서명한 *rsTransferChecked*로 토큰을 전송합니다.
명령 순서
_rsInitializePermanentDelegate_는 _rsInitializeMint_보다 먼저 와야 합니다.
CreateAccount, InitializePermanentDelegate, 그리고
_rsInitializeMint_는 동일한 트랜잭션에 포함되어야 합니다.
소스 참조
| 항목 | 설명 | 소스 |
|---|---|---|
PermanentDelegate | 민트의 모든 token account에 대해 승인된 위임자를 저장하는 민트 확장입니다. | 소스 |
InitializePermanentDelegate | InitializeMint 이전에 영구 위임자를 초기화하는 명령입니다. | 소스 |
SetAuthority | *rsAuthorityType::PermanentDelegate*와 함께 사용하여 위임자를 교체하는 기본 토큰 명령입니다. | 소스 |
AuthorityType::PermanentDelegate | *rsSetAuthority*와 함께 사용하여 민트의 영구 위임자를 교체하는 권한 식별자입니다. | 소스 |
process_initialize_permanent_delegate | 민트에 영구 위임자 확장을 기록하는 프로세서 로직입니다. | 소스 |
process_set_authority | *rsAuthorityType::PermanentDelegate*가 사용될 때 영구 위임자를 검증하고 교체하는 프로세서 로직입니다. | 소스 |
Typescript
아래의 Kit 예제는 생성된 명령을 직접 사용합니다. @solana/web3.js 및
@solana/spl-token를 사용하는 레거시 예제는 참고용으로 포함되어 있습니다.
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?