hello@akshatsharma.in

Creating a Token on Solana - 23/06/2023

Creating a Token on Solana

The Solana blockchain has emerged as a powerful platform for decentralised applications and digital asset creation. This article will serve as a comprehensive guide to help you navigate the process of creating your own token on the Solana network.

In this article, we will walk you through the essential steps and concepts involved in token creation on Solana. We’ll cover everything from setting up a development environment to deploying and managing your token.

Tokens on Solana

Tokens on Solana are somewhat different from the ones on other chains. They are made and managed by the Solana Program Library (aka SPL). The source code for SPL is publicly available on GitHub. There are widely two types of Tokens - NFTs and Regular Tokens (memecoins). Both of them come under the SPL Token Program.

As everything on the Solana Blockchain is an account, let’s first understand the working of SPL Tokens.

My First Board-2.jpg

		A High-Level overview of the management of Tokens on Solana.

Let’s go through each type of account:

1. Wallet Account:

2. Token Account:

Token on Solana.jpg

3. Mint Account:

Token on Solana-2.jpg

Minting a Token:

We will be using Metaplex SDK to mint our token and upload our token Metadata to Areave.

Pre Requisites:

To successfully mint your own token on Solana, you’ll need:

1. Setting Up Environment:

Create a new project directory in your terminal with the name of your token:

mkdir <name>
cd <name>

Let’s name our token Avatar. You can go with whatever name you like. Open the folder in VS Code by the command:

code .

Screenshot 2023-06-01 at 5.50.55 PM.png

Now in the VS Code’s integrated terminal, or your local terminal, run

npm init --yes

to initialise an NPM Project in the directory.

Screenshot 2023-06-01 at 5.55.08 PM.png

Also create a tsconfig.json by:

tsc --init

This initialises a TypeScript project into the directory.

Screenshot 2023-06-01 at 5.57.12 PM.png

Now, let’s install the SDKs that will help us work with the Metaplex and Solana:

npm install @solana/web3.js @metaplex-foundation/mpl-token-metadata @metaplex-foundation/js @solana/spl-token

Screenshot 2023-06-01 at 5.59.23 PM.png

If everything has been good so far, your directory will look something like this:

Screenshot 2023-06-01 at 6.00.02 PM.png

2. Creating a Wallet Account:

As discussed earlier, we need an account to take control of our token. We are assuming that you do not have any wallet setup on the CLI. So, let’s create one using Solana’s web3.js SDK and airdrop some funds for the network fees.

Now, in the same directory, create a new file named wallet.ts.

Let’s first import the SDKs into our file.

We are importing the Solana’s web3.js sdk to interact with the Solana Network and FileSystem (fs) to save the wallet onto our local system.

import { Keypair, LAMPORTS_PER_SOL, Connection } from "@solana/web3.js";
import * as fs from "fs";

Now, let’s make a connection to the Solana Devnet. In the same wallet.ts file, add this below the imports:

const endpoint = "https://api.devnet.solana.com";
const solanaConnection = new Connection(endpoint);

Let’s generate a new Solana wallet:

const keypair = Keypair.generate();
console.log(
  `Generated a Wallet with PublicKey: `,
  keypair.publicKey.toString()
);

const secret_array = keypair.secretKey
  .toString()
  .split(",")
  .map((value) => Number(value));

const secret = JSON.stringify(secret_array); //Convert to JSON string

fs.writeFile("wallet.json", secret, "utf8", function (err) {
  if (err) throw err;
  console.log("Wrote secret key to wallet.json.");
});

This code will create a new keypair using the Keypair object from the web3.js SDK and display the PublicKey of the generated wallet. It will also create a wallet.json file containing the Private Key of the generated Wallet.

Now, to pay for the Network Fees, let’s get some Devnet SOL airdropped to our wallet:

(async () => {
  const airrdropSignature = solanaConnection.requestAirdrop(
    keypair.publicKey,
    LAMPORTS_PER_SOL
  );
  try {
    const txId = await airdropSignature;
    console.log(`Airdropped 1 SOL. Transaction Id: ${txId}`);
    console.log(`https://explorer.solana.com/tx/${txId}?cluster=devnet`);
  } catch (err) {
    console.log(err);
  }
})();

Now, run the script which we have created so far:

ts-node wallet.ts

If you have been following the tutorial so far, you will get output like this into your terminal:

Screenshot 2023-06-01 at 6.15.55 PM.png

