---
title: Approve Delegate
description:
  Authorize another address to transfer or burn a limited amount of tokens from
  a token account.
url: /docs/tokens/basics/approve-delegate
type: tutorial
prerequisites:
  - /docs/tokens/basics/create-token-account
related:
  - /docs/tokens/basics/revoke-delegate
  - /docs/tokens/basics/transfer-tokens
  - /docs/tokens/basics/burn-tokens
  - /docs/tokens/basics/set-authority
---

## What Does Approving a Delegate Do?

Approving a delegate allows another address to transfer or burn a limited amount
of tokens from a token account on behalf of the token account owner.

A token account stores one current delegate and one delegated amount at a time.
Approving a new delegate replaces the previous delegate and allowance on the
account.

## How to Approve a Delegate

Approving a delegate uses the Token Program's _rs`Approve`_ or
_rs`ApproveChecked`_ instruction.

The examples below use _rs`ApproveChecked`_, which requires the caller to
provide the mint and decimals so the instruction can verify the expected mint
and token precision before processing the approval.

The token account owner signs the approval.

### Source Reference

| Item                  | Description                                                                                                                                                    | Token Program                                                                                                  | Token Extension Program                                                                                              |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| _rs`Account`_         | The token account state stores the owner, delegate, and delegated amount.                                                                                      | [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`Approve`_         | A delegate-approval instruction that records the delegate and allowance for a token account without requiring the caller to provide the mint or decimals.      | [Source](https://github.com/solana-program/token/blob/program%40v9.0.0/interface/src/instruction.rs#L109-L127) | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/instruction.rs#L150-L168) |
| _rs`ApproveChecked`_  | A delegate-approval instruction that requires the caller to provide the mint and decimals and checks those values before recording the delegate and allowance. | [Source](https://github.com/solana-program/token/blob/program%40v9.0.0/interface/src/instruction.rs#L274-L300) | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/instruction.rs#L340-L366) |
| _rs`process_approve`_ | Shared processor logic for delegate approval.                                                                                                                  | [Source](https://github.com/solana-program/token/blob/program%40v9.0.0/program/src/processor.rs#L344-L392)     | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/processor.rs#L582-L640)     |

### 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 delegate = 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 approving a delegate.
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:8)
  .approveChecked({
    source: tokenAccount, // Token account whose delegate approval changes.
    mint: mint.address, // Mint for the token the delegate may spend.
    delegate: delegate.address, // Delegate allowed to spend from the token account.
    owner: client.payer, // Owner approving this delegate change.
    amount: 25n, // Token amount in base units.
    decimals: 2 // Decimals defined on the mint account.
  })
  .sendTransaction();

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

console.log("Mint Address:", mint.address);
console.log("\nToken Account Address:", tokenAccount);
console.log("Token Account:", tokenAccountData.data);
console.log("\nDelegate Address:", delegate.address);
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 {
  fetchToken,
  findAssociatedTokenPda,
  getApproveCheckedInstruction,
  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 delegate = 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 approving a delegate.
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:8)
  getApproveCheckedInstruction({
    source: tokenAccount, // Token account whose delegate approval changes.
    mint: mint.address, // Mint for the token the delegate may spend.
    delegate: delegate.address, // Delegate allowed to spend from the token account.
    owner: client.payer, // Owner approving this delegate change.
    amount: 25n, // Token amount in base units.
    decimals: 2 // Decimals defined on the mint account.
  })
]);

const tokenAccountData = await fetchToken(client.rpc, tokenAccount);

console.log("Mint Address:", mint.address);
console.log("\nToken Account Address:", tokenAccount);
console.log("Token Account:", tokenAccountData.data);
console.log("\nDelegate Address:", delegate.address);
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,
  approveChecked,
  createMint,
  getAccount,
  mintToChecked,
  TOKEN_PROGRAM_ID
} from "@solana/spl-token";

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

