Anchor
Token2022 with Anchor

Token2022 with Anchor

The Interest Bearing Extension

The InterestBearing extension is a Mint account extension that lets users apply an interest rate to their tokens and retrieve the updated total, including interest, at any given moment.

Initializing the Mint Account

Since Anchor doesn't have any macros for the interest_bearing extension we're going to create a Mint account using the raw CPIs.

Here's how to create a mint with the Transfer Fee extension:

use anchor_lang::prelude::*;
use anchor_lang::system_program::{create_account, CreateAccount};
use anchor_spl::{
    token_2022::{
        initialize_mint2,
        spl_token_2022::{extension::ExtensionType, pod::PodMint},
        InitializeMint2,
    },
    token_interface::{
        interest_bearing_mint_initialize, InterestBearingMintInitialize, Mint, Token2022,
    },
};
 
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(mut)]
    pub mint_account: Signer<'info>,
    pub token_program: Program<'info, Token2022>,
    pub system_program: Program<'info, System>,
}
 
pub fn initialize_interest_bearing_config(
    ctx: Context<Initialize>,
    rate: i16
) -> Result<()> {
    // Calculate space required for mint and extension data
    let mint_size =
        ExtensionType::try_calculate_account_len::<PodMint>(&[ExtensionType::InterestBearingConfig])?;
 
    // Calculate minimum lamports required for size of mint account with extensions
    let lamports = (Rent::get()?).minimum_balance(mint_size);
 
    // Invoke System Program to create new account with space for mint and extension data
    create_account(
        CpiContext::new(
            ctx.accounts.system_program.to_account_info(),
            CreateAccount {
                from: ctx.accounts.payer.to_account_info(),
                to: ctx.accounts.mint_account.to_account_info(),
            },
        ),
        lamports,                          // Lamports
        mint_size as u64,                  // Space
        &ctx.accounts.token_program.key(), // Owner Program
    )?;
 
    // Initialize the InterestBearingConfig extension
    // This instruction must come before the instruction to initialize the mint data
    interest_bearing_mint_initialize(
        CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            InterestBearingMintInitialize {
                token_program_id: ctx.accounts.token_program.to_account_info(),
                mint: ctx.accounts.mint_account.to_account_info(),
            },
        ),
        Some(ctx.accounts.payer.key()),
        rate,
    )?;
 
    // Initialize the standard mint account data
    initialize_mint2(
        CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            InitializeMint2 {
                mint: ctx.accounts.mint_account.to_account_info(),
            },
        ),
        2,                               // decimals
        &ctx.accounts.payer.key(),       // mint authority
        Some(&ctx.accounts.payer.key()), // freeze authority
    )?;
 
    Ok(())
}

Calculating the Interest

The InterestBearing extension, does not generate new tokens; the displayed amount simply includes the accumulated interest through the amount_to_ui_amount function, making the change purely aesthetic.

So after unpacking the Token account that we want to calculate the interest from, it's easy to get all the information that we need to perform the calculation to get the amount of interest.

Luckily for us we don't even need to do so since we can use the amountToUiAmount function like so:

const tokenInfo = await getAccount(
    connection,
    tokenAccount,
    undefined,
    TOKEN_2022_PROGRAM_ID,
);
 
console.log("Token Amount: ", tokenInfo.amount);
    
const uiAmount = await amountToUiAmount(
    connection,
    keypair,
    mint.publicKey,
    tokenInfo.amount,
    TOKEN_2022_PROGRAM_ID,
);
    
console.log("UI Amount: ", uiAmount);

Updating the Interest Bearing Extension

With the InterestBearing extension is possible to change the interest produced by the token account thanks to the way its data is structured:

pub struct InterestBearing {
    pub rate_authority: Pubkey,
    pub initialization_timestamp: i64,
    pub pre_update_average_rate: u16,
    pub last_update_timestamp: i64,
    pub current_rate: u16,
}

To do so we can use the interest_bearing_mint_update_rate() instruction like so:

interest_bearing_mint_update_rate(
    CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        InterestBearingMintUpdateRate {
            token_program_id: ctx.accounts.token_program.to_account_info(),
            mint: ctx.accounts.mint_account.to_account_info(),
            rate_authority: ctx.accounts.authority.to_account_info(),
        },
    ),
    rate,
)?;

And if we want, we can change the authority over setting the interest using the set_authority() function and passing in the right AuthorityType like so:

set_authority(
    CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        SetAuthority {
            current_authority: ctx.accounts.authority.to_account_info,
            account_or_mint: ctx.accounts.mint.to_account_info,
        }
    ),
    spl_token_2022::instruction::AuthorityType::InterestRate,
    new_authority,
)?;
Contents
View Source
Blueshift © 2025Commit: 538ee5b