Get Yellowstone gRPC streaming for $49/month! Access real-time Solana blockchain data.    Learn more
  • Pricing
  • Docs

Solana interest-bearing tokens: Inside the mint extension

Created Mar 16, 2026 Updated Mar 19, 2026
Yh5baeaaaaalaaaaaabaaeaaaibraa7 logo

In the traditional DeFi stack, engineering a yield-bearing asset has always been a battle against state bloat and compute costs. If you saw systems around EVM rebasing tokens, think of stETH as example, you know the architectural headaches they introduce. To reflect accrued interest, an EVM protocol must either continuously update storage slots across thousands of user accounts (which is expensive) or rely on a complex “shares-to-balances” mathematical abstraction under the hood, forcing every integrating smart contract to implement custom logic just to read a user’s true balance.

Solana’s Token-2022 standard completely flips this paradigm. Instead of fighting state mutations, it eliminates them entirely.

With the introduction of the Interest-Bearing Tokens Mint Extension, Solana introduces the concept of “Cosmetic Yield.” At the state level, the raw token amount residing in a user’s Token Account never changes. There are no rebasing events, no automated yield-harvesting cranks, and no state bloat. Instead, the accrued interest is calculated mathematically and continuously at the UI and RPC layer based on the network’s clock. The protocol simply applies a continuous compounding formula:

A=PertA = P e^{rt}

to the static principal.

Just as Transfer Hooks modularized transfer logic by pushing it to external programs, the Interest-Bearing extension modularizes yield by separating the configuration of the interest rate from the execution of the math. It relies heavily on a strict separation between execution logic in processor.rs and pure mathematical interfaces mod.rs.

In this post, we are going to get under the hood of the InterestBearingConfig. We will break down the Rust implementation, explore the continuous compounding math, and look at the architecture.

The architecture of Cosmetic Yield vs. State Mutation

To truly appreciate the engineering behind Solana’s Interest-Bearing extension, we first have to look at the problem it solves. In blockchain architecture, state is expensive, and mutating state across thousands of accounts simultaneously is almost impossible.

The EVM bottleneck: rebasing and shares

If you look at the successful yield-bearing assets on the EVM for example Lido’s stETH Token it rely on state mutation. Because an ERC-20 balanceOf function needs to return a constantly growing number to reflect yield, EVM developers had to get creative.

They introduced the “shares” model. Under the hood, your wallet doesn’t actually hold stETH, it holds a static number of shares of the total pool. When you call balanceOf(user), the smart contract executes a calculation:

Diagram showing the shares-based balance calculation used by rebasing tokens such as stETH
https://lido.fi/how-lido-works/rewards-and-penalties

Here is where the architectural bottleneck occurs. While UserShares and TotalShares remain relatively static, the actual amount of ETH backing the protocol on the Beacon Chain is constantly growing. However, the Ethereum execution layer smart contract is entirely isolated from this growth. The variable TotalPooledEther is just a static number sitting in contract storage. It cannot update itself.

For users to actually see their stETH balances increase, an external Oracle (a bot) must explicitly broadcast a transaction to the smart contract every period of time. This transaction calls a function that overwrites the global TotalPooledEther state variable to reflect the new yield. Because that global variable was changed, the math equation now outputs a higher number for every single user. But getting that higher number required an active transaction, gas fees, and a global state write.

Solana’s Token-2022 approach: computation over state

Solana’s architecture prioritizes parallel execution and isolated account state. Forcing a global state update to reflect yield across all token holders would create massive write-locks, crippling network throughput.

Token-2022 solves this by shifting the burden entirely from state storage to computation. This introduces the concept of Cosmetic Yield.

When an interest-bearing mint is initialized, the raw amount is stored in a user’s Token Account (a simple u64 integer) represents only the principal: the exact original baseline number of tokens the user received. This raw principal amount never changes, even as the tokens earn yield. Instead of mutating the state to add the newly accumulated profit, the yield is calculated dynamically. No automated cranks, no oracle updates, and no global state mutations are required to generate this yield. The state remains static, while the math handles the growth.

The uiAmount abstraction (where the yield lives)

If the raw balance doesn’t change, and no oracle is updating a global variable, how does the user actually see their money growing?

This is where the uiAmount comes in. In the Token-2022 standard, the interface makes a strict distinction between the raw amount (the state) and the uiAmount (the human-readable representation).

Instead of relying on an external party to push updates, the Solana Token-2022 program pulls its truth from the Network Clock. The Mint account holds an InterestBearingConfig, which stores the initialization timestamp and the current interest rate in basis points.

