DEV Community

Cover image for Minting Fungible Tokens on Solana with Solidity
Shivam Soni
Shivam Soni

Posted on • Edited on

Minting Fungible Tokens on Solana with Solidity

Introduction

In this tutorial, I want to dive into Solana and Solang to show you how to build an spl-token-minter program using solidity.

By the end of this tutorial, you'll have accomplished a working on-chain program that can be deployed to the Solana blockchain and interact with the Solana blockchain for extremely low fees and fast confirmation times.

This is the second part of the Solang series, this part features a practical example using a Solidity program called spl-token-minter within the Anchor framework. This example will give you hands-on experience in creating and minting fungible tokens on Solana using Solidity.

The code for the project is located here

Here's what you're gonna learn:

Table of contents

  1. How to write Solana program
  2. Setting up a development environment.
    • Step 0 installing dependencies.
    • Step 1 Spl-token minter.
      • Solana blockchain cluster Types.
      • Important Differences(Compared with EVM smart contract)
      • Storing of state data.
    • Step 2 Creating the solidity program.
      • What it takes to mint any custom spl tokens to token address.
      • Write program.
  3. Setting up for building, deployment and testing
    • Writing test script
    • Building program
    • Deploying the program
    • Testing Program
  4. Conclusion
  5. Further learning resources

How to write a Solana program

Continuing our discussion on building Solana programs with the aid of Solana's tools and languages, let's now embark on a detailed exploration of developing a Solana program. Specifically, we'll focus on creating a Solana program named "spl-token-minter" using Solidity within the Anchor Solana framework. To begin this endeavour, we must initiate the setup of a new project.

Setting up the development environment

Step 0 Install dependencies(Prerequisites)

Do not be scared of rust here this is mandatory to install because Anchor and Solang use rust to build programs

We are Installing and setting up required dependencies for working with Solidity on the Solana blockchain development platform Make sure to do this on your preferred system, whether it's Linux, MacOS, or Windows.

Install and set up Rust

https://www.rust-lang.org/tools/install

Make sure the installation is successful by running

rustup --version  
rustc --version  
cargo --version
Enter fullscreen mode Exit fullscreen mode

Anchor requires nodejs and yarn on your machine to run so if you don’t have them yet install it from here

Install and set up Node.js

https://nodejs.org/en

Once the installation is completed confirm the successful installation
like this:

node --version  
npm --version
Enter fullscreen mode Exit fullscreen mode

Installing yarn

npm i -g yarn
Enter fullscreen mode Exit fullscreen mode

Next, we need to install Solana-tools from here
As mentioned solang pre-bundled with Solana tool suite.

The main things we'll be doing with the Solana CLI will be configuring our network (to devnet) as well as airdropping tokens into our wallets, pretty much everything else we'll be doing with the Anchor CLI.

Now Install and set up the Solana Tools Suite.

https://docs.solana.com/cli/install-solana-cli-tools

After a successful installation run:

solana --version
Enter fullscreen mode Exit fullscreen mode

Make a wallet account that you'll use later for deploying the program on Solana Devnet.

solana-keygen new
Enter fullscreen mode Exit fullscreen mode

Install and Configure Solang

The newest Solang version comes already included with the Solana Tools Suite that you've set up before. It should now work well together with Solana CLI.

To make sure Solang is installed correctly, use this command.

Solang --version
Enter fullscreen mode Exit fullscreen mode

If everything is fine you see something like this

➜  ~ solang --version
solang version v0.3.1
➜  ~ 
Enter fullscreen mode Exit fullscreen mode

Install and Configure Anchor Framework

https://www.anchor-lang.com/docs/installation

Once you've set up everything, just use the following command to install Confirm AVM and Anchor CLI:

avm --version
anchor --version
Enter fullscreen mode Exit fullscreen mode

If everything is fine you see something like this

➜  ~ avm --version
avm 0.28.0
➜  ~ anchor --version
anchor-cli 0.28.0
➜  ~ 
Enter fullscreen mode Exit fullscreen mode

Install and Configure Solang VSCode Extension

vs code extension

With the setup complete, let's move on to initializing a new Anchor project. I'll also provide an overview of the project files.

