---
title: Interest Bearing Tokens
description:
  Create a Token Extension Program mint with an onchain interest rate, update
  that rate, and convert token amounts into a UI amount with interest.
url: /docs/tokens/extensions/interest-bearing-tokens
type: tutorial
prerequisites:
  - /docs/tokens/basics/create-mint
  - /docs/tokens/basics/mint-tokens
related:
  - /docs/tokens/extensions/scaled-ui-amount
  - /docs/tokens/extensions
---

## What Is an Interest Bearing Mint?

The Token Extension Program's _rs`InterestBearingConfig`_ mint extension lets a
mint store an annualized interest rate directly onchain.

Interest bearing tokens do **not** add more tokens to token accounts over time.
The token amount stored in each token account stays the same until a token
program instruction changes it, such as minting, transferring, or burning.

As time passes, the calculated UI amount with interest can increase even though
the token amount and token supply stay the same.

## How Historical Rate Changes Are Calculated

- [`InterestBearingConfig`](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/interest_bearing_mint/mod.rs#L40-L50)
  stores `initialization_timestamp`, `pre_update_average_rate`,
  `last_update_timestamp`, and `current_rate`.
- [`amount_to_ui_amount`](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/interest_bearing_mint/mod.rs#L85-L94)
  calculates the UI amount in two steps: it first calculates interest from
  initialization to `last_update_timestamp` using `pre_update_average_rate`,
  then calculates interest from `last_update_timestamp` to the current timestamp
  using `current_rate`.
- When
  [`UpdateRate`](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/interest_bearing_mint/instruction.rs#L35-L113)
  runs,
  [`process_update_rate`](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/interest_bearing_mint/processor.rs#L50-L84)
  calls
  [`time_weighted_average_rate`](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/interest_bearing_mint/mod.rs#L121-L149)
  and updates `pre_update_average_rate` to a new time-weighted historical
  average, then records a new `last_update_timestamp` and `current_rate`.
- After rate changes over time, the earlier changes are represented by
  `pre_update_average_rate`, while the period from `last_update_timestamp` to
  the current time is calculated with `current_rate`.

## How to Create and Use Interest Bearing Tokens

To create and use interest bearing tokens:

1. Calculate the mint account size and rent needed for the mint and the
   _rs`InterestBearingConfig`_ extension.
2. Create the mint account with _rs`CreateAccount`_, initialize
   _rs`InterestBearingConfig`_, and initialize the mint with
   _rs`InitializeMint`_.
3. Mint tokens as usual.
4. Use _rs`UpdateRate`_ to change the mint's current rate over time.
5. Convert token amounts into a UI amount with interest with
   _rs`AmountToUiAmount`_ or helper methods that fetch the mint account and
   clock sysvar, then calculate the UI amount without sending a transaction.

<Callout type="info" title="Offchain and Onchain UI Amount Conversion">
  The offchain helper path fetches the mint account and the clock sysvar, then
  calculates the UI amount locally without sending a transaction. The onchain
  path uses _rs`AmountToUiAmount`_, which runs in the token program and returns
  the UI amount in transaction return data. _rs`AmountToUiAmount`_ can be sent
  in a transaction or simulated.
</Callout>

<ScrollyCoding>

## !!steps Calculate account size

Calculate the mint account size for the base mint plus the
_rs`InterestBearingConfig`_ extension. This is the size used in
_rs`CreateAccount`_.

<CodePlaceholder title="Example" />

```ts !! title="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();

// !focus(1:7)
const interestBearingExtension = extension("InterestBearingConfig", {
  rateAuthority: client.payer.address,
  initializationTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  lastUpdateTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  preUpdateAverageRate: 30000,
  currentRate: 30000
});

// !focus(1:1)
const mintSpace = BigInt(getMintSize([interestBearingExtension]));
```

## !!steps Calculate rent

Calculate rent using the size needed for the mint plus the
_rs`InterestBearingConfig`_ extension.

<CodePlaceholder title="Example" />

```ts !! title="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 interestBearingExtension = extension("InterestBearingConfig", {
  rateAuthority: client.payer.address,
  initializationTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  lastUpdateTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  preUpdateAverageRate: 30000,
  currentRate: 30000
});

const mintSpace = BigInt(getMintSize([interestBearingExtension]));

// !focus(1:3)
const mintRent = await client.rpc
  .getMinimumBalanceForRentExemption(mintSpace)
  .send();
```

## !!steps Create the mint account

Create the mint account with the calculated space and lamports.

<CodePlaceholder title="Example" />

```ts !! title="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 interestBearingExtension = extension("InterestBearingConfig", {
  rateAuthority: client.payer.address,
  initializationTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  lastUpdateTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  preUpdateAverageRate: 30000,
  currentRate: 30000
});

const mintSpace = BigInt(getMintSize([interestBearingExtension]));

const mintRent = await client.rpc
  .getMinimumBalanceForRentExemption(mintSpace)
  .send();

// !focus(1:9)
await client.sendTransaction([
  getCreateAccountInstruction({
    payer: client.payer,
    newAccount: mint,
    lamports: mintRent,
    space: mintSpace,
    programAddress: TOKEN_2022_PROGRAM_ADDRESS
  })
]);
```

## !!steps Initialize InterestBearingConfig

Initialize the _rs`InterestBearingConfig`_ extension on the mint.

<CodePlaceholder title="Example" />

```ts !! title="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 interestBearingExtension = extension("InterestBearingConfig", {
  rateAuthority: client.payer.address,
  initializationTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  lastUpdateTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  preUpdateAverageRate: 30000,
  currentRate: 30000
});

const mintSpace = BigInt(getMintSize([interestBearingExtension]));

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
  }),
  // !focus(1:5)
  getInitializeInterestBearingMintInstruction({
    mint: mint.address,
    rateAuthority: client.payer.address,
    rate: 30000
  })
]);
```

## !!steps Initialize the mint

Initialize the mint with _rs`InitializeMint`_ in the same transaction.

<CodePlaceholder title="Example" />

```ts !! title="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 interestBearingExtension = extension("InterestBearingConfig", {
  rateAuthority: client.payer.address,
  initializationTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  lastUpdateTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  preUpdateAverageRate: 30000,
  currentRate: 30000
});

const mintSpace = BigInt(getMintSize([interestBearingExtension]));

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
  }),
  getInitializeInterestBearingMintInstruction({
    mint: mint.address,
    rateAuthority: client.payer.address,
    rate: 30000
  }),
  // !focus(1:6)
  getInitializeMintInstruction({
    mint: mint.address,
    decimals: 0,
    mintAuthority: client.payer.address,
    freezeAuthority: client.payer.address
  })
]);
```

## !!steps Create a token account and mint tokens

Create a token account for the mint, then mint tokens to that token account.

<CodePlaceholder title="Example" />

```ts !! title="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 interestBearingExtension = extension("InterestBearingConfig", {
  rateAuthority: client.payer.address,
  initializationTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  lastUpdateTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  preUpdateAverageRate: 30000,
  currentRate: 30000
});

const mintSpace = BigInt(getMintSize([interestBearingExtension]));

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
  }),
  getInitializeInterestBearingMintInstruction({
    mint: mint.address,
    rateAuthority: client.payer.address,
    rate: 30000
  }),
  getInitializeMintInstruction({
    mint: mint.address,
    decimals: 0,
    mintAuthority: client.payer.address,
    freezeAuthority: client.payer.address
  })
]);

// !focus(1:21)
const recipient = await generateKeyPairSigner();
const tokenAmount = 1_000_000_000_000n;
const [tokenAccount] = await findAssociatedTokenPda({
  mint: mint.address,
  owner: recipient.address,
  tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
await client.sendTransaction([
  await getCreateAssociatedTokenInstructionAsync({
    payer: client.payer,
    mint: mint.address,
    owner: recipient.address
  }),
  getMintToCheckedInstruction({
    mint: mint.address,
    token: tokenAccount,
    mintAuthority: client.payer,
    amount: tokenAmount,
    decimals: 0
  })
]);
```

## !!steps Calculate the UI amount with the offchain helper

Fetch the mint account and clock sysvar, then calculate the UI amount without
sending a transaction.

<CodePlaceholder title="Example" />

```ts !! title="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 interestBearingExtension = extension("InterestBearingConfig", {
  rateAuthority: client.payer.address,
  initializationTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  lastUpdateTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  preUpdateAverageRate: 30000,
  currentRate: 30000
});

const mintSpace = BigInt(getMintSize([interestBearingExtension]));

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
  }),
  getInitializeInterestBearingMintInstruction({
    mint: mint.address,
    rateAuthority: client.payer.address,
    rate: 30000
  }),
  getInitializeMintInstruction({
    mint: mint.address,
    decimals: 0,
    mintAuthority: client.payer.address,
    freezeAuthority: client.payer.address
  })
]);

const recipient = await generateKeyPairSigner();
const tokenAmount = 1_000_000_000_000n;
const [tokenAccount] = await findAssociatedTokenPda({
  mint: mint.address,
  owner: recipient.address,
  tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
await client.sendTransaction([
  await getCreateAssociatedTokenInstructionAsync({
    payer: client.payer,
    mint: mint.address,
    owner: recipient.address
  }),
  getMintToCheckedInstruction({
    mint: mint.address,
    token: tokenAccount,
    mintAuthority: client.payer,
    amount: tokenAmount,
    decimals: 0
  })
]);

await new Promise((resolve) => setTimeout(resolve, 2_000));

// !focus(1:5)
const offchainUiAmount = await amountToUiAmountForMintWithoutSimulation(
  client.rpc,
  mint.address,
  tokenAmount
);
```

</ScrollyCoding>

<Callout type="info" title="Instruction Order">
  _rs`InterestBearingMintInstruction::Initialize`_ must come before
  _rs`InitializeMint`_. _rs`CreateAccount`_,
  _rs`InterestBearingMintInstruction::Initialize`_, and _rs`InitializeMint`_
  must be included in the same transaction.
</Callout>

### Source Reference

| Item                                             | Description                                                                                                                   | Source                                                                                                                                              |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| _rs`InterestBearingConfig`_                      | Mint extension that stores the interest rate authority, timestamps, and current and historical rates.                         | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/interest_bearing_mint/mod.rs#L40-L153)         |
| _rs`InterestBearingMintInstruction::Initialize`_ | Instruction that initializes the interest bearing config before _rs`InitializeMint`_.                                         | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/interest_bearing_mint/instruction.rs#L17-L90)  |
| _rs`InterestBearingMintInstruction::UpdateRate`_ | Instruction that changes the mint's current interest rate.                                                                    | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/interest_bearing_mint/instruction.rs#L35-L113) |
| _rs`AmountToUiAmount`_                           | Instruction that returns the current UI amount string for a token amount using the mint's active config.                      | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/instruction.rs#L520-L538)                                |
| _rs`InterestBearingConfig::amount_to_ui_amount`_ | Helper that converts a token amount into a UI amount with interest for a timestamp.                                           | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/interest_bearing_mint/mod.rs#L85-L96)          |
| _rs`process_initialize`_                         | Processor logic that initializes _rs`InterestBearingConfig`_ and records the initial timestamps and rate.                     | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/interest_bearing_mint/processor.rs#L21-L43)      |
| _rs`process_update_rate`_                        | Processor logic that updates the current rate, recalculates the time-weighted average rate, and records the update timestamp. | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/interest_bearing_mint/processor.rs#L46-L77)      |
| _rs`process_amount_to_ui_amount`_                | Processor logic that returns the UI amount string using the mint's active interest bearing configuration.                     | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/processor.rs#L1548-L1569)                                  |

### 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

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

```ts !! title="Instructions"
import {
  lamports,
  createClient,
  appendTransactionMessageInstructions,
  createTransactionMessage,
  generateKeyPairSigner,
  getBase64EncodedWireTransaction,
  pipe,
  setTransactionMessageFeePayerSigner,
  setTransactionMessageLifetimeUsingBlockhash,
  signTransactionMessageWithSigners,
  unwrapOption
} 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 {
  amountToUiAmountForMintWithoutSimulation,
  extension,
  fetchMint,
  findAssociatedTokenPda,
  getAmountToUiAmountInstruction,
  getCreateAssociatedTokenInstructionAsync,
  getInitializeInterestBearingMintInstruction,
  getInitializeMintInstruction,
  getMintSize,
  getMintToCheckedInstruction,
  getUpdateRateInterestBearingMintInstruction,
  isExtension,
  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 recipient = await generateKeyPairSigner();
const tokenAmount = 1_000_000_000_000n;

const interestBearingExtension = extension("InterestBearingConfig", {
  rateAuthority: client.payer.address,
  initializationTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  lastUpdateTimestamp: BigInt(Math.floor(Date.now() / 1000)),
  preUpdateAverageRate: 30000,
  currentRate: 30000
});
const mintSpace = BigInt(getMintSize([interestBearingExtension]));
const mintRent = await client.rpc
  .getMinimumBalanceForRentExemption(mintSpace)
  .send();

const [tokenAccount] = await findAssociatedTokenPda({
  mint: mint.address,
  owner: recipient.address,
  tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});

await client.sendTransaction([
  getCreateAccountInstruction({
    payer: client.payer, // Account funding the new mint account.
    newAccount: mint, // New mint account to create.
    lamports: mintRent, // Lamports funding the mint account rent.
    space: mintSpace, // Account size in bytes for the mint plus InterestBearingConfig.
    programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.
  }),
  // !mark(1:5)
  getInitializeInterestBearingMintInstruction({
    mint: mint.address, // Mint account that stores the InterestBearingConfig extension.
    rateAuthority: client.payer.address, // Authority allowed to update the interest rate later.
    rate: 30000 // Interest rate in basis points.
  }),
  getInitializeMintInstruction({
    mint: mint.address, // Mint account to initialize.
    decimals: 0, // Number of decimals for the token.
    mintAuthority: client.payer.address, // Authority allowed to mint new tokens.
    freezeAuthority: client.payer.address // Authority allowed to freeze token accounts.
  })
]);

await client.sendTransaction([
  await getCreateAssociatedTokenInstructionAsync({
    payer: client.payer, // Account funding the associated token account creation.
    mint: mint.address, // Mint for the associated token account.
    owner: recipient.address // Owner of the token account.
  }),
  getMintToCheckedInstruction({
    mint: mint.address, // Mint account that issues the tokens.
    token: tokenAccount, // Token account receiving the newly minted tokens.
    mintAuthority: client.payer, // Signer authorized to mint new tokens.
    amount: tokenAmount, // Token amount in base units.
    decimals: 0 // Decimals defined on the mint.
  })
]);

await new Promise((resolve) => setTimeout(resolve, 2_000));

const calculatedUiAmount = await amountToUiAmountForMintWithoutSimulation(
  client.rpc,
  mint.address,
  tokenAmount
);

// !mark(1:4)
const amountToUiInstruction = getAmountToUiAmountInstruction({
  mint: mint.address, // Mint whose UI amount conversion is being simulated.
  amount: tokenAmount // Token amount in base units.
});

const { value: latestBlockhash } = await client.rpc.getLatestBlockhash().send();
const amountToUiMessage = pipe(
  createTransactionMessage({ version: 0 }),
  (tx) => setTransactionMessageFeePayerSigner(client.payer, tx),
  (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
  (tx) => appendTransactionMessageInstructions([amountToUiInstruction], tx)
);
const signedAmountToUiMessage =
  await signTransactionMessageWithSigners(amountToUiMessage);
const simulation = await client.rpc
  .simulateTransaction(
    getBase64EncodedWireTransaction(signedAmountToUiMessage),
    {
      encoding: "base64"
    }
  )
  .send();

const simulatedUiAmount = Buffer.from(
  simulation.value.returnData?.data?.[0] ?? "",
  "base64"
).toString("utf8");

// !mark(1:5)
const updateRateInstruction = getUpdateRateInterestBearingMintInstruction({
  mint: mint.address, // Mint account that stores the InterestBearingConfig extension.
  rateAuthority: client.payer, // Signer authorized to update the interest rate.
  rate: 15000 // New interest rate in basis points.
});

await client.sendTransaction([updateRateInstruction]);

const mintAccount = await fetchMint(client.rpc, mint.address);
const interestBearingConfig = (
  unwrapOption(mintAccount.data.extensions) ?? []
).find((item) => isExtension("InterestBearingConfig", item));

console.log("Mint Address:", mint.address);
console.log("Token Account:", tokenAccount);
console.log("Calculated UI Amount:", calculatedUiAmount);
console.log("Simulated UI Amount:", simulatedUiAmount);
console.log("InterestBearingConfig:", interestBearingConfig);
```

</CodeTabs>

#### Web3.js

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

```ts !! title="Instructions"
import {
  Connection,
  Keypair,
  sendAndConfirmTransaction,
  SystemProgram,
  Transaction,
  LAMPORTS_PER_SOL
} from "@solana/web3.js";
import {
  ASSOCIATED_TOKEN_PROGRAM_ID,
  amountToUiAmountForMintWithoutSimulation,
  createAmountToUiAmountInstruction,
  createAssociatedTokenAccountInstruction,
  createInitializeMintInstruction,
  createInitializeInterestBearingMintInstruction,
  createMintToCheckedInstruction,
  createUpdateRateInterestBearingMintInstruction,
  ExtensionType,
  getAssociatedTokenAddressSync,
  getInterestBearingMintConfigState,
  getMint,
  getMintLen,
  TOKEN_2022_PROGRAM_ID
} from "@solana/spl-token";

const connection = new Connection("http://localhost:8899", "confirmed");
const latestBlockhash = await connection.getLatestBlockhash();

const feePayer = Keypair.generate();
const recipient = Keypair.generate();
const tokenAmount = 1_000_000_000_000n;

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

const extensions = [ExtensionType.InterestBearingConfig];

const mint = Keypair.generate();

const mintLength = getMintLen(extensions);

const mintRent = await connection.getMinimumBalanceForRentExemption(mintLength);

const tokenAccount = getAssociatedTokenAddressSync(
  mint.publicKey,
  recipient.publicKey,
  false,
  TOKEN_2022_PROGRAM_ID,
  ASSOCIATED_TOKEN_PROGRAM_ID
);

const createMintAccountInstruction = SystemProgram.createAccount({
  fromPubkey: feePayer.publicKey, // Account funding the new mint account.
  newAccountPubkey: mint.publicKey, // New mint account to create.
  space: mintLength, // Account size in bytes for the mint plus InterestBearingConfig.
  lamports: mintRent, // Lamports funding the mint account rent.
  programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
});

// !mark(1:7)
const initializeInterestBearingInstruction =
  createInitializeInterestBearingMintInstruction(
    mint.publicKey, // Mint account that stores the InterestBearingConfig extension.
    feePayer.publicKey, // Authority allowed to update the interest rate later.
    30000, // Interest rate in basis points.
    TOKEN_2022_PROGRAM_ID // Token program that owns the mint.
  );

const initializeMintInstruction = createInitializeMintInstruction(
  mint.publicKey, // Mint account to initialize.
  0, // Number of decimals for the token.
  feePayer.publicKey, // Authority allowed to mint new tokens.
  feePayer.publicKey, // Authority allowed to freeze token accounts.
  TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
);

const createTokenAccountInstruction = createAssociatedTokenAccountInstruction(
  feePayer.publicKey, // Account funding the associated token account creation.
  tokenAccount, // Associated token account address to create.
  recipient.publicKey, // Owner of the token account.
  mint.publicKey, // Mint for the associated token account.
  TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
  ASSOCIATED_TOKEN_PROGRAM_ID // Associated Token Program that creates the account.
);

const mintToTokenAccountInstruction = createMintToCheckedInstruction(
  mint.publicKey, // Mint account that issues the tokens.
  tokenAccount, // Token account receiving the newly minted tokens.
  feePayer.publicKey, // Signer authorized to mint new tokens.
  tokenAmount, // Token amount in base units.
  0, // Decimals defined on the mint.
  [], // Additional multisig signers.
  TOKEN_2022_PROGRAM_ID // Token program that owns the mint and token account.
);

await sendAndConfirmTransaction(
  connection,
  new Transaction({
    feePayer: feePayer.publicKey,
    blockhash: latestBlockhash.blockhash,
    lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
  }).add(
    createMintAccountInstruction,
    initializeInterestBearingInstruction,
    initializeMintInstruction
  ),
  [feePayer, mint]
);

await sendAndConfirmTransaction(
  connection,
  new Transaction().add(
    createTokenAccountInstruction,
    mintToTokenAccountInstruction
  ),
  [feePayer]
);

await new Promise((resolve) => setTimeout(resolve, 2_000));

const calculatedUiAmount = await amountToUiAmountForMintWithoutSimulation(
  connection,
  mint.publicKey,
  tokenAmount
);

// !mark(1:6)
const amountToUiInstruction = createAmountToUiAmountInstruction(
  mint.publicKey, // Mint whose UI amount conversion is being simulated.
  tokenAmount, // Token amount in base units.
  TOKEN_2022_PROGRAM_ID // Token program that owns the mint.
);

const amountToUiSimulation = await connection.simulateTransaction(
  new Transaction().add(amountToUiInstruction),
  [feePayer],
  false
);
const simulatedUiAmount = Buffer.from(
  amountToUiSimulation.value.returnData?.data?.[0] ?? "",
  "base64"
).toString("utf8");

// !mark(1:8)
const updateRateInstruction = createUpdateRateInterestBearingMintInstruction(
  mint.publicKey, // Mint account that stores the InterestBearingConfig extension.
  feePayer.publicKey, // Signer authorized to update the interest rate.
  15000, // New interest rate in basis points.
  [], // Additional multisig signers.
  TOKEN_2022_PROGRAM_ID // Token program that owns the mint.
);

await sendAndConfirmTransaction(
  connection,
  new Transaction().add(updateRateInstruction),
  [feePayer]
);

const mintAccount = await getMint(
  connection,
  mint.publicKey,
  "confirmed",
  TOKEN_2022_PROGRAM_ID
);
const interestBearingConfig = getInterestBearingMintConfigState(mintAccount);

console.log("Mint Address:", mint.publicKey.toBase58());
console.log("Token Account:", tokenAccount.toBase58());
console.log("Calculated UI Amount:", calculatedUiAmount);
console.log("Simulated UI Amount:", simulatedUiAmount);
console.log("InterestBearingConfig:", interestBearingConfig);
```

</CodeTabs>

### Rust

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

```rust !! title="Rust"
use anyhow::{anyhow, Result};
use base64::prelude::{Engine as _, BASE64_STANDARD};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{
    sysvar::clock::{self, Clock},
    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::{
        interest_bearing_mint::{
            instruction::{
                initialize as initialize_interest_bearing_instruction, update_rate,
            },
            InterestBearingConfig,
        },
        BaseStateWithExtensions, ExtensionType, StateWithExtensions,
    },
    instruction::{amount_to_ui_amount, initialize_mint, mint_to_checked},
    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 recipient = Keypair::new();
    let token_amount = 1_000_000_000_000u64;

    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::InterestBearingConfig])?;

    let mint_rent = client
        .get_minimum_balance_for_rent_exemption(mint_space)
        .await?;

    let create_mint_account_instruction = create_account(
        &fee_payer.pubkey(),    // Account funding the new mint account.
        &mint.pubkey(),         // New mint account to create.
        mint_rent,              // Lamports funding the mint account rent.
        mint_space as u64,      // Account size in bytes for the mint plus InterestBearingConfig.
        &TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
    );

    // !mark(1:6)
    let initialize_interest_bearing_instruction = initialize_interest_bearing_instruction(
        &TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.
        &mint.pubkey(), // Mint account that stores the InterestBearingConfig extension.
        Some(fee_payer.pubkey()), // Authority allowed to update the interest rate later.
        30000, // Interest rate in basis points.
    )?;

    let initialize_mint_instruction = initialize_mint(
        &TOKEN_2022_PROGRAM_ID,    // Program that owns the mint account.
        &mint.pubkey(),            // Mint account to initialize.
        &fee_payer.pubkey(),       // Authority allowed to mint new tokens.
        Some(&fee_payer.pubkey()), // Authority allowed to freeze token accounts.
        0,                         // Number of decimals for the token.
    )?;

    let create_mint_transaction = Transaction::new_signed_with_payer(
        &[
            create_mint_account_instruction,
            initialize_interest_bearing_instruction,
            initialize_mint_instruction,
        ],
        Some(&fee_payer.pubkey()),
        &[&fee_payer, &mint],
        client.get_latest_blockhash().await?,
    );

    client
        .send_and_confirm_transaction(&create_mint_transaction)
        .await?;

    let token_account = get_associated_token_address_with_program_id(
        &recipient.pubkey(),
        &mint.pubkey(),
        &TOKEN_2022_PROGRAM_ID,
    );

    let create_token_account_instruction = create_associated_token_account(
        &fee_payer.pubkey(), // Account funding the associated token account creation.
        &recipient.pubkey(), // Owner of the token account.
        &mint.pubkey(), // Mint for the associated token account.
        &TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
    );

    let mint_to_token_account_instruction = mint_to_checked(
        &TOKEN_2022_PROGRAM_ID, // Token program that owns the mint and token account.
        &mint.pubkey(), // Mint account that issues the tokens.
        &token_account, // Token account receiving the newly minted tokens.
        &fee_payer.pubkey(), // Signer authorized to mint new tokens.
        &[], // Additional multisig signers.
        token_amount, // Token amount in base units.
        0, // Decimals defined on the mint.
    )?;

    let create_token_account_transaction = Transaction::new_signed_with_payer(
        &[
            create_token_account_instruction,
            mint_to_token_account_instruction,
        ],
        Some(&fee_payer.pubkey()),
        &[&fee_payer],
        client.get_latest_blockhash().await?,
    );

    client
        .send_and_confirm_transaction(&create_token_account_transaction)
        .await?;

    tokio::time::sleep(std::time::Duration::from_secs(2)).await;

    let mint_account = client.get_account(&mint.pubkey()).await?;
    let mint_state = StateWithExtensions::<Mint>::unpack(&mint_account.data)?;
    let interest_bearing_config = mint_state.get_extension::<InterestBearingConfig>()?;

    let clock_account = client.get_account(&clock::ID).await?;
    let clock: Clock = clock_account.deserialize_data()?;
    let calculated_ui_amount = interest_bearing_config
        .amount_to_ui_amount(token_amount, mint_state.base.decimals, clock.unix_timestamp)
        .ok_or_else(|| anyhow!("Failed to calculate UI amount"))?;

    // !mark(1:5)
    let amount_to_ui_instruction = amount_to_ui_amount(
        &TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.
        &mint.pubkey(), // Mint whose UI amount conversion is being simulated.
        token_amount, // Token amount in base units.
    )?;
    let amount_to_ui_blockhash = client.get_latest_blockhash().await?;
    let amount_to_ui_transaction = Transaction::new_signed_with_payer(
        &[amount_to_ui_instruction],
        Some(&fee_payer.pubkey()),
        &[&fee_payer],
        amount_to_ui_blockhash,
    );
    let amount_to_ui_result = client.simulate_transaction(&amount_to_ui_transaction).await?;
    let simulated_ui_amount = String::from_utf8(
        BASE64_STANDARD.decode(
            amount_to_ui_result
                .value
                .return_data
                .ok_or_else(|| anyhow!("Expected AmountToUiAmount return data"))?
                .data
                .0,
        )?,
    )?;

    // !mark(1:6)
    let update_rate_instruction = update_rate(
        &TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.
        &mint.pubkey(), // Mint account that stores the InterestBearingConfig extension.
        &fee_payer.pubkey(), // Signer authorized to update the interest rate.
        &[], // Additional multisig signers.
        15000, // New interest rate in basis points.
    )?;

    let update_rate_transaction = Transaction::new_signed_with_payer(
        &[update_rate_instruction],
        Some(&fee_payer.pubkey()),
        &[&fee_payer],
        client.get_latest_blockhash().await?,
    );

    client
        .send_and_confirm_transaction(&update_rate_transaction)
        .await?;

    let updated_mint_account = client.get_account(&mint.pubkey()).await?;
    let updated_mint_state = StateWithExtensions::<Mint>::unpack(&updated_mint_account.data)?;
    let interest_bearing_config = updated_mint_state.get_extension::<InterestBearingConfig>()?;

    println!("Mint Address: {}", mint.pubkey());
    println!("Token Account: {}", token_account);
    println!("Calculated UI Amount: {}", calculated_ui_amount);
    println!("Simulated UI Amount: {}", simulated_ui_amount);
    println!("InterestBearingConfig: {:#?}", interest_bearing_config);

    Ok(())
}
```

</CodeTabs>
