Comment activer l'extension Pausable
L'extension
PausableExtension
permet à une autorité de pause désignée d'arrêter toute activité de jetons sur
un mint. L'autorité de pause peut mettre en pause ou reprendre le mint à tout
moment.
Lorsqu'il est en pause, le programme Token-2022 rejette tous les transferts, mints et burns pour ce jeton. Lorsqu'il n'est plus en pause, les opérations normales reprennent.
Il s'agit d'un modèle courant dans les systèmes de jetons blockchain et la finance traditionnelle, où la capacité de geler l'activité est nécessaire pour la conformité, la réponse aux incidents ou le contrôle administratif.
Typescript
import {airdropFactory,appendTransactionMessageInstructions,assertIsTransactionWithBlockhashLifetime,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import { getCreateAccountInstruction } from "@solana-program/system";import {extension,fetchMint,findAssociatedTokenPda,getCreateAssociatedTokenInstructionAsync,getInitializeMintInstruction,getInitializePausableConfigInstruction,getMintSize,getMintToInstruction,getPauseInstruction,getResumeInstruction,getTransferCheckedInstruction,TOKEN_2022_PROGRAM_ADDRESS} from "@solana-program/token-2022";// Create Connection, local validator in this exampleconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");const sendAndConfirm = sendAndConfirmTransactionFactory({rpc,rpcSubscriptions});// Generate the authority (fee payer, mint authority, freeze authority, pause authority)const authority = await generateKeyPairSigner();// Fund authority/fee payerawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: authority.address,lamports: lamports(5_000_000_000n), // 5 SOLcommitment: "confirmed"});// ---------- Create mint with Pausable extension ----------const mint = await generateKeyPairSigner();// Define the pausable extension for size calculationconst pausableExtension = extension("PausableConfig", {authority: authority.address,paused: false});// Calculate space for mint account with the Pausable extensionconst space = BigInt(getMintSize([pausableExtension]));// Get minimum balance for rent exemptionconst rent = await rpc.getMinimumBalanceForRentExemption(space).send();// Get latest blockhashconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// 1) Create the mint accountconst createMintAccountInstruction = getCreateAccountInstruction({payer: authority,newAccount: mint,lamports: rent,space,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// 2) Initialize the pausable extension (must come BEFORE initializeMint)const initializePausableInstruction = getInitializePausableConfigInstruction({mint: mint.address,authority: authority.address});// 3) Initialize the mintconst initializeMintInstruction = getInitializeMintInstruction({mint: mint.address,decimals: 9,mintAuthority: authority.address,freezeAuthority: authority.address});// 4) Create associated token account for authorityconst [authorityAta] = await findAssociatedTokenPda({mint: mint.address,owner: authority.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const createAtaInstruction = await getCreateAssociatedTokenInstructionAsync({payer: authority,mint: mint.address,owner: authority.address});// 5) Mint 100 tokens (100 * 10^9)const mintToInstruction = getMintToInstruction({mint: mint.address,token: authorityAta,mintAuthority: authority,amount: 100_000_000_000n});// 6) Create recipient and their ATAconst recipient = await generateKeyPairSigner();const [recipientAta] = await findAssociatedTokenPda({mint: mint.address,owner: recipient.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const createRecipientAtaInstruction =await getCreateAssociatedTokenInstructionAsync({payer: authority,mint: mint.address,owner: recipient.address});// Send transaction to create mint with pausable extension, ATAs, and mint tokensconst createMintTx = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(authority, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) =>appendTransactionMessageInstructions([createMintAccountInstruction,initializePausableInstruction,initializeMintInstruction,createAtaInstruction,mintToInstruction,createRecipientAtaInstruction],tx));const signedCreateMintTx =await signTransactionMessageWithSigners(createMintTx);assertIsTransactionWithBlockhashLifetime(signedCreateMintTx);await sendAndConfirm(signedCreateMintTx, {commitment: "confirmed",skipPreflight: true});console.log("Mint created with Pausable extension:", mint.address);// Read back the pausable config from the mintlet mintAccount = await fetchMint(rpc, mint.address);console.log("Pausable config:", mintAccount.data.extensions);// ---------- Pause the mint ----------const pauseInstruction = getPauseInstruction({mint: mint.address,authority: authority});const { value: latestBlockhash2 } = await rpc.getLatestBlockhash().send();const pauseTx = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(authority, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash2, tx),(tx) => appendTransactionMessageInstructions([pauseInstruction], tx));const signedPauseTx = await signTransactionMessageWithSigners(pauseTx);assertIsTransactionWithBlockhashLifetime(signedPauseTx);await sendAndConfirm(signedPauseTx, {commitment: "confirmed"});console.log("\nMint is now PAUSED");// Verify paused statemintAccount = await fetchMint(rpc, mint.address);console.log("Pausable config:", mintAccount.data.extensions);// Try a transfer while paused (should fail)const transferInstruction = getTransferCheckedInstruction({source: authorityAta,mint: mint.address,destination: recipientAta,authority: authority,amount: 1_000_000_000n, // 1 tokendecimals: 9});const { value: latestBlockhash3 } = await rpc.getLatestBlockhash().send();const transferWhilePausedTx = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(authority, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash3, tx),(tx) => appendTransactionMessageInstructions([transferInstruction], tx));const signedTransferWhilePausedTx = await signTransactionMessageWithSigners(transferWhilePausedTx);console.log("\nTransfer Expected to Fail with Error:");try {assertIsTransactionWithBlockhashLifetime(signedTransferWhilePausedTx);await sendAndConfirm(signedTransferWhilePausedTx, {commitment: "confirmed"});} catch (error: any) {console.log(" ", error.message ?? error);}// ---------- Resume (unpause) the mint ----------const resumeInstruction = getResumeInstruction({mint: mint.address,authority: authority});const { value: latestBlockhash4 } = await rpc.getLatestBlockhash().send();const resumeTx = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(authority, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash4, tx),(tx) => appendTransactionMessageInstructions([resumeInstruction], tx));const signedResumeTx = await signTransactionMessageWithSigners(resumeTx);assertIsTransactionWithBlockhashLifetime(signedResumeTx);await sendAndConfirm(signedResumeTx, {commitment: "confirmed"});console.log("\nMint is now RESUMED");// Verify resumed statemintAccount = await fetchMint(rpc, mint.address);console.log("Pausable config:", mintAccount.data.extensions);// Transfer should now succeed after resumeconst { value: latestBlockhash5 } = await rpc.getLatestBlockhash().send();const transferAfterResumeTx = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(authority, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash5, tx),(tx) => appendTransactionMessageInstructions([transferInstruction], tx));const signedTransferAfterResumeTx = await signTransactionMessageWithSigners(transferAfterResumeTx);assertIsTransactionWithBlockhashLifetime(signedTransferAfterResumeTx);await sendAndConfirm(signedTransferAfterResumeTx, {commitment: "confirmed"});const transferSig = getSignatureFromTransaction(signedTransferAfterResumeTx);console.log("\nTransfer after resume succeeded!");console.log("Transaction Signature:", transferSig);
Console
Click to execute the code.
Rust
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::{pausable::{instruction as pausable_ix, PausableConfig},BaseStateWithExtensions, ExtensionType, StateWithExtensions,},instruction::{initialize_mint, mint_to, 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 latest_blockhash = client.get_latest_blockhash().await?;let fee_payer = Keypair::new();// Airdrop SOL to fee payerlet 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;}}// ---------- Create mint with Pausable extension ----------let mint = Keypair::new();// Calculate space for mint account with the Pausable extensionlet mint_space = ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::Pausable])?;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space).await?;// 1) Create the mint accountlet create_mint_account_ix = create_account(&fee_payer.pubkey(),&mint.pubkey(),mint_rent,mint_space as u64,&TOKEN_2022_PROGRAM_ID,);// 2) Initialize the pausable extension (must come BEFORE initialize_mint)let initialize_pausable_ix = pausable_ix::initialize(&TOKEN_2022_PROGRAM_ID,&mint.pubkey(),&fee_payer.pubkey(), // pause authority)?;// 3) Initialize the mintlet initialize_mint_ix = initialize_mint(&TOKEN_2022_PROGRAM_ID,&mint.pubkey(),&fee_payer.pubkey(), // mint authoritySome(&fee_payer.pubkey()), // freeze authority9, // decimals)?;// 4) Create associated token account for fee_payerlet ata = get_associated_token_address_with_program_id(&fee_payer.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let create_ata_ix = create_associated_token_account(&fee_payer.pubkey(),&fee_payer.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);// 5) Mint 100 tokenslet amount = 100_000_000_000; // 100 tokens with 9 decimalslet mint_to_ix = mint_to(&TOKEN_2022_PROGRAM_ID,&mint.pubkey(),&ata,&fee_payer.pubkey(),&[&fee_payer.pubkey()],amount,)?;// 6) Create recipient ATAlet recipient = Keypair::new();let recipient_ata = get_associated_token_address_with_program_id(&recipient.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let create_recipient_ata_ix = create_associated_token_account(&fee_payer.pubkey(),&recipient.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let tx = Transaction::new_signed_with_payer(&[create_mint_account_ix,initialize_pausable_ix,initialize_mint_ix,create_ata_ix,mint_to_ix,create_recipient_ata_ix,],Some(&fee_payer.pubkey()),&[&fee_payer, &mint],latest_blockhash,);client.send_and_confirm_transaction(&tx).await?;println!("Mint created with Pausable extension: {}", mint.pubkey());// Read back the pausable config from the mintlet mint_data = client.get_account(&mint.pubkey()).await?;let mint_state = StateWithExtensions::<Mint>::unpack(&mint_data.data)?;let pausable = mint_state.get_extension::<PausableConfig>()?;println!("Pausable config: {:#?}", pausable);// ---------- Pause the mint ----------let pause_ix = pausable_ix::pause(&TOKEN_2022_PROGRAM_ID,&mint.pubkey(),&fee_payer.pubkey(), // pause authority&[&fee_payer.pubkey()],)?;let latest_blockhash = client.get_latest_blockhash().await?;let tx = Transaction::new_signed_with_payer(&[pause_ix],Some(&fee_payer.pubkey()),&[&fee_payer],latest_blockhash,);client.send_and_confirm_transaction(&tx).await?;println!("\nMint is now PAUSED");// Verify paused statelet mint_data = client.get_account(&mint.pubkey()).await?;let mint_state = StateWithExtensions::<Mint>::unpack(&mint_data.data)?;let pausable = mint_state.get_extension::<PausableConfig>()?;println!("Pausable config: {:#?}", pausable);// Try a transfer while paused (should fail)let transfer_ix = transfer_checked(&TOKEN_2022_PROGRAM_ID,&ata,&mint.pubkey(),&recipient_ata,&fee_payer.pubkey(),&[&fee_payer.pubkey()],1_000_000_000, // 1 token9,)?;let latest_blockhash = client.get_latest_blockhash().await?;let tx = Transaction::new_signed_with_payer(&[transfer_ix.clone()],Some(&fee_payer.pubkey()),&[&fee_payer],latest_blockhash,);println!("\nTransfer Expected to Fail with Error:");let result = client.simulate_transaction(&tx).await?;if result.value.err.is_some() {if let Some(logs) = result.value.logs {for log in logs.iter().filter(|l| l.starts_with("Program log:")) {println!(" {}", log);}}}// ---------- Resume (unpause) the mint ----------let resume_ix = pausable_ix::resume(&TOKEN_2022_PROGRAM_ID,&mint.pubkey(),&fee_payer.pubkey(), // pause authority&[&fee_payer.pubkey()],)?;let latest_blockhash = client.get_latest_blockhash().await?;let tx = Transaction::new_signed_with_payer(&[resume_ix],Some(&fee_payer.pubkey()),&[&fee_payer],latest_blockhash,);client.send_and_confirm_transaction(&tx).await?;println!("\nMint is now RESUMED");// Verify resumed statelet mint_data = client.get_account(&mint.pubkey()).await?;let mint_state = StateWithExtensions::<Mint>::unpack(&mint_data.data)?;let pausable = mint_state.get_extension::<PausableConfig>()?;println!("Pausable config: {:#?}", pausable);// Transfer should now succeedlet latest_blockhash = client.get_latest_blockhash().await?;let tx = Transaction::new_signed_with_payer(&[transfer_ix],Some(&fee_payer.pubkey()),&[&fee_payer],latest_blockhash,);let sig = client.send_and_confirm_transaction(&tx).await?;println!("\nTransfer after resume succeeded!");println!("Transaction Signature: {}", sig);// Verify recipient received the tokenslet recipient_data = client.get_account(&recipient_ata).await?;let recipient_state = StateWithExtensions::<Account>::unpack(&recipient_data.data)?;println!("\nRecipient Token Account: {:#?}", recipient_state.base);Ok(())}
Console
Click to execute the code.
Is this page helpful?