Step 1 Create a development workspace

First of all, create a new folder where all your work will be located. Once you have created the folder, open a terminal in that folder and run the following command.

anchor init spl-token-minter --solidity
Enter fullscreen mode Exit fullscreen mode

This will take some time
Anchor will be installing all dependencies that are needed for the project.

After going to the project directory

cd spl-token-minter
Enter fullscreen mode Exit fullscreen mode

Run the following command to open the vscode

code .
Enter fullscreen mode Exit fullscreen mode

You will go to your vscode app
from terminal
Then, You will see the project have a structure like this:

anchor file overviews

Below is How by default Anchor organizes newly created project, we will focus on these folders/files:

Here's a simplified and concise version of the content:

  • app: This folder holds frontend code if you plan to build and host it together with your code.

  • solidity: This folder contains your actual Solana smart contract code in ".sol" files.

  • tests: Here, you can test how your contract behaves when used, helping you build, deploy, and test Solana programs.

  • anchor.toml: Similar to package.json for Node.js, this tracks dependencies and important Anchor settings. You can install a VSCode extension to work with it easily.

  • .gitignore: This file lists things to ignore when you share your code on GitHub.

  • You can ignore these for now: migrations, node_modules, .prettierignore, package.json, tsconfig.json. We'll go over them later.

Note -:

If you're used to working with Solidity on Ethereum, you know about Mainnet and Testnet. With Solana, it might be a bit surprising to find out that there are three options instead of two.

Types of Solana Blockchain Cluster:

  1. Mainnet: This is the actual Solana Blockchain where you use real SOL cryptocurrency. It's for doing real things with Solana programs, and once they're here, they can't be undone.

  2. Devnet: When you're building on Solana using Solidity, you'll mostly be working here. It's a place to test your Solana programs before they go live on Mainnet. On Devnet, you get free SOL through airdrops (I'll explain how to get it below). Unlike Mainnet, stuff on Devnet can be reset, which means what you put here might not stay forever.

  3. Testnet: This is like Devnet, The Testnet is where Solana's main team carefully tests the newest release features in a live cluster setup. Their main goal is to check network performance, stability, and validator behaviour while also paying attention to good grammar, clear writing, and a smooth flow of information.

You'll only need to worry about Testnet if you're directly dealing with the Solana protocol. The tokens on Testnet aren't real money, and you can get free SOL through airdrops.

In anchor.toml file you will need to change cluster
localnet to devnet

update anchor file

To this

update anchor file

Next, let’s go over the starter code beginning with the on-chain program itself. Within your project's ./solidity directory, you’ll find the following contract named spl-token-minter.sol below, which includes:

A constructor for state variable initialization.
A print function for messages in the program logs.
A flip function to update the state variable.
A get function to retrieve the current state variable value.

Image description

Important Differences(Compared with EVM smart contract).

Compared to an EVM smart contract, there are two important differences you might notice:

The @program_id annotation:

On Solana, smart contracts are referred to as “programs”. The @program_id annotation is used to specify the on-chain address of the program.

@program_id("F1ipperKF9EfD821ZbbYjS319LXYiBmjhzkkf5a26rC") // 
Enter fullscreen mode Exit fullscreen mode

on-chain program address.

The @payer annotation:

When storing data on the Solana blockchain, a certain amount of SOL needs to be allocated to cover the storage costs and the rent(we will discuss this later in further articles). The @payer annotation specifies the user that will pay the SOL required to create the account for storing the state variable.

@payer(payer) // payer for the "data account"
constructor(address payer) {
    print("Hello, World!");
}
Enter fullscreen mode Exit fullscreen mode

Storing of state data

An important distinction between EVM smart contracts and Solana programs is how each store "state" variables/data:

EVM smart contracts can directly store state variables.
Solana on-chain programs, on the other hand, create separate accounts to hold state data. These are often referred to as "data accounts" and are "owned" by a program.

In this example, when the contract is deployed, it is deployed to the address specified in @program_id. When the constructor is called after the program is deployed, a separate account with its address is created to store the state variable, instead of being stored within the contract itself.

Step 2 Creating the solidity program

Now you can clear this all code off as we are going to build a spl-token-minter program

