Fermeture de mint

Qu'est-ce que l'autorité de fermeture de mint ?

L'extension de mint MintCloseAuthority du Token Extensions Program permet à une autorité désignée de fermer un mint account et de récupérer son rent une fois que l'offre totale du mint atteint zéro.

Sans cette extension, un mint account ne peut pas être fermé.

  • L'offre totale doit être de zéro, donc tous les tokens de tous les token accounts doivent être brûlés avant qu'un mint puisse être fermé
  • Seule l'autorité de fermeture configurée peut fermer le mint

Comment créer et fermer un mint

Pour créer et fermer un mint :

  1. Calculez la taille du mint account et le rent nécessaire pour le mint et l'extension MintCloseAuthority.
  2. Créez le mint account avec CreateAccount, initialisez MintCloseAuthority, et initialisez le mint avec InitializeMint.
  3. Si l'offre totale est zéro, le mint account peut être fermé avec CloseAccount, signé par l'autorité de fermeture.

Calculer la taille du compte

Calculez la taille du mint account pour le mint de base plus l'extension MintCloseAuthority. C'est la taille utilisée dans CreateAccount.

Calculer le rent

Calculez le rent en utilisant la taille nécessaire pour le mint plus l'extension MintCloseAuthority.

Créer le mint account

Créez le mint account avec l'espace et les lamports calculés.

Initialiser MintCloseAuthority

Initialisez l'extension MintCloseAuthority sur le mint.

Initialiser le mint

Initialisez le mint avec InitializeMint dans la même transaction.

Calculer la taille du compte

Calculez la taille du mint account pour le mint de base plus l'extension MintCloseAuthority. C'est la taille utilisée dans CreateAccount.

Calculer le rent

Calculez le rent en utilisant la taille nécessaire pour le mint plus l'extension MintCloseAuthority.

Créer le mint account

Créez le mint account avec l'espace et les lamports calculés.

Initialiser MintCloseAuthority

Initialisez l'extension MintCloseAuthority sur le mint.

Initialiser le mint

Initialisez le mint avec InitializeMint dans la même transaction.

Example
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 destination = await generateKeyPairSigner();
const mintCloseAuthorityExtension = extension("MintCloseAuthority", {
closeAuthority: client.payer.address
});
const mintSpace = BigInt(getMintSize([mintCloseAuthorityExtension]));

Ordre des instructions

InitializeMintCloseAuthority doit précéder InitializeMint. CreateAccount, InitializeMintCloseAuthority et InitializeMint doivent être inclus dans la même transaction.

Référence source

ÉlémentDescriptionSource
MintCloseAuthorityExtension de mint qui stocke l'autorité autorisée à fermer un mint une fois que son offre atteint zéro.Source
InitializeMintCloseAuthorityInstruction qui initialise l'autorité de fermeture du mint avant InitializeMint.Source
CloseAccountInstruction de token de base qui ferme le compte mint après que l'offre totale atteint zéro.Source
process_initialize_mint_close_authorityLogique de processeur qui écrit l'extension MintCloseAuthority sur un mint non initialisé.Source
process_close_accountGestionnaire de fermeture de compte partagé qui applique la règle d'offre zéro pour les mints avec autorité de fermeture.Source

Typescript

L'exemple Kit ci-dessous utilise directement les instructions générées. Des exemples hérités utilisant @solana/web3.js et @solana/spl-token sont inclus pour référence.

Kit