Because the network clock (Clock::get()?.unix_timestamp) inherently advances with every single block, the input to the continuous compounding formula:

A=PertA = P e^{rt}

is always growing automatically.

When a dApp, wallet, or RPC node queries a user’s balance, it doesn’t just read the static integer. Instead, it calls the program’s amount_to_ui_amount instruction (will be explained shortly). This routes the query directly through the extension’s math engine. The program fetches the current network timestamp, calculates the accrued interest purely in memory, and returns the combined total as a formatted string (the uiAmount). The protocol completely bypasses the need for state-mutating transactions; the state remains static, while the math handles the growth.

The math: continuous compounding on-chain

Now that we understand the yield is calculated dynamically rather than stored in state, we have to ask: how is the math actually executed? If you have written smart contracts before, you know that floating-point math and exponential calculations are generally the enemy of blockchain development. They consume massive amounts of compute units and introduce rounding errors that can easily be exploited. Yet, the Token-2022 program handles this seamlessly using continuous compounding.

The formula

The core mathematical engine of the Interest-Bearing extension relies on the standard continuous compound interest formula:

A=PertA = P e^{rt}

Here is exactly how these variables map to the Solana Token-2022 architecture:

  • A (Final Amount): The final ui_amount that includes the accrued yield.
  • P (Principal): The raw amount of tokens stored in the user’s Token Account (which never mutates).
  • e (Euler’s Number): The mathematical constant ~2.71828.
  • r (Rate): The annualized interest rate. On-chain, this is configured in basis points (where 10,000 bps = 100%) and converted to a decimal during the calculation.
  • t (Time): The time elapsed in years. Solana tracks this natively using the Unix timestamp in seconds, which the math engine divides by 31,556,736 (the number of seconds in a 365.24-day year).

reference can be found here.

The state variables: handling rate changes without state mutations

A major architectural challenge arises when the rate_authority decides to change the interest rate. If a token yielded 5% for the first six months, and the authority updates it to 10%, the protocol cannot retroactively apply 10% to the entire lifespan of the token.

If this were an EVM rebasing token, changing the rate would require a massive, O(N) state-mutating “checkpoint” transaction to lock in every individual user’s balance at the 5% rate before starting the 10% epoch.

Token-2022 solves this elegantly within the InterestBearingConfig Type-Length-Value (TLV) layout stored directly on the Mint account. It tracks three crucial variables (will be explained more in the next section):

  • current_rate: The active interest rate in basis points.
  • last_update_timestamp: The exact Unix timestamp of the last rate change.
  • pre_update_average_rate: The time-weighted average rate of all compounding periods prior to the last update.
fn process_update_rate(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    new_rate: &BasisPoints,
) -> ProgramResult {
    // ... Authority validation checks omitted for brevity
    let clock = Clock::get()?;
    
    // Calculate the historical average up to this exact second
    let new_average_rate = extension
        .time_weighted_average_rate(clock.unix_timestamp)
        .ok_or(TokenError::Overflow)?;
        
    // Lock in the historical average
    extension.pre_update_average_rate = new_average_rate.into();
    
    // Move the checkpoint timestamp forward to the current block
    extension.last_update_timestamp = clock.unix_timestamp.into();
    
    // Set the new active rate
    extension.current_rate = *new_rate;
    Ok(())
}

Notice what is completely absent from this function: there is no iteration over token accounts. Zero user balances are touched.

The protocol simply calculates the new pre_update_average_rate up to the current block time, moves the last_update_timestamp forward, and sets the new current_rate inside the Mint’s TLV extension. When a user’s balance is calculated tomorrow, the math engine simply uses the locked historical average for the past, and the new active rate for the present.

Full code can be found here.

How checkpointing works in practice

Now that we have seen the raw Rust logic, let’s watch the Token-2022 program execute this checkpointing in real-time. To see exactly how the state mutates without touching user accounts, we are going to use the Solana CLI to create a token, inspect its abstracted state, and trigger an interest rate change.

Creating the mint

First, we initialize a Token-2022 mint and tell the program to allocate space for the InterestBearingConfig TLV extension. We are going to set the initial rate to 5% (which is 500 basis points).

spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --interest-rate 500

Output:

Creating token Dmcd... (your new mint address)
Signature: 4j...

Inspecting the abstracted state

Now that the mint is live, we can look at the exact bytes stored in the account data using the display command.

spl-token display <YOUR_MINT_ADDRESS>

If you look closely at the output, the CLI decodes the Interest-bearing extension block. It will look like this:

Extensions
  Interest-bearing:
    Current rate: 500bps
    Average rate: 500bps
    Rate authority: <YOUR_WALLET>

Because the token was just created, the Average rate and Current rate are identical (500 bps). However, the CLI is actually hiding some crucial data here for the sake of a clean UI. Under the hood, the raw Rust struct is also storing the initialization and update timestamps. The math engine knows that, as of this exact moment, the token earns 5%.

Triggering the checkpoint (updating the rate)

Let’s say six months pass. We want to update the rate to 10% (1000 basis points). As the Rate Authority, we fire the set-interest-rate command:

spl-token set-interest-rate <YOUR_MINT_ADDRESS> 1000

If we immediately run spl-token display <YOUR_MINT_ADDRESS> again, we will see the Token-2022 math engine has safely mutated the Mint’s state:

Extensions
  Interest-bearing:
    Current rate: 1000bps
    Average rate: 500bps
    Rate authority: L6h2ugreR3oNmKtBCpcsMPSLg3BwLR31fSPB1iU76mp

Look at what just happened: Zero user accounts were touched. Instead, the Mint locked its own history.

Behind the scenes, the program updated its hidden last_update_timestamp to the current network clock. When a frontend UI or an RPC node calculates a user’s yield tomorrow, it simply breaks the continuous compounding formula into two distinct chunks:

  1. The Past: It looks at the time between the creation and the hidden update timestamp, applying the Average rate (1 year 5%).
  2. The Present: It looks at the time since the last update up to the current block time, applying the new Current rate (0.5 years 10%).

Peeking under the hood: Finding the hidden timestamps

While the CLI conveniently formats the output for a clean terminal experience, as developers, we need to know what the math engine is actually reading.

If you take that same Mint address, plug it into a Solana block explorer, and look at the raw on-chain data, the UI abstraction falls away. You will see the complete JSON representation of the interestBearingConfig extension exactly as it lives on the network:

Raw Solana mint account data showing interestBearingConfig timestamps and rate fields

Here, the hidden architecture is fully exposed. You can clearly see the initializationTimestamp and the lastUpdateTimestamp that the continuous compounding formula inherently relies on to slice the yield curve into the “Past” and the “Present” without touching a single user’s token account.

The abstraction layer and the BPF math engine

Raw amount vs uiAmount

The entire architecture of the Interest-Bearing extension hinges on a strict separation between state storage and presentation. In the Token-2022 standard, there is a hard boundary between the amount (the raw u64 integer stored in the database) and the uiAmount (the mathematically yielded float presented to the user).

If a developer or indexer bypasses this abstraction layer and attempts to read the raw state directly, the architecture breaks down visually. Look at this screenshot from a popular Solana block explorer attempting to render the exact Mint we just updated:

Block explorer displaying an incorrect 1000 percent interest rate due to raw basis-point parsing

The raw data trap

When we used the CLI to update the rate, we passed the integer 1000. In the Rust engine, this strictly represents 1000 basis points (bps), which equates to exactly 10%.

Because the block explorer bypassed the program’s native math engine and directly deserialized the raw currentRate bytes from the Mint’s TLV extension and appended a % sign to the raw integer. The result is a frontend mistakenly advertising a 1,000% APY.

This highlights a critical architectural constraint: You cannot natively trust the raw state of an Interest-Bearing token.

To safely bridge the gap between the static state and the continuous compounding math, the Solana developers introduced two specific, native instructions to the Token-2022 program:

  1. AmountToUiAmount
  2. UiAmountToAmount

Instead of forcing RPC nodes or other programs to import a math library and attempt to recreate the

formula locally, these instructions allow you to query the program directly. You pass the raw u64 amount into the instruction, and the Token-2022 program executes the exponential math using the Mint’s exact timestamps, returning the formatted string.

Querying the math engine

To safely bridge the gap between the static state and the continuous compounding math, the Solana developers built the yield conversion directly into the RPC layer.

Before we can query a balance, we actually need a token account with some principal in it. Let’s use the CLI to create an account for our Mint and give ourselves 100 tokens.

spl-token create-account <YOUR_MINT_ADDRESS>
spl-token mint <YOUR_MINT_ADDRESS> 100

Now, let’s imagine a few hours pass. To see the yield in action, we can query that specific token account using a standard curl request to the getTokenAccountBalance RPC method:

curl https://api.devnet.solana.com -X POST -H "Content-Type: application/json" -d '
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTokenAccountBalance",
  "params": [
    "<YOUR_TOKEN_ACCOUNT_ADDRESS>"
  ]
}'