What it takes to mint any custom spl tokens to token address

  • Minting SPL Tokens involves a conceptually straightforward process, yet the intricacy emerges in understanding Solana's mechanism for monitoring users' balances of SPL Tokens.
  • By default, each Solana account tracks its SOL balance, the native token. However, a crucial question arises: How does every Solana account manage its balance for any possible SPL Token within the network?
  • In reality, achieving this feat directly is unfeasible. The solution necessitates the creation of distinct accounts purposefully configured for each SPL Token, known as Associated Token Accounts.
  • Imagine the scenario of introducing a new token named "JOE." To determine an individual's JOE token balance:
  • Mint the JOE tokens.
  • Establish an Associated Token Account customized for the user's wallet, designed to monitor their JOE balance.
  • Mint or transfer JOE tokens into the user's JOE Associated Token Account.
  • In essence, Associated Token Accounts operate as basic counters, directly tied to a Mint and a Wallet. Their primary function is to convey the balance linked to a specific Mint for a specific Wallet.

Write program

Design

We will create a Solana program focused on minting fungible SPL tokens. To make this happen, we'll need the right Solidity libraries that handle creating tokens, making a token mint, and sending the freshly minted tokens to a user's wallet.

Create a directory name libraries in the project root and add these files to it

No need to delve into the code just yet. Let me clarify what we're aiming for with these files: we'll be importing this code and utilizing specific methods to create and mint SPL tokens.

Create mpl_metdata.sol in the library and add code from here
mpl_metadata.sol

This library provides a way for Solidity to interact with Metaplex mpl metadata

Create spl_token.sol in the library and add code from here
spl_token.sol

This library provides a way for Solidity to interact with Solana's SPL-Token

Create system_instruction.sol in the library and add code from here
system_instruction.sol

This library provides a bridge for Solidity to interact with Solana's system instructions.

Now go over to spl-token-minter.sol file and let’s import these libraries

import "../libraries/spl_token.sol";
import "../libraries/mpl_metadata.sol";
Enter fullscreen mode Exit fullscreen mode

Using Import Directives for Solidity Files

In this code block, the import directive helps bring things from libraries. This is handy when you want to keep one definition in a file and use it in lots of other files.

These import statements let the current contract use the functions, data structures, and variables from "spl_token.sol" (Solana SPL token) and "mpl_metadata.sol" (Metaplex) files. This adds to the contract's ability to create and mint tokens.

Next, write this

@program_id("4Huxs8ujxKqT76qqfmfJXMThmo4WMsvmdiCkqzNKF5kq")
Enter fullscreen mode Exit fullscreen mode

On Solana, smart contracts are referred to as “programs”. The @program_id annotation is used to specify the on-chain address of the program.

Next, write a program

contract spl_token_minter {
@payer(payer)
    constructor() {}
}
Enter fullscreen mode Exit fullscreen mode

Within a contract constructor, you have the option to include the @payer annotation, which takes a sequence of characters as an argument. This annotation designates a Solana account responsible for funding the initialization of the contract's data account.

The syntax @payer(payer) introduces an account named payer, which becomes essential for every invocation of the constructor.

The significance of the @payer annotation:

Allocating SOL for Blockchain Data Storage

