Mint tokens on Solana using the Rust SDK
How to get started with using the spl token program on Solana to mint your very own tokens
New II: A guide on how to structure your solana program and help on how to solve the IdlError: Type is not found
New: An article on how to transfer tokens between accounts on Solana in a smart contract is out. Please check it out if you want to continue to learn about Solana development.
gm gm, I’m 0xksure and I write mostly about rust development and solana (usually with the anchor framework) development. I have a lot of experience in backend development, but have also done my fair share of frontend work and general sdk work using typescript.
I’ve recently had some time to sit down and really dig into the programming model on Solana. As you probably know Solana is a proof of stake blockchain that is built with rust and promises high throughput. Therefore, the smart contract programming language is naturally rust. Don’t worry you don’t have to be an expert in rust to develop smart contracts on Solana. Now, you might have heard of anchor which is a framework on top of the Solana runtime which makes it easier to build with a syntax similar to what people are familiar with when doing Ethereum smart contract development. I will not use that framework here as the focus will be more on the integration testing part in rust than smart contract development.
I thought it was a good idea to start off with the Solana chicago bootcamp youtube series to get a grip of programming on Solana. So I went through the first exercise which was creating an echo program to store arbitrary data in accounts thus treating the account as a buffer. The final exercise was to create an exchange booth where a user could deposit a token pair, say USDC and SOL, into an exchange booth. The tokens or mints would then be stored in vault_usdc and vault_sol respectively. If a users wanted to swap sol for usdc the program could call an oracle to get the SOL/USDC ratio and then return the appropriate amount of usdc. Say the user wanted to swap 1 sol with the ratio being sol/usdc= 103.4 then the user would receive 103 usdc for which the program could take 0.4 usdc in fees.
So I created the program with the initialize_exchange_booth instruction and was ready to write the corresponding test. However, in order to really test the instruction I would have to create two tokens and two vaults that can hold each of the minted tokens. Additionally, the test user has to hold some amount of each. This right here is the motivation behind this post. How would you set up such an environment?
So in order to mint different tokens you will have to interact with the token program. Here are some useful links
I won’t drag you through the resources but rather reference them when needed. Let’s get started.
Setting up the environment
The Solana cargo project
First, we need to set up a Solana cargo project. I will assume you have cargo edition 2021 installed, if not please follow the Solana docs guide for getting started
To start a new project type
into your terminal. These commands will create a new rust library project and then go into the newly created project.
Then we need some dependencies to be able to write our tests. Basically we need
- solana_program (dependency)
- spl_token (dependency)
- solana_program_test (dev-dependency)
- solana_sdk (dev-dependency)
- solana-validator (dev-dependency)
you also need to enable the test-bpf feature and specify that the crate type is a library. Thus your Cargo.toml should look like this:
As of now the current version of most of these crates are 1.9.1 but please use the newest version if possible.
NOTE: if you are using vscode to code up your Solana program I would advice you to use the rust-analyzer extension. It would help a lot when figuring out which accounts are needed for the different instructions.
Setting up testing environment
Time to set up the environment. This is usually done in a separate test folder. So create a test folder with a test.rs in it. So the folder structure looks like this
We will do async testing using the tokio runtime included in the solana_program_test crate. So your test skeleton should be this:
Yes, clean simple but no tests yet. Now let’s check that we have set up everything correctly by running the test-bfp command in your terminal
This should result in an “all tests passed output” although there are not yet any tests available.
Nice, let’s continue to set up the testing environment.
First of all we must set up the banks_client that we can use to interact with the Solana runtime. This means initializing the ProgramTest.
Since we are not going to interact with our own Solana program it is enough to just set the ProgramTest to default. Then we start the program and get a client, a payer and the recent blockhash in return. The payer will be used later to fund the various transactions needed to mint and distribute tokens.
Finally we are ready to interact with the spl_token program and mint some tokens!
In order to do so we need a couple of accounts:
- mint_account: <public key+private key> that will hold the newly minted object
- owner: <public key + private key> that has the authority to mint token by signing future mint transactions
- token_program: <public key> this is public key of the token program
So in order to mint a token we first need to create a mint_account that has enough space for the mint and is well funded to store the mint data. Then we initialize the mint with the mint_account and with the owner as the mint_authority. These two instructions should then be executed in the same transaction so that another party can’t acquire ownership of the newly created mint_account.
Finally we submit the transaction to the client. The resulting test should be
- line 22–28: we create the mint account instruction
- line 30–37: we create the initialize mint instruction and unwrap it to check if the instruction is valid.
- line 40–45: we create the transaction that will execute each of the instructions so that we end up with a new mint account. The signers is the payer of the mint_account and the mint_account itself.
- line 47–50: the transaction is submitted by the client.
Finally you can again run
to see that you are actually able to initialize the mint.
Create account to hold minted tokens
The next thing we want to do is to create a new account and prepare it for holding our minted tokens. It is very similar to creating a mint account but instead of using the spl_token instruction initializing_mint we instead use initialize_account instruction.
Instead of including the whole test so far I will just display the parts that are relevant for the current transaction
The account initialization happens on line 54- 81:
- lines 4–10: the payer creates a new account and funds it with enough lamports to hold the necessary account data. The owner is the spl_token program.
- lines 13–19: we create the instruction for initializing the account and specify the mint_pubkey in order to associate it with out previous mint. We also specify the owner to be a new keypair.
- lines 21–26: the transaction to first create the account and then to initialize the account to hold tokens.
- lines 27–30: submit the transaction using the banks client.
Again you can run
to check that everything runs smoothly.
Mint tokens into account
Finally, we are ready to mint some tokens into our newly created token_account. In order to do so we use the mint_to spl_token instruction which is pretty explanatory. So we can add the following commands to our program
- lines 3–11: create an instruction to mint tokens into the token_account.
- lines 13–18: create a transaction that contains the mint_to instruction.
- line 19: process the transaction using the client.
Congratulations! 10 newly minted tokens are now in your account.
Check your account
Finally we can check that the 10 tokens actually are in your account
- lines 2–6: use the bank client to fetch the token_account information.
- line 7: unpack the data contained in the token_account into the spl_token::state:Account struct.
- lines 9–13: check that the expected amount of tokens are actually in your account.
Finally the complete test.rs file should look like this
and finally by running
you should see that the assertion is true and that no transactions fails.
Through this short tutorial we have achieved:
- Setting up a Solana smart contract project from scratch
- Setting up a integration test against a local test validator
- Minted our own token into a fresh account
If you are more into NFTs then you could instead of minting 10 tokens into one account you could instead mint 1 token into the same account which would make that token a fungible token instead.
If you want to do some improvement you could create a function for initializing a mint and another for initializing an account. This would help immensely with the readability.
The github repo can be found here: https://github.com/kristohberg/test-Solana-sdk Please fork as much as you want!
New: A new article on how to transfer tokens between accounts on Solana in a smart contract is out. Please check it out if you want to continue to learn about Solana development.
Please follow me on twitter at https://twitter.com/kristohberg for more casual content on how to develop on Solana as well as general thoughts about the blockchain space.