Summary
- You may recall NZT is the ‘native token’ of Nexis Native Chain. All other tokens, fungible and non-fungible tokens (NFTs), are called SPL Tokens.
- The Token Program contains instructions for creating and interacting with SPL Tokens.
- Token Mints are accounts that define a specific token. This includes information about the token itself (like how many decimals it has), the account allowed to mint more tokens (called the mint authority), and where to find more information about the token like a description, image, etc. The mint authority can use the token mint to make more tokens!
- Token Accounts hold tokens of a specific Token Mint. For most users, their balances of each token mint are stored in Associated Token Accounts - accounts with addresses made from their wallet address and the token’s mint.
- Creating Token Mints and Token Accounts requires allocating rent in NZT. The rent for a Token Account can be refunded when the account is closed. Additionally, tokens created with the Token Extensions Program can also close Token Mints.
Lesson
The Token Program is one of many programs made available by the Nexis Native Chain Program Library (SPL). It contains instructions for creating and interacting with SPL Tokens. These tokens represent all non-native (i.e. not NZT) tokens on the Nexis Native Chain network. This lesson will focus on the basics of creating and managing a new SPL Token using the Token Program:- Creating a new Token Mint
- Creating Token Accounts
- Minting
- Transferring tokens from one holder to another
@nexis-network/spl-token Javascript library.
Token Mint
To create a new SPL Token you first have to create a Token Mint. A Token Mint is an account that holds data about a specific token. As an example, let’s look at USD Coin (USDC) on the Nexis Native Chain Explorer. USDC’s Token Mint address isEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.
With the explorer, we can see the particular details about USDC’s Token Mint
such as the current supply of tokens, the addresses of the mint and freeze
authorities, and the decimal precision of the token:
To create a new Token Mint, you need to send the right transaction instructions
to the Token Program. To do this, we’ll use the createMint function from
@nexis-network/spl-token.
createMint function returns the publicKey of the new token mint. This
function requires the following arguments:
connection- the JSON-RPC connection to the clusterpayer- the public key of the payer for the transactionmintAuthority- the account that is authorized to do the actual minting of tokens from the token mint.freezeAuthority- an account authorized to freeze the tokens in a token account. If freezing is not a desired attribute, the parameter can be set to nulldecimals- specifies the desired decimal precision of the token
createMint function. However, if you were to build a
website to allow users to create a new token mint, you would need to do so with
the user’s secret key without making them expose it to the browser. In that
case, you would want to build and submit a transaction with the right
instructions.
Under the hood, the createMint function is simply creating a transaction that
contains two instructions:
- Create a new account
- Initialize a new mint
Rent and Rent Exemption
Note that the first line in the function body of the previous code snippet contains a call togetMinimumBalanceForRentExemptMint, the result of which is
passed into the createAccount function. This is part of account initialization
called rent exemption.
Until recently, all accounts on Nexis Native Chain were required to do one of the following
to avoid being deallocated:
- Pay rent at specific intervals
- Deposit enough NZT upon initialization to be considered rent-exempt
getMinimumBalanceForRentExemptMint from the @nexis-network/spl-token library.
However, this concept applies to all accounts and you can use the more generic
getMinimumBalanceForRentExemption method on Connection for other accounts
you may need to create.
Token Account
Before you can mint tokens (issue new supply), you need a Token Account to hold the newly issued tokens. A Token Account holds tokens of a specific “mint” and has a specified “owner” of the account. Only the owner is authorized to decrease the Token Account balance (transfer, burn, etc.) while anyone can send tokens to the Token Account to increase its balance. You can use thespl-token library’s createAccount function to create the new
Token Account:
createAccount function returns the publicKey of the new token account.
This function requires the following arguments:
connection- the JSON-RPC connection to the clusterpayer- the account of the payer for the transactionmint- the token mint that the new token account is associated withowner- the account of the owner of the new token accountkeypair- this is an optional parameter for specifying the new token account address. If no keypair is provided, thecreateAccountfunction defaults to a derivation from the associatedmintandowneraccounts.
createAccount function is different from the
createAccount function shown above when we looked under the hood of the
createMint function. Previously we used the createAccount function on
SystemProgram to return the instruction for creating all accounts. The
createAccount function here is a helper function in the spl-token library
that submits a transaction with two instructions. The first creates the account
and the second initializes the account as a Token Account.
Like with creating a Token Mint, if we needed to build the transaction for
createAccount manually we could duplicate what the function is doing under the
hood:
- Use
getMintto retrieve the data associated with themint - Use
getAccountLenForMintto calculate the space needed for the token account - Use
getMinimumBalanceForRentExemptionto calculate the lamports needed for rent exemption - Create a new transaction using
SystemProgram.createAccountandcreateInitializeAccountInstruction. Note that thiscreateAccountis from@nexis-network/web3.jsand used to create a generic new account. ThecreateInitializeAccountInstructionuses this new account to initialize the new token account
Associated Token Accounts
An Associated Token Account stores tokens in an address made from:- The owner’s public key
- The token mint
publicKey for a specific token.
There are other ways to create token accounts (particularly for onchain
programs), but nearly all the time you want to store tokens for a user, you’ll
want it to be an Associated Token Account. Even if the user doesn’t already have
an ATA for that token, you can simply find the address and make the account for
them.
spl-token library’s
createAssociatedTokenAccount function.
publicKey of the new associated token account and
requires the following arguments:
connection- the JSON-RPC connection to the clusterpayer- the account of the payer for the transactionmint- the token mint that the new token account is associated withowner- the account of the owner of the new token account
getOrCreateAssociatedTokenAccount to get the Token Account
associated with a given address or create it if it doesn’t exist. For example,
if you were writing code to airdrop tokens to a given user, you’d likely use
this function to ensure that the token account associated with the given user
gets created if it doesn’t already exist.
Under the hood, createAssociatedTokenAccount is doing two things:
- Using
getAssociatedTokenAddressto derive the associated token account address from themintandowner - Building a transaction using instructions from
createAssociatedTokenAccountInstruction
Mint Tokens
Minting tokens is the process of issuing new tokens into circulation. When you mint tokens, you increase the supply of the token mint and deposit the newly minted tokens into a token account. Only the mint authority of a token mint is allowed to mint new tokens. To mint tokens using thespl-token library, you can use the mintTo function.
mintTo function returns a TransactionSignature that can be viewed on the
Nexis Native Chain Explorer. The mintTo function requires the following arguments:
connection- the JSON-RPC connection to the clusterpayer- the account of the payer for the transactionmint- the token mint that the new token account is associated withdestination- the token account that tokens will be minted toauthority- the account authorized to mint tokensamount- the raw amount of tokens to mint outside of decimals, e.g. if Scrooge Coin mint’s decimals property was set to 2 then to get 1 full Scrooge Coin you would need to set this property to 100
mintTo function simply creates a transaction with the
instructions obtained from the createMintToInstruction function.
Transfer Tokens
SPL Token transfers require both the sender and receiver to have token accounts for the mint of the tokens being transferred. The tokens are transferred from the sender’s token account to the receiver’s token account. You can usegetOrCreateAssociatedTokenAccount when obtaining the receiver’s
associated token account to ensure their token account exists before the
transfer. If the account doesn’t exist already, this function will create it and
the payer on the transaction will be debited the lamports required for the
account creation.
Once you know the receiver’s token account address, you transfer tokens using
the spl-token library’s transfer function.
transfer function returns a TransactionSignature that can be viewed on
the Nexis Native Chain Explorer. The transfer function requires the following arguments:
connection- the JSON-RPC connection to the clusterpayer- the account of the payer for the transactionsource- the token account sending tokensdestination- the token account receiving tokensowner- the account of the owner of thesourcetoken accountamount- the number of tokens to transfer
transfer function simply creates a transaction with the
instructions obtained from the createTransferInstruction function:
Lab
We’re going to use the Token Token Program to create a Token Mint, create an Associated Token Account, mint tokens, transfer tokens, and burn tokens. Assuming you already have a.env file with a SECRET_KEY setup per
Cryptography fundamentals.
Create the Token Mint
Create an empty file calledcreate-token-mint.ts. After loading our keypairs,
we’ll call createMint(), setting our user as the payer, mintAuthority,
and freezeAuthority.
Think of the token mint as the factory that makes tokens. Our user, as the
mintAuthority is the person that runs the factory.
npx esrun create-token-mint.ts. You should see
Make some token metadata
You’ll notice our token account does not have a pretty symbol and shows up as ‘Unknown Token’ in Explorer. That’s because our token has no metadata! Let’s add some. We’ll use the Metaplexmpl-token-metadata Program, version 2. This is the most
popular version of mpl-token-metadata and saves significant complexity
compared to the newer version 3.
create-token-metadata.ts
Warning! Token names and logos are not unique. This token may have spoofed its name and logo to look like another token. Verify the token’s mint address to ensure it is correct.This warning is accurate - indeed anyone can make any token have any symbol or name they like. However for your reference, if you are making an original token that becomes very well known, Nexis Native Chain Explorer uses a whitelist based on the Unified Token List API.
Create an Associated Token Account to store the tokens
Now that we’ve created the mint, let’s create a new Associated Token Account so that someone can store our tokens. This Associated Token Account could be for our wallet (if we, as the token mint authority, want to mint tokens to our address) or anyone else we know with a devnet wallet! Create an empty file calledcreate-token-account.ts. Then use
getOrCreateAssociatedTokenAccount() to get an associated token account based
on a wallet and our mint address, making the account if it needs to.
Remember to substitute in your token mint address below!
npx esrun create-token-mint.ts. You should see:
Mint Tokens
Now that we have a token mint and a token account, let’s mint tokens to the token account. Recall that we set theuser as the mintAuthority for the
mint we created.
Create a function mintTokens that uses the spl-token function mintTo to
mint tokens:
npx esrun mint-tokens.ts. You should see:
Transfer Tokens
Next, let’s transfer some of the tokens we just minted using thespl-token
library’s transfer function. You can
add a second account on devnet
if you like, or find a friend who has a devnet account and send them your token!
As you saw in Explorer, the tokens currently reside in an Associated Token
Account attached to our wallet. We don’t have to remember the address for our
associated token account - we can just look it up using
getOrCreateAssociatedTokenAccount() and provide our wallet address and the
mint of the token we want to send. Likewise, we can find (or make) an ATA for
our recipient to hold this token too.
Challenge
Now it’s your turn to build something independently. Create an application that allows a user to create a new mint, create a token account, and mint tokens. To interact with the Token Program using the wallet adapter, you will have to build each transaction and then submit the transaction to the wallet app for approval.
- You can build this from scratch or you can download the starter code.
- Create a new Token Mint in the
CreateMintcomponent. If you need a refresher on how to send transactions to a wallet for approval, have a look at the Wallets lesson.
Keypair will also have to sign
the transaction. When additional signers are required in addition to the
connected wallet, use the following format:
- Create a new Token Account in the
CreateTokenAccountcomponent. - Mint tokens in the
MintToFormcomponent.