When you put data on the blockchain, you need to set aside some SOL to pay for storage costs, including rent (which we'll explain more about later in the series). The @payer annotation tells us who's responsible for giving the needed SOL to create the account that stores the data.

Next, write this code block (createTokenMint function) after the constructor in spl_token_minter contract

function createTokenMint(
        address payer, // payer account
        address mint, // mint account to be created
        address mintAuthority, // mint authority for the mint account
        address freezeAuthority, // freeze authority for the mint account
        address metadata, // metadata account to be created
        uint8 decimals, // decimals for the mint account
        string name, // name for the metadata account
        string symbol, // symbol for the metadata account
        string uri // URI for the metadata account
    ) public {
        // Invoke System Program to create a new account for the mint account and,
        // Invoke Token Program to initialize the mint account
        // Set mint authority, freeze authority, and decimals for the mint account
        SplToken.create_mint(
            payer,            // payer account
            mint,            // mint account
            mintAuthority,   // mint authority
            freezeAuthority, // freeze authority
            decimals         // decimals
        );

        // Invoke Metadata Program to create a new account for the metadata account
        MplMetadata.create_metadata_account(
            metadata, // metadata account
            mint,  // mint account
            mintAuthority, // mint authority
            payer, // payer
            payer, // update authority (of the metadata account)
            name, // name
            symbol, // symbol
            uri // URI (off-chain metadata json)
        );
    }
Enter fullscreen mode Exit fullscreen mode

Let's break down what's happening in this code block:

The createTokenMint function makes use of these parameters:

  • address payer, // payer account(who pay for the rent)
  • address mint, // mint account to be created (mint account for that specific any custom spl token)
  • address mintAuthority, // mint authority for the mint account(mint authority is account signer )
  • address freezeAuthority, // freeze authority for the mint account(same as mint authority)
  • address metadata, // metadata account to be created(metadata address used for storing metadata using metaplex program)
  • uint8 decimals, // decimals for the mint account(eth does not have decimal concept to create and mint token eth have different types of interfaces like erc-20 for fungible token and ERC721 for nft whereas in Solana is single spl token program is responsible for minting both type of tokens using decimal concept)
  • string name, // name for the metadata account(title, name of the token)
  • string symbol, // symbol for the metadata account(symbol of token)
  • string uri // uri for the metadata account(this is uri that is stored in somewhere decentralized storage providers like (ipfs) this is used for storing token logo and different types of metadata).

This is responsible for creating a new token mint and its associated metadata within the Solana blockchain ecosystem.

We use the public specifier for this function to make it accessible to everyone.

Creating a Mint Account and Setting Authorities

In this step, we're making a mint account for our token and deciding who's in charge and how many decimal places it has. We do this by using the System Program and the Token Program. It happens when we use the SplToken.create_mint call. This sets up things like who's in charge, who can freeze, and how many decimals there are for the Mint account.

SplToken.create_mint(
            payer,            // payer account
            mint,            // mint account
            mintAuthority,   // mint authority
            freezeAuthority, // freeze authority
            decimals         // decimals
        );
Enter fullscreen mode Exit fullscreen mode

Creating Metadata Account for Minted Tokens

After making the mint account, the process moves on to using the Metadata Program. This program helps set up a new account just for storing metadata linked to the minted tokens. We make this happen by using the MplMetadata.create_metadata_account call. This step takes care of things like who owns the metadata account, the name and symbol of the tokens, and a link to off-chain metadata using a URI (Uniform Resource Identifier).

// Invoke Metadata Program to create a new account for the metadata account
        MplMetadata.create_metadata_account(
            metadata, // metadata account
            mint,  // mint account
            mintAuthority, // mint authority
            payer, // payer
            payer, // update authority (of the metadata account)
            name, // name
            symbol, // symbol
            uri // uri (off-chain metadata JSON)
        );
Enter fullscreen mode Exit fullscreen mode

Now place the "mintTo" function directly under the "createTokenMint" function.

function mintTo(address mintAuthority, address tokenAccount, address mint, uint64 amount) public {
        // Mint tokens to the token account
        SplToken.mint_to(
            mint, // mint account
            tokenAccount, // token account
            mintAuthority, // mint authority
            amount // amount
        );
    }
Enter fullscreen mode Exit fullscreen mode

Here we are minting tokens to user wallet with mintTo Function

The mintTo function does something important: it mints tokens to a user's associated token account in their wallet. This helps Solana keep track of how many tokens each account has.

We create associated accounts using the token mint and minting towards a user's wallet. Here's an example:

Imagine I create the JOE token. To see how much JOE someone has, I'd have to:

Make the JOE token
Make an Associated Token Account for that person's wallet to keep track of their JOE balance
Mint or send JOE tokens to their JOE Associated Token Account

Our Solidity program is now complete, allowing us to create and send tokens to a user's wallet.

Next, we'll move to building, deploying, and running this program on the chain using the devnet cluster. Thanks to the anchor framework, this is an easy and smooth process. To do this, we just need to set up the steps for running and testing our program.

Setting up for deployment and testing

First, go to the package.json file and change this block of code

{
    "scripts": {
        "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
        "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
    },
    "dependencies": {
        "@coral-xyz/anchor": "^0.28.0",
        "@metaplex-foundation/js": "^0.19.4",
        "@solana/spl-token": "^0.3.8",
        "@solana/web3.js": "^1.78.4",
    },
    "devDependencies": {
        "@types/bn.js": "^5.1.0",
        "@types/chai": "^4.3.0",
        "@types/mocha": "^9.0.0",
        "chai": "^4.3.4",
        "mocha": "^9.0.3",
        "prettier": "^2.6.2",
        "ts-mocha": "^10.0.0",
        "typescript": "^4.3.5"
    }
}
Enter fullscreen mode Exit fullscreen mode

Save this go to the terminal and write the command-:

npm install 
Enter fullscreen mode Exit fullscreen mode

For installing these dependencies

The anchor.toml file serves as the central driver for our application. It's responsible for both app configuration and running tests.

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
Enter fullscreen mode Exit fullscreen mode

This script is an anchor.toml file responsible for running tests in test folder

Now, let's proceed with writing a test script for spl-token-minter. To do this, navigate to the test folder and open the spl-token-minter.ts file. Remove all existing code in that file.

Start with importing these programs and dependencies at the top of the file

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { SplTokenMinter } from "../target/types/spl_token_minter";
import { PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY,LAMPORTS_PER_SOL,ParsedAccountData } from "@solana/web3.js";
import { Metaplex } from "@metaplex-foundation/js";
import { assert } from "chai"
import {
  ASSOCIATED_TOKEN_PROGRAM_ID,
  getOrCreateAssociatedTokenAccount,
  TOKEN_PROGRAM_ID,
  getAccount
} from "@solana/spl-token";
Enter fullscreen mode Exit fullscreen mode

Here are the imported modules, classes, and tools that are utilized in test for creating and sending SPL tokens to a user's associated token account:

  1. import * as anchor from "@coral-xyz/anchor";: This line brings in the entire Anchor library, enabling us to reference it as "anchor." Anchor is a valuable tool for developing Solana programs.

  2. import { Program } from "@coral-xyz/anchor";: This import specifically fetches the Program class from the Anchor library. The Program class facilitates the creation and interaction with Solana programs.

  3. import { SplTokenMinter } from "../target/types/spl_token_minter";: This import statement obtains the SplTokenMinter class from the spl_token_minter module, which will be explored further during the building process.

  4. import { PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY, LAMPORTS_PER_SOL, ParsedAccountData } from "@solana/web3.js";: In this line, we import classes such as PublicKey, SystemProgram, and others from the @solana/web3.js library. These classes are valuable for managing Solana's public keys, system program, and rent-related aspects.

  5. import { Metaplex } from "@metaplex-foundation/js";: This import statement brings in the Metaplex class from the @metaplex-foundation/js library. Metaplex is likely used for tasks related to NFTs and metadata.

  6. import { ASSOCIATED_TOKEN_PROGRAM_ID, getOrCreateAssociatedTokenAccount, TOKEN_PROGRAM_ID, getAccount } from "@solana/spl-token";: These imports originate from the @solana/spl-token library, which is dedicated to Solana's token standard (SPL tokens). They are instrumental in managing associated token accounts and handling various token-related tasks.

Now describe the test for spl-token-minter

describe("spl-token-minter", () => {
});
Enter fullscreen mode Exit fullscreen mode

In this section, we will discuss and thoroughly test the functionality of the "spl-token-minter" program. Our testing process will include the creation of various test cases, assertions, and expectations to ensure the program's accurate behaviour. These tests will encompass a variety of scenarios, including token creation and minting.

For this, you need to first set the requirements for the test
Let’s write this

// Configure the client to use the local cluster.
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);

  // Generate a new keypair for the data account for the program
  const dataAccount = anchor.web3.Keypair.generate();
  // Generate a mint keypair
  const mintKeypair = anchor.web3.Keypair.generate();
  const wallet = provider.wallet as anchor.Wallet;
  const connection = provider.connection;

  const program = anchor.workspace.SplTokenMinter as Program<SplTokenMinter>;

  // Metadata for the Token
  const tokenTitle = "Solana pro";
  const tokenSymbol = "Gold";
  const tokenUri =
    "https://raw.githubusercontent.com/solana-developers/program-examples/new-examples/tokens/tokens/.assets/spl-token.json";