Instructions
import { lamports, createClient, generateKeyPairSigner } 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,
findAssociatedTokenPda,
getBurnCheckedInstruction,
getCloseAccountInstruction,
getCreateAssociatedTokenInstructionAsync,
getInitializeMintCloseAuthorityInstruction,
getInitializeMintInstruction,
getMintSize,
getMintToCheckedInstruction,
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 destination = await generateKeyPairSigner();
const mintCloseAuthorityExtension = extension("MintCloseAuthority", {
closeAuthority: client.payer.address
});
const mintSpace = BigInt(getMintSize([mintCloseAuthorityExtension]));
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializeMintCloseAuthorityInstruction({
mint: mint.address, // Mint account that stores the MintCloseAuthority extension.
closeAuthority: client.payer.address // Authority allowed to close the mint.
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);
const [token] = await findAssociatedTokenPda({
mint: mint.address,
owner: client.payer.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
await client.sendTransaction([
await getCreateAssociatedTokenInstructionAsync({
payer: client.payer,
mint: mint.address,
owner: client.payer.address
}),
getMintToCheckedInstruction({
mint: mint.address,
token,
mintAuthority: client.payer,
amount: 1n,
decimals: 0
})
]);
await client.sendTransaction([
getBurnCheckedInstruction({
mint: mint.address,
account: token,
authority: client.payer,
amount: 1n,
decimals: 0
}),
getCloseAccountInstruction({
account: mint.address, // Mint account to close.
destination: destination.address, // Account receiving the reclaimed lamports.
owner: client.payer // Close authority signing the instruction.
})
]);
const mintInfo = await client.rpc.getAccountInfo(mint.address).send();
console.log("Mint Address:", mint.address);
console.log("Destination Address:", destination.address);
console.log("Mint Closed:", mintInfo.value === null);
Console
Click to execute the code.

Web3.js

Instructions
import {
Connection,
Keypair,
sendAndConfirmTransaction,
SystemProgram,
Transaction,
LAMPORTS_PER_SOL
} from "@solana/web3.js";
import {
createInitializeMintInstruction,
ExtensionType,
getMintLen,
createInitializeMintCloseAuthorityInstruction,
TOKEN_2022_PROGRAM_ID,
createCloseAccountInstruction
} from "@solana/spl-token";
const connection = new Connection("http://localhost:8899", "confirmed");
const latestBlockhash = await connection.getLatestBlockhash();
const feePayer = Keypair.generate();
const destination = 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 mint = Keypair.generate();
const extensions = [ExtensionType.MintCloseAuthority];
const mintLength = getMintLen(extensions);
const mintRent = await connection.getMinimumBalanceForRentExemption(mintLength);
const createAccountInstruction = SystemProgram.createAccount({
fromPubkey: feePayer.publicKey,
newAccountPubkey: mint.publicKey,
space: mintLength,
lamports: mintRent,
programId: TOKEN_2022_PROGRAM_ID
});
const initializeMintCloseAuthorityInstruction =
createInitializeMintCloseAuthorityInstruction(
mint.publicKey, // Mint account that stores the MintCloseAuthority extension.
feePayer.publicKey, // Authority allowed to close the mint.
TOKEN_2022_PROGRAM_ID // Token program that owns the mint.
);
const initializeMintInstruction = createInitializeMintInstruction(
mint.publicKey, // mint pubkey
9, // decimals
feePayer.publicKey, // mint authority
feePayer.publicKey, // freeze authority
TOKEN_2022_PROGRAM_ID
);
const transaction = new Transaction({
feePayer: feePayer.publicKey,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
}).add(
createAccountInstruction,
initializeMintCloseAuthorityInstruction,
initializeMintInstruction
);
const transactionSignature = await sendAndConfirmTransaction(
connection,
transaction,
[feePayer, mint]
);
console.log("Mint Address:", mint.publicKey.toBase58());
console.log("Transaction Signature:", transactionSignature);
const latestBlockhash2 = await connection.getLatestBlockhash();
const closeMintInstruction = createCloseAccountInstruction(
mint.publicKey, // Mint account to close.
destination.publicKey, // Account receiving the reclaimed lamports.
feePayer.publicKey, // Close authority signing the instruction.
[], // Additional multisig signers.
TOKEN_2022_PROGRAM_ID // Token program that owns the mint.
);
const closeMintTransaction = new Transaction({
feePayer: feePayer.publicKey,
blockhash: latestBlockhash2.blockhash,
lastValidBlockHeight: latestBlockhash2.lastValidBlockHeight
}).add(closeMintInstruction);
const transactionSignature3 = await sendAndConfirmTransaction(
connection,
closeMintTransaction,
[feePayer]
);
console.log("\nDestination Address:", destination.publicKey.toBase58());
console.log("Transaction Signature:", transactionSignature3);
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_token_2022_interface::{
extension::{
mint_close_authority::MintCloseAuthority, BaseStateWithExtensions, ExtensionType,
StateWithExtensions,
},
instruction::{close_account, initialize_mint, initialize_mint_close_authority},
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 destination = 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::MintCloseAuthority])?;
let mint_rent = client
.get_minimum_balance_for_rent_exemption(mint_space)
.await?;
let create_account_instruction = create_account(
&fee_payer.pubkey(), // payer
&mint.pubkey(), // new account (mint)
mint_rent, // lamports
mint_space as u64, // space
&TOKEN_2022_PROGRAM_ID, // program id
);
let initialize_close_mint_instruction = initialize_mint_close_authority(
&TOKEN_2022_PROGRAM_ID, // token program
&mint.pubkey(), // mint
Some(&fee_payer.pubkey()), // close authority
)?;
let initialize_mint_instruction = initialize_mint(
&TOKEN_2022_PROGRAM_ID, // token program
&mint.pubkey(), // mint
&fee_payer.pubkey(), // mint authority
Some(&fee_payer.pubkey()), // freeze authority
9, // decimals
)?;
let transaction = Transaction::new_signed_with_payer(
&[
create_account_instruction,
initialize_close_mint_instruction,
initialize_mint_instruction,
],
Some(&fee_payer.pubkey()),
&[&fee_payer, &mint],
client.get_latest_blockhash().await?,
);
client.send_and_confirm_transaction(&transaction).await?;
println!("Mint Address: {}", mint.pubkey());
let mint_account = client.get_account(&mint.pubkey()).await?;
let mint_state = StateWithExtensions::<Mint>::unpack(&mint_account.data)?;
let mint_extension_types = mint_state.get_extension_types()?;
println!("\nMint extensions enabled: {:?}", mint_extension_types);
let mint_close_authority = mint_state.get_extension::<MintCloseAuthority>()?;
println!("\n{:#?}", mint_close_authority);
let close_mint_instruction = close_account(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.
&mint.pubkey(), // Mint account to close.
&destination.pubkey(), // Account receiving the reclaimed lamports.
&fee_payer.pubkey(), // Close authority signing the instruction.
&[&fee_payer.pubkey()], // Additional multisig signers.
)?;
let transaction = Transaction::new_signed_with_payer(
&[close_mint_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer],
client.get_latest_blockhash().await?,
);
client.send_and_confirm_transaction(&transaction).await?;
match client.get_account(&mint.pubkey()).await {
Ok(_) => println!("\nMint account still exists (unexpected)"),
Err(e) => println!("\nMint account successfully closed: {:#?}", e),
}
Ok(())
}
Console
Click to execute the code.

Is this page helpful?

Table des matières

Modifier la page

Géré par

© 2026 Fondation Solana.
Tous droits réservés.
Restez connecté