Close Mint

What Is Mint Close Authority?

The Token Extension Program's MintCloseAuthority mint extension lets a designated authority close a mint account and reclaim its rent once the mint's total supply reaches zero.

Without this extension, a mint account cannot be closed.

  • The total supply must be zero, so all tokens in all token accounts must be burned before a mint can be closed
  • Only the configured close authority can close the mint

How to Create and Close a Mint

To create and close a mint:

  1. Calculate the mint account size and rent needed for the mint and the MintCloseAuthority extension.
  2. Create the mint account with CreateAccount, initialize MintCloseAuthority, and initialize the mint with InitializeMint.
  3. If the total supply is zero, the mint account can be closed with CloseAccount, signed by the close authority.

Calculate account size

Calculate the mint account size for the base mint plus the MintCloseAuthority extension. This is the size used in CreateAccount.

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]));

Calculate rent

Calculate rent using the size needed for the mint plus the MintCloseAuthority extension.

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]));
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();

Create the mint account

Create the mint account with the calculated space and lamports.

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]));
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
})
]);

Initialize MintCloseAuthority

Initialize the MintCloseAuthority extension on the mint.

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]));
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,
closeAuthority: client.payer.address
})
]);

Initialize the mint

Initialize the mint with InitializeMint in the same 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]));
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,
closeAuthority: client.payer.address
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);

Calculate account size

Calculate the mint account size for the base mint plus the MintCloseAuthority extension. This is the size used in CreateAccount.

Calculate rent

Calculate rent using the size needed for the mint plus the MintCloseAuthority extension.

Create the mint account

Create the mint account with the calculated space and lamports.

Initialize MintCloseAuthority

Initialize the MintCloseAuthority extension on the mint.

Initialize the mint

Initialize the mint with InitializeMint in the same 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]));

Instruction Order

InitializeMintCloseAuthority must come before InitializeMint. CreateAccount, InitializeMintCloseAuthority, and InitializeMint must be included in the same transaction.

Source Reference

ItemDescriptionSource
MintCloseAuthorityMint extension that stores the authority allowed to close a mint once its supply reaches zero.Source
InitializeMintCloseAuthorityInstruction that initializes the mint close authority before InitializeMint.Source
CloseAccountBase token instruction that closes the mint account after the total supply reaches zero.Source
process_initialize_mint_close_authorityProcessor logic that writes the MintCloseAuthority extension onto an uninitialized mint.Source
process_close_accountShared close-account handler that enforces the zero-supply rule for mints with close authority.Source

Typescript

The Kit example below uses the generated instructions directly. Legacy examples using @solana/web3.js and @solana/spl-token are included for reference.

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 of Contents

Edit Page
© 2026 Solana Foundation. All rights reserved.