Enter fullscreen mode Exit fullscreen mode

Here we are configuring and initiating the setup for testing our spl-token-minter Solana program.

  • Configuration and Provider Setup: First, we connect to the local cluster and set up the provider using AnchorProvider.env(). This provider is then assigned with anchor.setProvider(provider) The Provider is an abstraction of a connection to the Solana network, typically consisting of a Connection, Wallet, and a preflight commitment.
  • Generating Key Pairs: To store the program's state, we create a new data account using a key pair. We also generate a mintKeypair, likely for the minting process. We set up wallet and connection variables too.
  • Initializing the Program: We use anchor.workspace.SplTokenMinter to make an instance of the program as Program. This prepares the SplTokenMinter program from the workspace, The program is an abstraction that combines the Provider, idl, and the programID (which is generated when the program is built) and allows us to call RPC methods against our program.
  • Defining Metadata: We provide info about the token's metadata. This means giving the token a title, symbol, and URI. These metadata bits are super important for describing what the token is all about.

Now to initialize the data account write this

it("Is initialized!", async () => {
    // Initialize data account for the program, which is required by Solang
    const tx = await program.methods
      .new()
      .accounts({ dataAccount: dataAccount.publicKey })
      .signers([dataAccount])
      .rpc();
    console.log("Your transaction signature", tx);
  });