const feePayer = Keypair.generate();
const delegate = 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:15)
const result = await approveChecked(
  connection, // Connection to the local validator.
  feePayer, // Account paying transaction fees.
  mintPubkey, // Mint for the token the delegate may spend.
  associatedTokenAccount, // Token account whose delegate approval changes.
  delegate.publicKey, // Delegate allowed to spend from the token account.
  feePayer, // Owner approving this delegate change.
  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 tokenAccountData = await getAccount(
  connection,
  associatedTokenAccount,
  "confirmed",
  TOKEN_PROGRAM_ID
);

console.log("Mint Address:", mintPubkey.toBase58());
console.log(
  "\nAssociated Token Account Address:",
  associatedTokenAccount.toBase58()
);
console.log("Associated Token Account:", tokenAccountData);
console.log("\nDelegate Address:", delegate.publicKey.toBase58());
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,
  createApproveCheckedInstruction,
  createAssociatedTokenAccountInstruction,
  createInitializeMintInstruction,
  createMintToCheckedInstruction,
  getAccount,
  getAssociatedTokenAddressSync,
  getMinimumBalanceForRentExemptMint,
  MINT_SIZE,
  TOKEN_PROGRAM_ID
} from "@solana/spl-token";

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

const feePayer = Keypair.generate();
const delegate = 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 approveBlockhash = await connection.getLatestBlockhash();

const result = await sendAndConfirmTransaction(
  connection,
  new Transaction({
    feePayer: feePayer.publicKey,
    blockhash: approveBlockhash.blockhash,
    lastValidBlockHeight: approveBlockhash.lastValidBlockHeight
  }).add(
    // !mark(1:10)
    createApproveCheckedInstruction(
      associatedTokenAccount, // Token account whose delegate approval changes.
      mint.publicKey, // Mint for the token the delegate may spend.
      delegate.publicKey, // Delegate allowed to spend from the token account.
      feePayer.publicKey, // Owner approving this delegate change.
      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 tokenAccountData = await getAccount(
  connection,
  associatedTokenAccount,
  "confirmed",
  TOKEN_PROGRAM_ID
);

console.log("Mint Address:", mint.publicKey.toBase58());
console.log(
  "\nAssociated Token Account Address:",
  associatedTokenAccount.toBase58()
);
console.log("Associated Token Account:", tokenAccountData);
console.log("\nDelegate Address:", delegate.publicKey.toBase58());
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::{approve_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(),
    );
    let delegate = Keypair::new();

    // !collapse(1:62) collapsed
    // Setup: create a mint and fund the payer's ATA before approving a delegate.
    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 approve_amount = 25;
    let transaction = Transaction::new_signed_with_payer(
        &[
            // !mark(1:10)
            approve_checked(
                &token_program_id(), // Token program to invoke.
                &associated_token_address, // Token account whose delegate approval changes.
                &mint.pubkey(), // Mint for the token the delegate may spend.
                &delegate.pubkey(), // Delegate allowed to spend from the token account.
                &fee_payer.pubkey(), // Owner approving this delegate change.
                &[], // Additional multisig signers.
                approve_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 token_account = client.get_account(&associated_token_address).await?;
    let token_data = Account::unpack(&token_account.data)?;

    println!("Mint Address: {}", mint.pubkey());
    println!(
        "\nAssociated Token Account Address: {}",
        associated_token_address
    );
    println!("Associated Token Account: {:#?}", token_data);
    println!("\nDelegate Address: {}", delegate.pubkey());
    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 (
    approve_checked,
    ApproveCheckedParams,
    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_APPROVE = 25

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

    delegate = Keypair()

    async with rpc:
        # !collapse(1:21) collapsed
        # Setup: create a mint, token account, and initial token balance before approving a delegate.
        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:11)
        approve_delegate_instruction = approve_checked(
            ApproveCheckedParams(
                program_id=TOKEN_PROGRAM_ID,  # Token program to invoke.
                source=token_account_address,  # Token account whose delegate approval changes.
                mint=mint.pubkey(),  # Mint for the token the delegate may spend.
                delegate=delegate.pubkey(),  # Account allowed to spend from the token account.
                owner=fee_payer.pubkey(),  # Account that owns the token account.
                amount=AMOUNT_TO_APPROVE,  # 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([approve_delegate_instruction], fee_payer.pubkey()),
            latest_blockhash.value.blockhash,
        )
        result = await rpc.send_transaction(transaction)

        token_account_info = await token.get_account_info(token_account_address)
        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("\nToken Account Address:", token_account_address)
        print("Token Account:")
        print(json.dumps(token_account, indent=2))
        print("\nDelegate Address:", delegate.pubkey())
        print("\nTransaction Signature:", result.value)

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

</CodeTabs>
