This section focuses on creating a minting program using Solidity. You'll learn how to set up and run tests for your project, create and mint SPL tokens using Solidity, and write test cases for your Solidity code.
illustration
b
Setting Up Tests for Minting Program

In this lesson, you will:

  • Prepare your project for deployment.
  • Set up and execute tests for your project.
  • Create and mint SPL tokens using Solidity.

We'll set up the development environment by installing the necessary dependencies in the package.json file. We'll also create and mint SPL tokens for our program and write test cases for our Solidity code.

Firstly, update your package.json file with the following dependencies:

{
  "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"
  }
}

After updating, run npm install in your terminal to install these dependencies.

The anchor.toml file is central to your application, particularly for configuring and running tests. Update it with the following script:

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

This script in the anchor.toml file is set up to run tests located in the test folder.

Go to the test folder and open the spl-token-minter.ts file. Start by removing any existing code, then import the necessary programs and dependencies:

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,
} 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";

These imports bring in the necessary tools and libraries for creating and testing the functionality of the spl-token-minter program, including token creation and minting.

Set up the test environment:

const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);

const dataAccount = anchor.web3.Keypair.generate();
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>;

const tokenTitle = "Solana pro";
const tokenSymbol = "Gold";
const tokenUri = "https://example.com/spl-token.json";

This configuration includes setting up the provider, generating keypairs, initializing the program, and defining metadata for the token.

To initialize a data account for our Solang program, we utilize the new() method within program.methods. In this process, dataAccount is designated as an account parameter, and its keypair is used as a signer, ensuring proper authorization and input for the initialization.

Moving on to SPL token creation, the script below guides us through the process:

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

  // Crafting 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: 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,
      },
    ])
    .signers([mintKeypair])
    .rpc({ skipPreflight: true });
  console.log("Your transaction signature", tx);
});

This script crafts a custom SPL token, "Solana Pro," titled "Gold." It begins with creating a Metaplex connection and retrieving the metadata address for the token mint and a decimal of 9 to make sure we create a Fungible Token mint., crucial steps for the subsequent creation process.

To mint "Solana Pro" tokens to your wallet, follow this script:

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

  // Executing the minting process
  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);
});

In this part, we first establish an associated token account that connects the token mint to the user's wallet address. This is done using the getOrCreateAssociatedTokenAccount() function, which takes the connection, payer (associated with the wallet), mint, and the owner (wallet's owner) as parameters.

After setting up the token account, we proceed to mint tokens using the mintTo method from our spl-token-minter program. This method requires important parameters like the wallet, the address of the associated token account, the mint keypair, and the number of tokens to mint. It also includes all necessary accounts and signers to facilitate the minting process.

Finally, to confirm that our tokens have been successfully minted, we check the token balance:

// Verify the minted token amount

const tokenAmount = (await getAccount(connection, tokenAccount.address)).amount;
console.log("tokenAmount", tokenAmount);

// Converting tokenAmount to a regular number for validation

let tokens = Number(tokenAmount);
console.log("minted token amounts", tokens / LAMPORTS_PER_SOL);
assert.equal(tokens / LAMPORTS_PER_SOL, 159);

This script retrieves the token amount from the associated token account, converts it into a regular number, and then displays the minted token amounts for verification. An assertion is used to validate that the expected number of tokens has been minted.

Lastly, update your anchor.toml file by appending this configuration at the end:

[[test.validator.clone]]
address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"

This line specifies the metadata program ID for Metaplex, essential for the proper functioning of your program. With these steps completed, your program is set up for further development, including building, deploying, and testing, which will be covered in the subsequent lesson.