Enter fullscreen mode Exit fullscreen mode

Here we initialise the data account for the program, a crucial requirement for Solang, the Solidity compiler for Solana. The initialization process is conducted using the new() method from the program.methods object

Here we specify the dataAccount as an account parameter and the dataAccount keypair as a signer. This ensures that the transaction possesses the necessary authorization and input parameters for the initialization operation.

Now create spl token

it("Create an SPL Token!", async () => {
    // Get the metadata address for the mint
    const metaplex = Metaplex.make(connection);
    const metadataAddress = await metaplex
      .nfts()
      .pdas()
      .metadata({ mint: mintKeypair.publicKey });

    // Create the token mint
    const tx = await program.methods
      .createTokenMint(
        wallet.publicKey, // payer
        mintKeypair.publicKey, // mint
        wallet.publicKey, // mint authority
        wallet.publicKey, // freeze authority
        metadataAddress, // metadata address
        9, // decimals
        tokenTitle, // token name
        tokenSymbol, // token symbol
        tokenUri // token uri
      )
      .accounts({ dataAccount: dataAccount.publicKey })
      .remainingAccounts([
        {
          pubkey: wallet.publicKey,
          isWritable: true,
          isSigner: true,
        },
        { pubkey: mintKeypair.publicKey, isWritable: true, isSigner: true },
        {
          pubkey: new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"), // Metadata program id
          isWritable: false,
          isSigner: false,
        },
        { pubkey: metadataAddress, isWritable: true, isSigner: false },
        { pubkey: SystemProgram.programId, isWritable: false, isSigner: false },
        { pubkey: SYSVAR_RENT_PUBKEY, isWritable: false, isSigner: false },
      ])
      .signers([mintKeypair])
      .rpc({ skipPreflight: true });
    console.log("Your transaction signature", tx);
  });
Enter fullscreen mode Exit fullscreen mode

Here we create an custom spl token named solana pro and have title gold

For this first, we create the metaplex connection using this

const metaplex = Metaplex.make(connection);
Enter fullscreen mode Exit fullscreen mode

Get the metadata address for our token mint

// Get the metadata address for the mint
const metadataAddress = await metaplex
      .nfts()
      .pdas()
      .metadata({ mint: mintKeypair.publicKey });

Enter fullscreen mode Exit fullscreen mode

Let's look at how we get metadata details for a specific mint. We use mintKeypair.publicKey to say which mint we're looking at. When we run this code, we find the metadata address connected to that mint and save it in metadataAddress.

Next, we use the createtokenmint method from our spl-token-minter program to make a token mint. We give it necessary accounts, data, and a decimal of 9 to make sure we create a fungible token mint. We also give accounts or signers for the methods.