You will also see a file named wallet.json has been created into the directory.

Screenshot 2023-06-01 at 6.17.29 PM.png

3. Minting our token:

Before creating the minting script, first go to tsconfig.json and uncomment or add this line:

(You can use Ctrl/Cmd + F to find the line)

"resolveJsonModule": true,

Now, we are good to go! Create a file named mint.ts and add the imports:

import {
  Transaction,
  SystemProgram,
  Keypair,
  Connection,
  PublicKey,
  sendAndConfirmTransaction,
} from "@solana/web3.js";
import {
  MINT_SIZE,
  TOKEN_PROGRAM_ID,
  createInitializeMintInstruction,
  getMinimumBalanceForRentExemptMint,
  getAssociatedTokenAddress,
  createAssociatedTokenAccountInstruction,
  createMintToInstruction,
} from "@solana/spl-token";
import {
  DataV2,
  createCreateMetadataAccountV3Instruction,
} from "@metaplex-foundation/mpl-token-metadata";
import {
  bundlrStorage,
  keypairIdentity,
  Metaplex,
  UploadMetadataInput,
} from "@metaplex-foundation/js";
import secret from "./wallet.json";

Just below the imports, make a connection to the Solana devnet:

const endpoint = "https://api.devnet.solana.com/";
const solanaConnection = new Connection(endpoint);
const userWallet = Keypair.fromSecretKey(new Uint8Array(secret));
const metaplex = Metaplex.make(solanaConnection)
  .use(keypairIdentity(userWallet))
  .use(
    bundlrStorage({
      address: "https://devnet.bundlr.network",
      providerUrl: endpoint,
      timeout: 60000,
    })
  );

Now, create the config for out token. Below the connection, add

const MINT_CONFIG = {
  numDecimals: 2,
  numberTokens: 100,
};

You are free to use any number of decimals and number of tokens. I am going with 2 decimals and 100 tokens.

Now, add the token metadata part. This will contain a logo, name, description for your token:

const MY_TOKEN_METADATA: UploadMetadataInput = {
  name: "Avatar",
  symbol: "AVT",
  description: "Token for Avatar",
  image: "https://i.ibb.co/PtRMT08/avatar-orange.jpg", //add public URL to image you'd like to use
};

We now need to create the on-chain metadata:

const ON_CHAIN_METADATA = {
  name: MY_TOKEN_METADATA.name,
  symbol: MY_TOKEN_METADATA.symbol,
  uri: "TO_UPDATE_LATER",
  sellerFeeBasisPoints: 0,
  creators: null,
  collection: null,
  uses: null,
} as DataV2;

Till now, we have created the metadata. Now, it’s the time to get On Chain. We use Bundlr to upload the metadata to Arweave.

const uploadMetadata = async (
  tokenMetadata: UploadMetadataInput
): Promise<string> => {
  //Upload to Arweave
  const { uri } = await metaplex.nfts().uploadMetadata(tokenMetadata);
  console.log(`Arweave URL: `, uri);
  return uri;
};

The next step is to create a mint transaction. Create a function named createNewMintTransaction. We also need these 3 values further:

  1. getMinimumBalanceForRentExemptMint: Number of required Lamports we need to pay for our Token Mint account.
  2. findMetadataPDA: Get the Metaplex program derived address (“PDA”) for our token mint, using the Metaplex method.
  3. createAssociatedTokenAccount: This is needed to get the destination owner wallet address.