When the RPC node receives this request, it detects the Interest-Bearing extension, runs the exponential math using the Mint’s hidden timestamps, and returns both numbers simultaneously:

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "apiVersion": "3.1.10",
      "slot": 448286815
    },
    "value": {
      "amount": "100000000000",   <-- The raw principal (100 tokens with 9 decimals)
      "decimals": 9,
      "uiAmount": 100.009224922,  <-- The mathematically yielded balance!
      "uiAmountString": "100.009224922"
    }
  },
  "id": 1
}

Notice what is completely absent from this JSON response? The interest rate itself.

You don’t have to fetch the 1000 basis points from the Mint, and you don’t have to manually calculate whether that means 10% or 1,000%. The RPC node handled the basis-point conversion, fetched the network clock, and ran the

A=PertA = P e^{rt}

formula entirely under the hood. The uiAmount already contains the correct yield.

By relying on the RPC (or the native on-chain CPI) to execute the math, your protocol becomes completely immune to the kind of frontend parsing bugs we saw on the block explorer. The hard boundary is perfectly clear: the amount stays static, but the uiAmount safely delivers the continuously compounding truth.

Conclusion

To wrap up, the Token-2022 Interest-Bearing extension represents a fundamental architectural shift from state storage to computation:

  • No State Bloat: Unlike EVM rebasing tokens that require constant Oracle transactions to mutate global state, Solana calculates yield dynamically using continuous compounding driven by the network clock.
A=PertA = P e^{rt}

  • Checkpointing the Mint: Interest rate changes lock historical averages directly into the Mint’s TLV configuration. This completely bypasses the need to iterate over or mutate individual user accounts.
  • The Abstraction Layer: A user’s raw amount is strictly static principal. To get the true yielded balance, developers cannot just read the raw state, they must query the program’s math engine directly via RPC or on-chain CPI.

Ultimately, it is an elegant, compute-heavy solution to EVM state bloat, paving the way for highly scalable, natively yielding assets.

Reliable Solana RPC infrastructure

Getting started with Solana on Chainstack is fast and straightforward. Developers can deploy a reliable Solana node within seconds through an intuitive Console — no complex setup or hardware management required. 

Chainstack provides low-latency Solana RPC access and real-time gRPC data streaming via Yellowstone Geyser Plugin, ensuring seamless connectivity for building, testing, and scaling DeFi, analytics, and trading applications. With Solana low-latency endpoints powered by global infrastructure, you can achieve lightning-fast response times and consistent performance across regions. 

Start for free, connect your app to a reliable Solana RPC endpoint, and experience how easy it is to build and scale on Solana with Chainstack – one of the best RPC providers.

FAQ

What is a Solana interest-bearing token?

It is a Token-2022 token that accrues yield mathematically without changing the raw token amount stored in each account.

What is the Interest-Bearing Mint Extension?

It is a Token-2022 extension that stores interest-rate configuration at the mint level and lets clients derive the interest-adjusted balance over time.

Does the token account balance change as interest accrues?

No. The raw on-chain amount stays the same. The yield-adjusted value is exposed through UI- and RPC-level calculations.

What is the difference between amount and uiAmount?

amount is the raw stored principal. uiAmount is the interest-adjusted value shown to users after applying the extension logic.

Why is this different from rebasing tokens?

Rebasing tokens rely on balance updates or shares-based abstractions. Solana’s model avoids per-account state mutation and computes yield dynamically.

Why does this matter for developers?

Integrations must read balances correctly, understand RPC abstractions, and avoid treating raw account data as the final user-visible balance.

Learn more about Solana architecture from our articles

SHARE THIS ARTICLE
andrey_o

Andrey Obruchkov

Senior Software Engineer and blockchain specialist with hands-on experience building across EVM, Solana, Bitcoin, Cosmos, Aptos, and Sui ecosystems from smart contracts and DeFi infrastructure to cross-chain integrations and developer tooling.

Corn 530x281 logo

Chainstack introduces support for Corn

Build BTCFi DApps on Corn with Chainstack—Arbitrum Orbit L2 using Bitcoin-backed gas, veTokenomics incentives, and native cross-chain DeFi.

Andrey Novosad18 150x150 logo
Petar Stoykov
May 20
Customer Stories

tendex

Multi-contract stress-testing to ensure smooth trading infrastructure mainnet operations.

SMARTy Pay

Automating infrastructure network operations with databases and the blockchain application.

Unicrypt

Eliminating block synchronization issues with smooth network performance and affordable pricing.