After that, we execute the transaction using .rpc() and set skipPreflight to true for an async process. We get the transaction signature (tx) for reference, and the results show up in the console.

Now mint some solana pro tokens to our wallet

it("Mint some tokens to your wallet!", async () => {
    // Wallet's associated token account address for mint
    const tokenAccount = await getOrCreateAssociatedTokenAccount(
      connection,
      wallet.payer, // payer
      mintKeypair.publicKey, // mint
      wallet.publicKey // owner
    );

    const tx = await program.methods
      .mintTo(
        wallet.publicKey, // payer
        tokenAccount.address, // associated token account address
        mintKeypair.publicKey, // mint
        new anchor.BN(15900000000) // amount to mint
      )
      .accounts({ dataAccount: dataAccount.publicKey })
      .remainingAccounts([
        {
          pubkey: wallet.publicKey,
          isWritable: true,
          isSigner: true,
        },
        { pubkey: tokenAccount.address, isWritable: true, isSigner: false },
        { pubkey: mintKeypair.publicKey, isWritable: true, isSigner: false },
        {
          pubkey: SystemProgram.programId,
          isWritable: false,
          isSigner: false,
        },
        { pubkey: TOKEN_PROGRAM_ID, isWritable: false, isSigner: false },
        {
          pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
          isWritable: false,
          isSigner: false,
        },
      ])
      .rpc({ skipPreflight: true });
    console.log("Your transaction signature", tx);
  });
Enter fullscreen mode Exit fullscreen mode

Initially, we establish an associated token account by leveraging the token mint and associating it with the user's wallet address. To accomplish this, we employ the getOrCreateAssociatedTokenAccount() method. This method requires several parameters, including the connection, the payer (associated with the wallet), the mint, and the owner (the wallet's owner).

Next, we make a transaction by using the mintTo method from our spl-token-minter program. We give it important stuff like the wallet, the address of the associated token account, the mint key pair, and the number of tokens we want to mint (like Bn). We also include the accounts and signers, making sure we provide all the accounts needed to mint tokens to the user's wallet.

Now let’s test and make an assertion if our tokens are minted or not by getting the token amount balance

And make assert.equal with 159 tokens

// Get the minted token amount on the associated token account
    const tokenAmount = (await getAccount(connection, tokenAccount.address)).amount;
    console.log("tokenAmount", tokenAmount);
    // Converting tokenAmount to a regular number using Number()
    let tokens = Number(tokenAmount);
    console.log("minted token amounts", tokens / LAMPORTS_PER_SOL);
    assert.equal(tokens / LAMPORTS_PER_SOL, 159);
Enter fullscreen mode Exit fullscreen mode

Aha, All test writing is done

Now we can move forward to build our program to generate program types and idl

open anchor.toml file
and add this block of code in the end of the file

[[test.validator.clone]]
address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
Enter fullscreen mode Exit fullscreen mode

This is the metadata program id for Metaplex

It’s time to build our spl-token-minter program

Building program
Go to the terminal in project root
and write command

anchor build
Enter fullscreen mode Exit fullscreen mode

This command will generate a target folder in the project root and generate idl and types of our Solana program to interact with the client side using rpc methods

Now open a new terminal and check our configuration By writing this command

solana config get
Enter fullscreen mode Exit fullscreen mode

After running this you will see something like this

Config File: ~/.config/solana/cli/config.yml
RPC URL: https://api.devnet.solana.com
WebSocket URL: wss://api.devnet.solana.com/ (computed)
Keypair Path: ~/.config/solana/id.json
Commitment: confirmed
Enter fullscreen mode Exit fullscreen mode

For Devnet, please note that it uses an RPC URL, and your wallet's default location is specified by the keypairpath. Make sure to generate a wallet keypair for program deployment.
You can set devnet for cluster
by writing this command

solana config set --url https://api.devnet.solana.com
Enter fullscreen mode Exit fullscreen mode

by writing this command you can generate a new keypair

solana-keygen new
Enter fullscreen mode Exit fullscreen mode

Now you have a wallet
You can check the address(pubkey) and balance of this account using these commands

solana address 
solana balance
Enter fullscreen mode Exit fullscreen mode