const createNewMintTransaction = async (connection: Connection, payer: Keypair, mintKeypair: Keypair, destinationWallet: PublicKey, mintAuthority: PublicKey, freezeAuthority: PublicKey) => {
    //Get the minimum lamport balance to create a new account and avoid rent payments
    const requiredBalance = await getMinimumBalanceForRentExemptMint(connection);
    //metadata account associated with mint
    const metadataPDA = await metaplex.nfts().pdas().metadata({ mint: mintKeypair.publicKey });
    //get associated token account of your wallet
    const tokenATA = await getAssociatedTokenAddress(mintKeypair.publicKey, destinationWallet);

Now, let’s create our Transaction. Solana allows you to append multiple transactions into a single one. Yes, Only Possible On Solana.

Create a new variable named, createNewTokenTransactionand add these 5 instructions to it:

const createNewTokenTransaction = new Transaction().add(
        SystemProgram.createAccount({
            fromPubkey: payer.publicKey,
            newAccountPubkey: mintKeypair.publicKey,
            space: MINT_SIZE,
            lamports: requiredBalance,
            programId: TOKEN_PROGRAM_ID,
        }),
        createInitializeMintInstruction(
            mintKeypair.publicKey, //Mint Address
            MINT_CONFIG.numDecimals, //Number of Decimals of New mint
            mintAuthority, //Mint Authority
            freezeAuthority, //Freeze Authority
            TOKEN_PROGRAM_ID),
        createAssociatedTokenAccountInstruction(
            payer.publicKey, //Payer
            tokenATA, //Associated token account
            payer.publicKey, //token owner
            mintKeypair.publicKey, //Mint
        ),
        createMintToInstruction(
            mintKeypair.publicKey, //Mint
            tokenATA, //Destination Token Account
            mintAuthority, //Authority
            MINT_CONFIG.numberTokens * Math.pow(10, MINT_CONFIG.numDecimals),//number of tokens
        ),
        createCreateMetadataAccountV3Instruction({
            metadata: metadataPDA,
            mint: mintKeypair.publicKey,
            mintAuthority: mintAuthority,
            payer: payer.publicKey,
            updateAuthority: mintAuthority,
        }, {
            createMetadataAccountArgsV3: {
                data: ON_CHAIN_METADATA,
                isMutable: true,
                collectionDetails: null
            }
        })
    );

    return createNewTokenTransaction;
}
  1. createAccount: Creating new mint account.
  2. createInitializeMintInstruction: Initialize the new mint account.
  3. createAssociatedTokenAccountInstruction: Create a new token account for the new mint in your destination wallet.
  4. createMintToInstruction: Instruction that will define what tokens to mint, where to mint them to, and how many to mint.
  5. createCreateMetadataAccountV2Instruction: Associate our token meta data with this mint.

A lot of instructions! That may have been overwhelming. Just go through the comments and you will understand the stuff we have been trying to do.

The only thing left now is to execute the functions we have been creating so far. For that, we need a main function. Let’s create that and get our own token.

Now, below this function, add that main() function as well:

const main = async () => {
  console.log(`---STEP 1: Uploading MetaData---`);
  const userWallet = Keypair.fromSecretKey(new Uint8Array(secret));
  let metadataUri = await uploadMetadata(MY_TOKEN_METADATA);
  ON_CHAIN_METADATA.uri = metadataUri;

  console.log(`---STEP 2: Creating Mint Transaction---`);
  let mintKeypair = Keypair.generate();
  console.log(`New Mint Address: `, mintKeypair.publicKey.toString());

  const newMintTransaction: Transaction = await createNewMintTransaction(
    solanaConnection,
    userWallet,
    mintKeypair,
    userWallet.publicKey,
    userWallet.publicKey,
    userWallet.publicKey
  );

  console.log(`---STEP 3: Executing Mint Transaction---`);
  let { lastValidBlockHeight, blockhash } =
    await solanaConnection.getLatestBlockhash("finalized");
  newMintTransaction.recentBlockhash = blockhash;
  newMintTransaction.lastValidBlockHeight = lastValidBlockHeight;
  newMintTransaction.feePayer = userWallet.publicKey;
  const transactionId = await sendAndConfirmTransaction(
    solanaConnection,
    newMintTransaction,
    [userWallet, mintKeypair]
  );
  console.log(`Transaction ID: `, transactionId);
  console.log(
    `Succesfully minted ${MINT_CONFIG.numberTokens} ${
      ON_CHAIN_METADATA.symbol
    } to ${userWallet.publicKey.toString()}.`
  );
  console.log(
    `View Transaction: https://explorer.solana.com/tx/${transactionId}?cluster=devnet`
  );
  console.log(
    `View Token Mint: https://explorer.solana.com/address/${mintKeypair.publicKey.toString()}?cluster=devnet`
  );
};

main();

The final step is here! Running our code.

TypeScript uses ts-node to compile and execute the code. Go into your terminal, and invoke ts-node :

ts-node mint.ts

You will get an output like this, with your unique URL on Arweave.

Screenshot 2023-06-01 at 7.27.35 PM.png

Hit up the last link and you can see your token on the explorer. Here’s mine:

Untitled

https://explorer.solana.com/address/3gRYAwYJyCE7HP6u5jpXV4Ufpf178nT3w24Kb5952h4c?cluster=devnet

Here’s the final version of both the codes:

  1. wallet.ts
  2. mint.ts

Congratulations on minting your own token on Solana.