---
title: Burn Tokens
description:
  Burn tokens from a token account and reduce the mint's total supply.
url: /docs/tokens/basics/burn-tokens
type: tutorial
prerequisites:
  - /docs/tokens/basics/create-token-account
  - /docs/tokens/basics/mint-tokens
related:
  - /docs/tokens/basics/transfer-tokens
  - /docs/tokens/basics/approve-delegate
  - /docs/tokens/basics/revoke-delegate
  - /docs/tokens/basics/close-account
---

## What Does Burning Do?

Burning tokens permanently decreases a token account balance and reduces the
mint's total supply by the same amount.

## How to Burn Tokens

Burning tokens uses the Token Program's _rs`Burn`_ or _rs`BurnChecked`_
instruction.

The examples below use _rs`BurnChecked`_, which requires the caller to provide
the mint's decimals so the instruction can verify the expected token precision
before burning.

The [native mint](/docs/tokens/basics/sync-native) does not support burning. Use
_rs`CloseAccount`_ instead.

The token account owner or an approved delegate signs the transaction. In the
Token Extension Program, a mint permanent delegate can also authorize the burn
if the mint has the permanent delegate extension enabled.

### Source Reference

| Item               | Description                                                                                                                                                 | Token Program                                                                                                  | Token Extension Program                                                                                              |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| _rs`Mint`_         | The mint state stores the total token supply.                                                                                                               | [Source](https://github.com/solana-program/token/blob/program%40v9.0.0/interface/src/state.rs#L13-L30)         | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/state.rs#L25-L42)         |
| _rs`Account`_      | The token account state stores the token balance reduced by a burn.                                                                                         | [Source](https://github.com/solana-program/token/blob/program%40v9.0.0/interface/src/state.rs#L84-L108)        | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/state.rs#L99-L123)        |
| _rs`Burn`_         | A burn instruction that decreases a token account balance and mint supply without requiring the caller to provide the mint's decimals.                      | [Source](https://github.com/solana-program/token/blob/program%40v9.0.0/interface/src/instruction.rs#L178-L196) | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/instruction.rs#L221-L239) |
| _rs`BurnChecked`_  | A burn instruction that requires the caller to provide the mint's decimals and checks that value before decreasing a token account balance and mint supply. | [Source](https://github.com/solana-program/token/blob/program%40v9.0.0/interface/src/instruction.rs#L326-L350) | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/instruction.rs#L393-L417) |
| _rs`process_burn`_ | Shared processor logic for token burns.                                                                                                                     | [Source](https://github.com/solana-program/token/blob/program%40v9.0.0/program/src/processor.rs#L586-L668)     | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/processor.rs#L1070-L1194)   |

### Typescript

The `Kit` examples below show the recommended approach using `@solana/kit`.
Legacy examples using `@solana/web3.js` are included for reference.

#### Kit

<CodeTabs storage="token-ts-kit" flags="r">

```ts !! title="Plugin"
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";
import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";
import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";
import {
  findAssociatedTokenPda,
  tokenProgram,
  TOKEN_PROGRAM_ADDRESS
} from "@solana-program/token";

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)))
  // !mark[/\.use\(tokenProgram\(\)\)/]
  .use(tokenProgram());

const mint = await generateKeyPairSigner();

const [tokenAccount] = await findAssociatedTokenPda({
  mint: mint.address,
  owner: client.payer.address,
  tokenProgram: TOKEN_PROGRAM_ADDRESS
});

// !collapse(1:16) collapsed
// Setup: create a mint and fund the payer's ATA before burning tokens.
await client.sendTransaction([
  client.token.instructions.createMint({
    newMint: mint,
    decimals: 2,
    mintAuthority: client.payer.address,
    freezeAuthority: client.payer.address
  }),
  await client.token.instructions.mintToATA({
    mint: mint.address,
    owner: client.payer.address,
    mintAuthority: client.payer,
    amount: 100n,
    decimals: 2
  })
]);

const result = await client.token.instructions
  // !mark(1:7)
  .burnChecked({
    account: tokenAccount, // Token account holding the tokens to burn.
    mint: mint.address, // Mint for the token being burned.
    authority: client.payer, // Owner or delegate approving the burn.
    amount: 25n, // Token amount in base units.
    decimals: 2 // Decimals defined on the mint account.
  })
  .sendTransaction();

const mintAccount = await client.token.accounts.mint.fetch(mint.address);
const tokenAccountData = await client.token.accounts.token.fetch(tokenAccount);

console.log("Mint Address:", mint.address);
console.log("Mint Account:", mintAccount.data);
console.log("\nToken Account Address:", tokenAccount);
console.log("Token Account:", tokenAccountData.data);
console.log("\nTransaction Signature:", result.context.signature);
```

```ts !! title="Instructions"
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";
import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";
import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";
import {
  fetchMint,
  fetchToken,
  findAssociatedTokenPda,
  getBurnCheckedInstruction,
  getCreateMintInstructionPlan,
  getMintToATAInstructionPlanAsync,
  TOKEN_PROGRAM_ADDRESS
} from "@solana-program/token";

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 [tokenAccount] = await findAssociatedTokenPda({
  mint: mint.address,
  owner: client.payer.address,
  tokenProgram: TOKEN_PROGRAM_ADDRESS
});

// !collapse(1:18) collapsed
// Setup: create a mint and fund the payer's ATA before burning tokens.
await client.sendTransaction([
  getCreateMintInstructionPlan({
    payer: client.payer,
    newMint: mint,
    decimals: 2,
    mintAuthority: client.payer.address,
    freezeAuthority: client.payer.address
  }),
  await getMintToATAInstructionPlanAsync({
    payer: client.payer,
    mint: mint.address,
    owner: client.payer.address,
    mintAuthority: client.payer,
    amount: 100n,
    decimals: 2
  })
]);

const result = await client.sendTransaction([
  // !mark(1:7)
  getBurnCheckedInstruction({
    account: tokenAccount, // Token account holding the tokens to burn.
    mint: mint.address, // Mint for the token being burned.
    authority: client.payer, // Owner or delegate approving the burn.
    amount: 25n, // Token amount in base units.
    decimals: 2 // Decimals defined on the mint account.
  })
]);

const mintAccount = await fetchMint(client.rpc, mint.address);
const tokenAccountData = await fetchToken(client.rpc, tokenAccount);

console.log("Mint Address:", mint.address);
console.log("Mint Account:", mintAccount.data);
console.log("\nToken Account Address:", tokenAccount);
console.log("Token Account:", tokenAccountData.data);
console.log("\nTransaction Signature:", result.context.signature);
```

</CodeTabs>

#### Web3.js

<CodeTabs storage="token-ts-legacy" flags="r">

```ts !! title="Helper Function"
import { Connection, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";
import {
  createAssociatedTokenAccount,
  burnChecked,
  createMint,
  getAccount,
  getMint,
  mintToChecked,
  TOKEN_PROGRAM_ID
} from "@solana/spl-token";

// !collapse(1:54) collapsed
// Setup: create a mint and fund the payer's ATA before burning tokens.
const connection = new Connection("http://localhost:8899", "confirmed");
const latestBlockhash = await connection.getLatestBlockhash();

const feePayer = Keypair.generate();

const airdropSignature = await connection.requestAirdrop(
  feePayer.publicKey,
  LAMPORTS_PER_SOL
);
await connection.confirmTransaction({
  blockhash: latestBlockhash.blockhash,
  lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
  signature: airdropSignature
});

const mintPubkey = await createMint(
  connection,
  feePayer,
  feePayer.publicKey,
  feePayer.publicKey,
  2,
  Keypair.generate(),
  {
    commitment: "confirmed"
  },
  TOKEN_PROGRAM_ID
);

const associatedTokenAccount = await createAssociatedTokenAccount(
  connection,
  feePayer,
  mintPubkey,
  feePayer.publicKey,
  {
    commitment: "confirmed"
  },
  TOKEN_PROGRAM_ID
);

await mintToChecked(
  connection,
  feePayer,
  mintPubkey,
  associatedTokenAccount,
  feePayer,
  100,
  2,
  [],
  {
    commitment: "confirmed"
  },
  TOKEN_PROGRAM_ID
);

// !mark(1:14)
const result = await burnChecked(
  connection, // Connection to the local validator.
  feePayer, // Account paying transaction fees.
  associatedTokenAccount, // Token account holding the tokens to burn.
  mintPubkey, // Mint for the token being burned.
  feePayer, // Owner or delegate approving the burn.
  25, // Token amount in base units.
  2, // Decimals defined on the mint account.
  [], // Additional multisig signers.
  {
    commitment: "confirmed" // Confirmation options for the transaction.
  },
  TOKEN_PROGRAM_ID // Token program to invoke.
);

const mintAccount = await getMint(
  connection,
  mintPubkey,
  "confirmed",
  TOKEN_PROGRAM_ID
);
const tokenAccountData = await getAccount(
  connection,
  associatedTokenAccount,
  "confirmed",
  TOKEN_PROGRAM_ID
);

console.log("Mint Address:", mintPubkey.toBase58());
console.log("Mint Account:", mintAccount);
console.log(
  "\nAssociated Token Account Address:",
  associatedTokenAccount.toBase58()
);
console.log("Associated Token Account:", tokenAccountData);
console.log("\nTransaction Signature:", result);
```

```ts !! title="Instructions"
import {
  Connection,
  Keypair,
  sendAndConfirmTransaction,
  SystemProgram,
  Transaction,
  LAMPORTS_PER_SOL
} from "@solana/web3.js";
import {
  ASSOCIATED_TOKEN_PROGRAM_ID,
  createBurnCheckedInstruction,
  createAssociatedTokenAccountInstruction,
  createInitializeMintInstruction,
  createMintToCheckedInstruction,
  getAccount,
  getAssociatedTokenAddressSync,
  getMinimumBalanceForRentExemptMint,
  getMint,
  MINT_SIZE,
  TOKEN_PROGRAM_ID
} from "@solana/spl-token";

// !collapse(1:69) collapsed
// Setup: create a mint and fund the payer's ATA before burning tokens.
const connection = new Connection("http://localhost:8899", "confirmed");
const latestBlockhash = await connection.getLatestBlockhash();

const feePayer = Keypair.generate();

const airdropSignature = await connection.requestAirdrop(
  feePayer.publicKey,
  LAMPORTS_PER_SOL
);
await connection.confirmTransaction({
  blockhash: latestBlockhash.blockhash,
  lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
  signature: airdropSignature
});

const mint = Keypair.generate();
const mintRent = await getMinimumBalanceForRentExemptMint(connection);
const associatedTokenAccount = getAssociatedTokenAddressSync(
  mint.publicKey,
  feePayer.publicKey,
  false, // allowOwnerOffCurve
  TOKEN_PROGRAM_ID,
  ASSOCIATED_TOKEN_PROGRAM_ID
);

await sendAndConfirmTransaction(
  connection,
  new Transaction({
    feePayer: feePayer.publicKey,
    blockhash: latestBlockhash.blockhash,
    lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
  }).add(
    SystemProgram.createAccount({
      fromPubkey: feePayer.publicKey,
      newAccountPubkey: mint.publicKey,
      space: MINT_SIZE,
      lamports: mintRent,
      programId: TOKEN_PROGRAM_ID
    }),
    createInitializeMintInstruction(
      mint.publicKey,
      2,
      feePayer.publicKey,
      feePayer.publicKey,
      TOKEN_PROGRAM_ID
    ),
    createAssociatedTokenAccountInstruction(
      feePayer.publicKey,
      associatedTokenAccount,
      feePayer.publicKey,
      mint.publicKey,
      TOKEN_PROGRAM_ID,
      ASSOCIATED_TOKEN_PROGRAM_ID
    ),
    createMintToCheckedInstruction(
      mint.publicKey,
      associatedTokenAccount,
      feePayer.publicKey,
      100,
      2,
      [],
      TOKEN_PROGRAM_ID
    )
  ),
  [feePayer, mint]
);

const burnBlockhash = await connection.getLatestBlockhash();

const result = await sendAndConfirmTransaction(
  connection,
  new Transaction({
    feePayer: feePayer.publicKey,
    blockhash: burnBlockhash.blockhash,
    lastValidBlockHeight: burnBlockhash.lastValidBlockHeight
  }).add(
    // !mark(1:9)
    createBurnCheckedInstruction(
      associatedTokenAccount, // Token account holding the tokens to burn.
      mint.publicKey, // Mint for the token being burned.
      feePayer.publicKey, // Owner or delegate approving the burn.
      25, // Token amount in base units.
      2, // Decimals defined on the mint account.
      [], // Additional multisig signers.
      TOKEN_PROGRAM_ID // Token program to invoke.
    )
  ),
  [feePayer]
);

const mintAccount = await getMint(
  connection,
  mint.publicKey,
  "confirmed",
  TOKEN_PROGRAM_ID
);
const tokenAccountData = await getAccount(
  connection,
  associatedTokenAccount,
  "confirmed",
  TOKEN_PROGRAM_ID
);

console.log("Mint Address:", mint.publicKey.toBase58());
console.log("Mint Account:", mintAccount);
console.log(
  "\nAssociated Token Account Address:",
  associatedTokenAccount.toBase58()
);
console.log("Associated Token Account:", tokenAccountData);
console.log("\nTransaction Signature:", result);
```

</CodeTabs>

### Rust

<CodeTabs storage="token-rs" flags="r">

```rust !! title="Rust"
use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{
    program_pack::Pack,
    signature::{Keypair, Signer},
    transaction::Transaction,
};
use solana_system_interface::instruction::create_account;
use spl_associated_token_account_interface::{
    address::get_associated_token_address, instruction::create_associated_token_account,
};
use spl_token_interface::{
    id as token_program_id,
    instruction::{burn_checked, initialize_mint, mint_to_checked},
    state::{Account, Mint},
};

#[tokio::main]
async fn main() -> Result<()> {
    let client = RpcClient::new_with_commitment(
        String::from("http://localhost:8899"),
        CommitmentConfig::confirmed(),
    );

    // !collapse(1:62) collapsed
    // Setup: create a mint and fund the payer's ATA before burning tokens.
    let fee_payer = Keypair::new();
    let decimals = 2;

    let airdrop_signature = client
        .request_airdrop(&fee_payer.pubkey(), 1_000_000_000)
        .await?;
    loop {
        let confirmed = client.confirm_transaction(&airdrop_signature).await?;
        if confirmed {
            break;
        }
    }

    let mint = Keypair::new();
    let mint_rent = client
        .get_minimum_balance_for_rent_exemption(Mint::LEN)
        .await?;
    let associated_token_address = get_associated_token_address(
        &fee_payer.pubkey(),
        &mint.pubkey(),
    );
    let latest_blockhash = client.get_latest_blockhash().await?;
    let setup_transaction = Transaction::new_signed_with_payer(
        &[
            create_account(
                &fee_payer.pubkey(),
                &mint.pubkey(),
                mint_rent,
                Mint::LEN as u64,
                &token_program_id(),
            ),
            initialize_mint(
                &token_program_id(),
                &mint.pubkey(),
                &fee_payer.pubkey(),
                Some(&fee_payer.pubkey()),
                decimals,
            )?,
            create_associated_token_account(
                &fee_payer.pubkey(),
                &fee_payer.pubkey(),
                &mint.pubkey(),
                &token_program_id(),
            ),
            mint_to_checked(
                &token_program_id(),
                &mint.pubkey(),
                &associated_token_address,
                &fee_payer.pubkey(),
                &[],
                100,
                decimals,
            )?,
        ],
        Some(&fee_payer.pubkey()),
        &[&fee_payer, &mint],
        latest_blockhash,
    );
    client
        .send_and_confirm_transaction(&setup_transaction)
        .await?;

    let burn_amount = 25;
    let transaction = Transaction::new_signed_with_payer(
        &[
    // !mark(1:9)
            burn_checked(
                &token_program_id(), // Token program to invoke.
                &associated_token_address, // Token account holding the tokens to burn.
                &mint.pubkey(), // Mint for the token being burned.
                &fee_payer.pubkey(), // Owner or delegate approving the burn.
                &[], // Additional multisig signers.
                burn_amount, // Token amount in base units.
                decimals, // Decimals defined on the mint account.
            )?,
        ],
        Some(&fee_payer.pubkey()),
        &[&fee_payer],
        latest_blockhash,
    );

    let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;
    let mint_account = client.get_account(&mint.pubkey()).await?;
    let mint_data = Mint::unpack(&mint_account.data)?;
    let token_account = client.get_account(&associated_token_address).await?;
    let token_data = Account::unpack(&token_account.data)?;

    println!("Mint Address: {}", mint.pubkey());
    println!("Mint Account: {:#?}", mint_data);
    println!(
        "\nAssociated Token Account Address: {}",
        associated_token_address
    );
    println!("Associated Token Account: {:#?}", token_data);
    println!("\nTransaction Signature: {}", transaction_signature);

    Ok(())
}
```

</CodeTabs>

### Python

<CodeTabs flags="r">

```py !! title="Python"
#!/usr/bin/env python3

import asyncio
import json
from solana.rpc.async_api import AsyncClient
from solders.keypair import Keypair
from solders.message import Message
from solders.pubkey import Pubkey
from solders.system_program import create_account, CreateAccountParams
from solders.transaction import Transaction
from spl.token.async_client import AsyncToken
from spl.token.instructions import (
    burn_checked,
    BurnCheckedParams,
    create_associated_token_account,
    get_associated_token_address,
    initialize_mint,
    InitializeMintParams,
    mint_to_checked,
    MintToCheckedParams,
)
from spl.token.constants import MINT_LEN, TOKEN_PROGRAM_ID

DECIMALS = 2
AMOUNT_TO_MINT = 100
AMOUNT_TO_BURN = 25

async def main():
    rpc = AsyncClient("http://localhost:8899")

    async with rpc:
        # !collapse(1:21) collapsed
        # Setup: create a mint, token account, and initial token balance before burning tokens.
        fee_payer = Keypair()
        airdrop_signature = (await rpc.request_airdrop(fee_payer.pubkey(), 1_000_000_000)).value
        await rpc.confirm_transaction(airdrop_signature)
        mint = Keypair()
        token_account_address = get_associated_token_address(fee_payer.pubkey(), mint.pubkey())
        mint_rent = (await rpc.get_minimum_balance_for_rent_exemption(MINT_LEN)).value
        setup_instructions = [
            create_account(CreateAccountParams(from_pubkey=fee_payer.pubkey(), to_pubkey=mint.pubkey(), lamports=mint_rent, space=MINT_LEN, owner=TOKEN_PROGRAM_ID)),
            initialize_mint(InitializeMintParams(decimals=DECIMALS, program_id=TOKEN_PROGRAM_ID, mint=mint.pubkey(), mint_authority=fee_payer.pubkey())),
            create_associated_token_account(fee_payer.pubkey(), fee_payer.pubkey(), mint.pubkey(), TOKEN_PROGRAM_ID),
            mint_to_checked(MintToCheckedParams(program_id=TOKEN_PROGRAM_ID, mint=mint.pubkey(), dest=token_account_address, mint_authority=fee_payer.pubkey(), amount=AMOUNT_TO_MINT, decimals=DECIMALS)),
        ]
        setup_blockhash = await rpc.get_latest_blockhash()
        setup_transaction = Transaction(
            [fee_payer, mint],
            Message(setup_instructions, fee_payer.pubkey()),
            setup_blockhash.value.blockhash,
        )
        await rpc.send_transaction(setup_transaction)
        token = AsyncToken(rpc, mint.pubkey(), TOKEN_PROGRAM_ID, fee_payer)

        # !mark(1:10)
        burn_tokens_instruction = burn_checked(
            BurnCheckedParams(
                program_id=TOKEN_PROGRAM_ID,  # Token program to invoke.
                mint=mint.pubkey(),  # Mint for the token being burned.
                account=token_account_address,  # Token account whose balance decreases.
                owner=fee_payer.pubkey(),  # Account that owns the token account and signs the transaction.
                amount=AMOUNT_TO_BURN,  # Token amount in base units.
                decimals=DECIMALS,  # Decimals defined on the mint account.
            )
        )
        latest_blockhash = await rpc.get_latest_blockhash()
        transaction = Transaction(
            [fee_payer],
            Message([burn_tokens_instruction], fee_payer.pubkey()),
            latest_blockhash.value.blockhash,
        )
        result = await rpc.send_transaction(transaction)

        mint_info = await token.get_mint_info()
        token_account_info = await token.get_account_info(token_account_address)
        mint_account = {
            key: str(value) if isinstance(value, Pubkey) else value
            for key, value in mint_info._asdict().items()
        }
        token_account = {
            key: str(value) if isinstance(value, Pubkey) else value
            for key, value in token_account_info._asdict().items()
        }

        print("Mint Address:", mint.pubkey())
        print("Mint Account:")
        print(json.dumps(mint_account, indent=2))
        print("\nToken Account Address:", token_account_address)
        print("Token Account:")
        print(json.dumps(token_account, indent=2))
        print("\nTransaction Signature:", result.value)

if __name__ == "__main__":
    asyncio.run(main())
```

</CodeTabs>