If you have some devnet sol then it is okay for deployment if it is not then Get some devnet airdrop by writing this command

solana airdrop 4
Enter fullscreen mode Exit fullscreen mode

Deploying the program
Open the terminal in the new tab and start the network cluster
by writing this command

solana-test-validator
Enter fullscreen mode Exit fullscreen mode

This will start a devnet cluster in the system
Next, deploy the program using

anchor deploy
Enter fullscreen mode Exit fullscreen mode

you will get something like this

Deploying cluster: https://api.devnet.solana.com
Upgrade authority: ~.config/solana/id.json
Deploying program "spl_token_minter"...
Program path: ~/Desktop/demo/spl-token-minter/target/deploy/spl_token_minter.so...
Program Id: 3iCbv94ivHb4of8NEeikMbk2kckLVSDmUEprpQZ1cBUw
Deploy success
Enter fullscreen mode Exit fullscreen mode

Our program is now deployed on the Devnet cluster. It possesses program upgrade authority, which is granted to our wallet. To proceed, we need to obtain the program ID of our deployed program on-chain.

Once we have the program ID, update it in our program (solidity file) as well as the anchor.toml file. Make sure to replace the existing program ID with the new one in both files.

Build the program again with the command

anchor build
Enter fullscreen mode Exit fullscreen mode

Testing Program

Before testing check dependencies and run:

yarn install 
Enter fullscreen mode Exit fullscreen mode

Now we are doing testing with the command

anchor test
Enter fullscreen mode Exit fullscreen mode

after this, you will get results like this

spl-token-minter
Your transaction signature 5KzehPTbYCuUtwQzbigvTnwnkiuNcWuXPtuWSMGMVc914xcV8ddFLV3BqEr3hMsbS9ga6SJjzEDcjRMZhGi6KgvJ
✔ Is initialized! (4616ms)
Your transaction signature ePvkJFcHdgjbWnYh1e5nab57bv32TyviXgrfNi1QQFsHytXgcJvuneD9BzmrEv4rcMu1KXQSr2hpJ2Gsqzf2TAm
✔ Create an SPL Token! (1466ms)
Your transaction signature 4qdXPTQArZTyFq5mCtdyJVx2Hu1NxeYnY1wwGCVpHy91SZjdkngvRYVWDJvmaTigUEshACFbKMULojfChMxcNp85
minted token amounts **156**
✔ Mint some tokens to your wallet! (6090ms)
3 passing (12s)
Enter fullscreen mode Exit fullscreen mode

All test is passed and we can see minted tokens in Solana Explorer

Get the workspace program wallet address using this command Write in the terminal

solana address
Enter fullscreen mode Exit fullscreen mode

Now goto this link
https://explorer.solana.com/?cluster=devnet
Paste wallet address in the search explorer
you will get something like this

solana explorer

This will give you info about the wallet
like balance transactions and token holdings

To see our minted tokens click on tokens then you will see something like this

Image description

Find out minted tokens with the title solana pro that have 159 GM supply
Click on this you will see something like this

Image description

All the information related to minted tokens is here
that’s it

This is how you can mint fungible tokens on Solana using solidity(solang).

Conclusion

This article, delved deep into different aspects of Solana program development using Solidity. It laid a solid foundation by highlighting the differences in behaviour between Solidity programs on the Solana and Ethereum platforms.

To enhance clarity and organization, the article was methodically partitioned into two distinct sections, facilitating a well-structured grasp of the subject matter for readers.

The initial section was dedicated to imparting a fundamental comprehension of Solidity Solana programs within the context of the Solana ecosystem. This segment intricately elucidated multiple aspects of Solana blockchain development utilizing the Solidity (Solang) approach.

The subsequent section offered an engaging hands-on experience by meticulously detailing the creation of a "spl-token-minter" program using Solidity. This practical example empowered readers with the ability to practically apply the previously discussed concepts.

As the series advances, the upcoming article will pivot its focus towards crafting a Flip program using CPI(Cross-program-invocation) and PDA(program-derived address) in Solidity (Solang), thereby deepening and expanding the scope of knowledge conveyed throughout this series.

Further learning resources

Top comments (0)