Що таке мінт з можливістю паузи?
Розширення мінта PausableConfig програми Token Extension надає
повноваженням паузи можливість призупиняти та відновлювати активність токенів
для всього мінта.
Коли мінт призупинено, програма Token Extension відхиляє:
- Перекази
- Мінтинг
- Спалювання
Коли мінт відновлено, ці інструкції знову працюють у звичайному режимі.
Як створити, призупинити та відновити мінт
Щоб створити, призупинити та відновити мінт:
- Розрахуйте розмір облікового запису мінта та необхідну ренту для мінта та
розширення
PausableConfig. - Створіть обліковий запис мінта за допомогою
CreateAccount, ініціалізуйтеPausableConfigта ініціалізуйте мінт за допомогоюInitializeMint. - Змінтіть токени та створіть токен-акаунти для мінта.
- Використовуйте
Pause, щоб заблокувати активність токенів для мінта. - Використовуйте
Resume, щоб знову дозволити активність токенів.
Розрахуйте розмір облікового запису
Розрахуйте розмір облікового запису мінта для базового мінта плюс розширення
PausableConfig. Це розмір, який використовується в CreateAccount.
Розрахуйте ренту
Розрахуйте ренту, використовуючи розмір, необхідний для мінта плюс розширення
PausableConfig.
Створіть обліковий запис мінта
Створіть mint account з обчисленим простором та lamport.
Ініціалізація PausableConfig
Ініціалізуйте розширення PausableConfig на mint.
Ініціалізація mint
Ініціалізуйте mint за допомогою InitializeMint в тій самій транзакції.
Призупинення mint
Призупиніть mint за допомогою Pause.
Відновлення mint
Відновіть mint за допомогою Resume.
Порядок інструкцій
PausableInstruction::Initialize повинен виконуватись перед
InitializeMint. CreateAccount,
PausableInstruction::Initialize та InitializeMint повинні бути
включені в одну транзакцію.
Посилання на джерело
| Елемент | Опис | Джерело |
|---|---|---|
PausableConfig | Розширення mint, яке зберігає повноваження на призупинення та поточний стан призупинення mint. | Джерело |
PausableInstruction::Initialize | Інструкція, яка ініціалізує pausable config перед InitializeMint. | Джерело |
PausableInstruction::Pause | Інструкція, яка позначає mint як призупинений. | Джерело |
PausableInstruction::Resume | Інструкція, яка позначає mint як активний. | Джерело |
process_initialize | Логіка процесора, яка ініціалізує PausableConfig на неініціалізованому mint і зберігає повноваження на призупинення. | Джерело |
process_toggle_pause | Логіка процесора, яка перевіряє повноваження на призупинення перед перемиканням прапорця призупинення mint. | Джерело |
Typescript
Приклад Kit нижче використовує згенеровані інструкції безпосередньо. Застарілі
приклади з використанням @solana/web3.js та @solana/spl-token включені для
довідки.
Комплект
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,findAssociatedTokenPda,getCreateAssociatedTokenInstructionAsync,getInitializeMintInstruction,getInitializePausableConfigInstruction,getMintSize,getMintToCheckedInstruction,getPauseInstruction,getResumeInstruction,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 recipient = await generateKeyPairSigner();const pausableExtension = extension("PausableConfig", {authority: client.payer.address,paused: false});const mintSpace = BigInt(getMintSize([pausableExtension]));const mintRent = await client.rpc.getMinimumBalanceForRentExemption(mintSpace).send();await client.sendTransaction([getCreateAccountInstruction({payer: client.payer, // Account funding account creation.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 PausableConfig.programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.}),getInitializePausableConfigInstruction({mint: mint.address, // Mint account that stores the PausableConfig extension.authority: client.payer.address // Authority allowed to pause and resume 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: client.payer.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: client.payer.address // Owner of the associated 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 associated 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([getPauseInstruction({mint: mint.address, // Mint account to pause.authority: client.payer // Signer authorized to pause the mint.})]);const mintAccountAfterPause = await fetchMint(client.rpc, mint.address);const configAfterPause = (unwrapOption(mintAccountAfterPause.data.extensions) ?? []).find((item) => isExtension("PausableConfig", item));let pausedTransferFailure: string | undefined;try {await client.sendTransaction([getTransferCheckedInstruction({source: sourceToken, // Token account sending the transfer.mint: mint.address, // Mint with the pausable configuration.destination: destinationToken, // Token account receiving the transfer.authority: client.payer, // Signer approving the transfer.amount: 1n, // Token amount in base units.decimals: 0 // Decimals defined on the mint.})]);} catch (error) {pausedTransferFailure =error instanceof Error ? error.message : String(error);}if (!pausedTransferFailure) {throw new Error("Expected the paused transfer to fail");}await client.sendTransaction([getResumeInstruction({mint: mint.address, // Mint account to resume.authority: client.payer // Signer authorized to resume the mint.})]);await client.sendTransaction([getTransferCheckedInstruction({source: sourceToken, // Token account sending the transfer.mint: mint.address, // Mint with the pausable configuration.destination: destinationToken, // Token account receiving the transfer.authority: client.payer, // Signer approving the transfer.amount: 1n, // Token amount in base units.decimals: 0 // Decimals defined on the mint.})]);const mintAccountAfterResume = await fetchMint(client.rpc, mint.address);const configAfterResume = (unwrapOption(mintAccountAfterResume.data.extensions) ?? []).find((item) => isExtension("PausableConfig", item));console.log("\nMint Address:", mint.address);console.log("\nConfig After Pause:", configAfterPause);console.log("\nError From Failed Transaction:", pausedTransferFailure);console.log("\nConfig After Resume:", configAfterResume);
Web3.js
import {Connection,Keypair,LAMPORTS_PER_SOL,sendAndConfirmTransaction,SystemProgram,Transaction} from "@solana/web3.js";import {ASSOCIATED_TOKEN_PROGRAM_ID,createAssociatedTokenAccountInstruction,createInitializeMintInstruction,createInitializePausableConfigInstruction,createMintToCheckedInstruction,createPauseInstruction,createResumeInstruction,createTransferCheckedInstruction,ExtensionType,getAssociatedTokenAddressSync,getMint,getMintLen,getPausableConfig,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 mint = 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 mintSpace = getMintLen([ExtensionType.PausableConfig]);const mintRent = await connection.getMinimumBalanceForRentExemption(mintSpace);await sendAndConfirmTransaction(connection,new Transaction().add(SystemProgram.createAccount({fromPubkey: feePayer.publicKey, // Account funding account creation.newAccountPubkey: mint.publicKey, // New mint account to create.space: mintSpace, // Account size in bytes for the mint plus PausableConfig.lamports: mintRent, // Lamports funding the mint account rent.programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.}),createInitializePausableConfigInstruction(mint.publicKey, // Mint account that stores the PausableConfig extension.feePayer.publicKey, // Authority allowed to pause and resume the mint.TOKEN_2022_PROGRAM_ID // Token program that owns the mint.),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.)),[feePayer, mint],{ commitment: "confirmed" });const sourceToken = getAssociatedTokenAddressSync(mint.publicKey,feePayer.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);await sendAndConfirmTransaction(connection,new Transaction().add(createAssociatedTokenAccountInstruction(feePayer.publicKey, // Account funding the associated token account creation.sourceToken, // Associated token account address to create.feePayer.publicKey, // Owner of the associated 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 associated 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(createPauseInstruction(mint.publicKey, // Mint account to pause.feePayer.publicKey, // Authority allowed to pause the mint.[], // Additional multisig signers.TOKEN_2022_PROGRAM_ID // Token program that owns the mint.)),[feePayer],{ commitment: "confirmed" });const mintAccountAfterPause = await getMint(connection,mint.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);const configAfterPause = getPausableConfig(mintAccountAfterPause);let pausedTransferFailure: string | undefined;try {await sendAndConfirmTransaction(connection,new Transaction().add(createTransferCheckedInstruction(sourceToken, // Token account sending the transfer.mint.publicKey, // Mint with the pausable configuration.destinationToken, // Token account receiving the transfer.feePayer.publicKey, // Signer approving 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],{ commitment: "confirmed" });} catch (error: any) {pausedTransferFailure =error instanceof Error ? error.message : String(error);}if (!pausedTransferFailure) {throw new Error("Expected the paused transfer to fail");}await sendAndConfirmTransaction(connection,new Transaction().add(createResumeInstruction(mint.publicKey, // Mint account to resume.feePayer.publicKey, // Authority allowed to resume the mint.[], // Additional multisig signers.TOKEN_2022_PROGRAM_ID // Token program that owns the mint.)),[feePayer],{ commitment: "confirmed" });await sendAndConfirmTransaction(connection,new Transaction().add(createTransferCheckedInstruction(sourceToken, // Token account sending the transfer.mint.publicKey, // Mint with the pausable configuration.destinationToken, // Token account receiving the transfer.feePayer.publicKey, // Signer approving 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],{ commitment: "confirmed" });const mintAccountAfterResume = await getMint(connection,mint.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);const configAfterResume = getPausableConfig(mintAccountAfterResume);console.log("\nMint Address:", mint.publicKey.toBase58());console.log("\nConfig After Pause:", configAfterPause);console.log("\nError From Failed Transaction:", pausedTransferFailure);console.log("\nConfig After Resume:", configAfterResume);
Rust
use anyhow::{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::{pausable::{instruction as pausable_ix, PausableConfig},BaseStateWithExtensions, ExtensionType, StateWithExtensions,},instruction::{initialize_mint, mint_to_checked, transfer_checked},state::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 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 recipient = Keypair::new();let mint_space = ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::Pausable])?;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space).await?;let create_mint_transaction = Transaction::new_signed_with_payer(&[create_account(&fee_payer.pubkey(), // Account funding account creation.&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 PausableConfig.&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.),pausable_ix::initialize(&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.&mint.pubkey(), // Mint account that stores the PausableConfig extension.&fee_payer.pubkey(), // Authority allowed to pause and resume the mint.)?,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.)?,],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(&fee_payer.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.&fee_payer.pubkey(), // Owner of the associated 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 associated 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 pause_ix = pausable_ix::pause(&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.&mint.pubkey(), // Mint account to pause.&fee_payer.pubkey(), // Authority allowed to pause the mint.&[&fee_payer.pubkey()], // Additional multisig signers.)?;let pause_transaction = Transaction::new_signed_with_payer(&[pause_ix],Some(&fee_payer.pubkey()),&[&fee_payer],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&pause_transaction).await?;let mint_data_after_pause = client.get_account(&mint.pubkey()).await?;let mint_state_after_pause =StateWithExtensions::<Mint>::unpack(&mint_data_after_pause.data)?;let config_after_pause = mint_state_after_pause.get_extension::<PausableConfig>()?;let transfer_ix = transfer_checked(&TOKEN_2022_PROGRAM_ID, // Token program that processes the transfer.&source_token, // Token account sending the transfer.&mint.pubkey(), // Mint with the pausable configuration.&destination_token, // Token account receiving the transfer.&fee_payer.pubkey(), // Signer approving the transfer.&[&fee_payer.pubkey()], // Additional multisig signers.1, // Token amount in base units.0, // Decimals defined on the mint.)?;let paused_transfer_transaction = Transaction::new_signed_with_payer(&[transfer_ix.clone()],Some(&fee_payer.pubkey()),&[&fee_payer],client.get_latest_blockhash().await?,);let paused_transfer_result = client.simulate_transaction(&paused_transfer_transaction).await?;let paused_transfer_failure = paused_transfer_result.value.err.ok_or_else(|| anyhow!("Expected the paused transfer to fail"))?;let resume_ix = pausable_ix::resume(&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.&mint.pubkey(), // Mint account to resume.&fee_payer.pubkey(), // Authority allowed to resume the mint.&[&fee_payer.pubkey()], // Additional multisig signers.)?;let resume_transaction = Transaction::new_signed_with_payer(&[resume_ix],Some(&fee_payer.pubkey()),&[&fee_payer],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&resume_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 pausable configuration.&destination_token, // Token account receiving the transfer.&fee_payer.pubkey(), // Signer approving the transfer.&[&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(&transfer_transaction).await?;let mint_data_after_resume = client.get_account(&mint.pubkey()).await?;let mint_state_after_resume =StateWithExtensions::<Mint>::unpack(&mint_data_after_resume.data)?;let config_after_resume = mint_state_after_resume.get_extension::<PausableConfig>()?;println!("\nMint Address: {}", mint.pubkey());println!("\nConfig After Pause: {:#?}", config_after_pause);println!("\nError From Failed Transaction: {:?}", paused_transfer_failure);println!("\nConfig After Resume: {:#?}", config_after_resume);Ok(())}
Is this page helpful?