The InterestBearingConfig
extension enables developers to set an interest rate
stored directly on the Mint Account. Interest is compounded continuously, based
on the network's timestamp.
In other words, the accrued interest is simply a visual UI conversion, but the underlying token quantity remains unchanged. This design eliminates the need for frequent rebase or update operations to adjust for accrued interest.
Note that the interest accrual is only a calculation, and does not involve minting new tokens.
In this guide, we'll walk through an example of using Solana Playground. Here is the final script.
Getting Started
Start by opening this Solana Playground link with the following starter code.
// Clientconsole.log("My address:", pg.wallet.publicKey.toString());const balance = await pg.connection.getBalance(pg.wallet.publicKey);console.log(`My balance: ${balance / web3.LAMPORTS_PER_SOL} SOL`);
If it is your first time using Solana Playground, you'll first need to create a Playground Wallet and fund the wallet with devnet SOL.
If you do not have a Playground wallet, you may see a type error within the
editor on all declarations of pg.wallet.publicKey
. This type error will clear
after you create a Playground wallet.
To get devnet SOL, run the solana airdrop
command in the Playground's
terminal, or visit this devnet faucet.
solana airdrop 5
Once you've created and funded the Playground wallet, click the "Run" button to run the starter code.
Add Dependencies
Let's start by setting up our script. We'll be using the @solana/web3.js
and
@solana/spl-token
libraries.
Replace the starter code with the following:
import {Connection,Keypair,SystemProgram,Transaction,clusterApiUrl,sendAndConfirmTransaction,} from "@solana/web3.js";import {ExtensionType,updateRateInterestBearingMint,createInitializeInterestBearingMintInstruction,createInitializeMintInstruction,getMintLen,TOKEN_2022_PROGRAM_ID,amountToUiAmount,getInterestBearingMintConfigState,getMint,} from "@solana/spl-token";// Playground walletconst payer = pg.wallet.keypair;// Connection to devnet clusterconst connection = new Connection(clusterApiUrl("devnet"), "confirmed");// Transaction signature returned from sent transactionlet transactionSignature: string;
Mint Setup
First, let's define the properties of the Mint Account we'll be creating in the following step.
// Generate new keypair for Mint Accountconst mintKeypair = Keypair.generate();// Address for Mint Accountconst mint = mintKeypair.publicKey;// Decimals for Mint Accountconst decimals = 2;// Authority that can mint new tokensconst mintAuthority = pg.wallet.publicKey;// Authority that can update the interest rateconst rateAuthority = pg.wallet.keypair;// Interest rate basis points (100 = 1%)// Max value = 32,767 (i16)const rate = 32_767;
Next, let's determine the size of the new Mint Account and calculate the minimum lamports needed for rent exemption.
// Size of Mint Account with extensionconst mintLen = getMintLen([ExtensionType.InterestBearingConfig]);// Minimum lamports required for Mint Accountconst lamports = await connection.getMinimumBalanceForRentExemption(mintLen);
With Token Extensions, the size of the Mint Account will vary based on the extensions enabled.
Build Instructions
Next, let's build the set of instructions to:
- Create a new account
- Initialize the
InterestBearingConfig
extension - Initialize the remaining Mint Account data
First, build the instruction to invoke the System Program to create an account and assign ownership to the Token Extensions Program.
// Instruction to invoke System Program to create new accountconst createAccountInstruction = SystemProgram.createAccount({fromPubkey: payer.publicKey, // Account that will transfer lamports to created accountnewAccountPubkey: mint, // Address of the account to createspace: mintLen, // Amount of bytes to allocate to the created accountlamports, // Amount of lamports transferred to created accountprogramId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account});
Next, build the instruction to initialize the InterestBearingConfig
extension
for the Mint Account.
// Instruction to initialize the InterestBearingConfig Extensionconst initializeInterestBearingMintInstruction =createInitializeInterestBearingMintInstruction(mint, // Mint Account addressrateAuthority.publicKey, // Designated Rate Authorityrate, // Interest rate basis pointsTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);
Lastly, build the instruction to initialize the rest of the Mint Account data. This is the same as with the original Token Program.
// Instruction to initialize Mint Account dataconst initializeMintInstruction = createInitializeMintInstruction(mint, // Mint Account Addressdecimals, // Decimals of MintmintAuthority, // Designated Mint Authoritynull, // Optional Freeze AuthorityTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);
Send Transaction
Next, let's add the instructions to a new transaction and send it to the
network. This will create a Mint Account with the InterestBearingConfig
extension enabled.
// Add instructions to new transactionconst transaction = new Transaction().add(createAccountInstruction,initializeInterestBearingMintInstruction,initializeMintInstruction,);// Send transactiontransactionSignature = await sendAndConfirmTransaction(connection,transaction,[payer, mintKeypair], // Signers);console.log("\nCreate Mint Account:",`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`,);
Run the script by clicking the Run
button. You can then inspect the
transaction details on SolanaFM.
Update Interest Rate
The designated Rate Authority can then update the interest rate on the Mint Account at any time.
// New interest rate in basis pointsconst updateRate = 0;// Update interest rate on Mint AccounttransactionSignature = await updateRateInterestBearingMint(connection,payer, // Transaction fee payermint, // Mint Account AddressrateAuthority, // Designated Rate AuthorityupdateRate, // New interest rateundefined, // Additional signersundefined, // Confirmation optionsTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);console.log("\nUpdate Rate:",`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`,);
Fetch Interest Config State
Next, let's check the updated interest rate by fetching the Mint Account data.
// Fetch Mint Account dataconst mintAccount = await getMint(connection,mint, // Mint Account Addressundefined, // Optional commitmentTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);// Get Interest Config for Mint Accountconst interestBearingMintConfig = await getInterestBearingMintConfigState(mintAccount, // Mint Account data);console.log("\nMint Config:",JSON.stringify(interestBearingMintConfig, null, 2),);
Calculate Accrued Interest
Lastly, let's calculate the interest accrued for a given amount. Note that this calculation can be performed independently for any amount, without the need for minting tokens.
// Wait 1 secondsleep(1000);// Amount to convertconst amount = 100;// Convert amount to UI amount with accrued interestconst uiAmount = await amountToUiAmount(connection, // Connection to the Solana clusterpayer, // Account that will transfer lamports for the transactionmint, // Address of the Mint accountamount, // Amount to be convertedTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);console.log("\nAmount with Accrued Interest:", uiAmount);
Run the script by clicking the Run
button. You can then inspect the
transaction details on SolanaFM and view the data logged in the Playground
terminal.
Interest is continuously compounded based on the timestamp in the network. Due to drift that may occur in the network timestamp, the accumulated interest could be lower than the expected value. Thankfully, this is rare.
You should see an output similar to snippet below, where the decimal values indicate the interest that has accumulated:
Mint Config: {"rateAuthority": "3z9vL1zjN6qyAFHhHQdWYRTFAcy69pJydkZmSFBKHg1R","initializationTimestamp": 1702321738,"preUpdateAverageRate": 32767,"lastUpdateTimestamp": 1702321740,"currentRate": 0}Amount with Accrued Interest: 1.000000207670422
Conclusion
The InterestBearingConfig
extension introduces a simple mechanism for tokens
to increase or decrease in value over time. By seamlessly integrating tools
commonly found in traditional finance, this innovation broadens Solana's
capabilities, bridging the gap between conventional financial instruments and
the world of blockchain.