Solana’s Token Program, Explained

Breaking down how fungible and non-fungible tokens work in Solana.

Solana’s Account Model

In order to understand tokens, you need to understand Solana’s account model. I highly recommend reading this wiki, especially if you come from an Ethereum background. Here’s a brief summary:

  • An account either holds data (e.g. how many tokens you have) or is an executable program (i.e. a smart contract). I refer to the former as “data accounts” and the latter as “program accounts.” Importantly, unlike Ethereum, program accounts do not store state. All state is stored in data accounts.
  • Each account contains the following fields 👇
  • Each account has a unique address (similar to Ethereum). Most addresses are the public key of a keypair.
  • Each account is owned by a program. By default, a newly created account is owned by a built-in program called the “System Program.” Only the owner of an account can modify it.
The account on the left is a program account that can increment a counter. The counter’s value needs to be stored in a separate data account. The data account is owned by the program account, which means the program account can modify the data account’s state.

What is Solana’s Token Program?

Solana’s token program enables the following (for fungible and non-fungible tokens):

  • Token minting
  • Token transferring
  • Token burning

How Does Solana’s Token Program Work?

The easiest way to understand this is by going through a few examples. Our examples will cover fungible tokens, and we’ll use the spl-token command-line tool to interact with the token program (you can install it by running cargo install spl-token-cli).

$ echo $SOLADDR1
3sdsSwWWjjGA7HpPBQfGaXRE2HqmdKicMXHRapqLAu4L
$ echo $SOLADDR2
ES2C1YPzNh5JjQu7DdxrveaPUHj9CnrRWSdrFo4ku5Zh
$ spl-token create-token --mint-authority ~/my_solana_wallet1.json                                         
Creating token 6ifRGEkJ6XmEjuqfTFNqAjjomUiDJTjRv4GHqBH6usWr
$ solana account $TOKEN1
Public Key: Aqf1rBKNQYgX1mjE64STwV3miEwXEe2ioZzD7n4vkpXk
Balance: 0.0014616 SOL
Owner: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Executable: false
Rent Epoch: 207
Length: 82 (0x52) bytes
0000: 01 00 00 00 2a b0 1a 64 bb c5 f0 df bf 57 d5 61 ....*..d.....W.a
0010: 56 a8 b8 85 8f a8 0b 09 f1 f1 a2 dc 4d 51 b3 63 V...........MQ.c
0020: 8f 72 bd e9 00 00 00 00 00 00 00 00 09 01 00 00 .r..............
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0050: 00 00 ..
https://explorer.solana.com/address/TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA?cluster=devnet
https://explorer.solana.com/address/Aqf1rBKNQYgX1mjE64STwV3miEwXEe2ioZzD7n4vkpXk?cluster=devnet
  • Address—I hope this is self-explanatory 😛 This is just the mint account’s address.
  • Current Supply—The number of tokens that have been minted. Since we just created the token, it is 0.
  • Mint Authority—The public key of the keypair that is allowed to mint tokens (we specified this with the --mint-authority flag. If anyone else tries to mint tokens, it will fail.
  • Decimals—This dictates the smallest denomination of the token. For NFTs, it should be zero. Nine is the default.
  • “Internal Solana relations” refers to the owner field that is set on every account, e.g. the owner that’s displayed when you run solana account $TOKEN1.
  • “User-space relations” refers to when a relation between two accounts is encoded in an account’s data, e.g. the “mint authority” field we saw above.
The Token Program’s owner is the BPF Loader. I don’t picture it here because it’s not that important, and it clutters up the diagram.
$ spl-token create-account $TOKEN1 --owner $SOLADDR1                              
Creating account 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
$ solana account $TOKENACCT1
Public Key: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
Balance: 0.00203928 SOL
Owner: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Executable: false
Rent Epoch: 207
https://explorer.solana.com/address/9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak?cluster=devnet
$ spl-token balance $TOKEN1 --owner $SOLADDR1
0
$ spl-token mint $TOKEN1 10 $TOKENACCT1 --mint-authority ~/my_solana_wallet1.json
Minting 10 tokens
Token: Aqf1rBKNQYgX1mjE64STwV3miEwXEe2ioZzD7n4vkpXk
Recipient: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
$ spl-token balance $TOKEN1 --owner $SOLADDR1
10
The token balance gets updated in Solana Explorer as well. https://explorer.solana.com/address/9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak?cluster=devnet.
$ spl-token accounts  --owner $SOLADDR1                                                                                                      
Token Balance
---------------------------------------------------------------
Aqf1rBKNQYgX1mjE64STwV3miEwXEe2ioZzD7n4vkpXk 10
$ spl-token mint $TOKEN1 10 $TOKENACCT1 --mint-authority ~/my_solana_wallet2.json
Minting 10 tokens
Token: Aqf1rBKNQYgX1mjE64STwV3miEwXEe2ioZzD7n4vkpXk
Recipient: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
RPC response error -32002: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x4 [5 log messages]
$ spl-token transfer $TOKEN1 1 $SOLADDR2 --owner ~/my_solana_wallet1.json
Transfer 5 tokens
Sender: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
Recipient: ES2C1YPzNh5JjQu7DdxrveaPUHj9CnrRWSdrFo4ku5Zh
Recipient associated token account: FFednTgQRKDbGYjXrXk8SWPfzSJW3Q8ApN5mGCpGdAtE
Error: Recipient's associated token account does not exist. Add `--fund-recipient` to fund their account
  1. Create a token account for the recipient and then transfer the tokens.
  2. Use the --fund-recipient flag. This makes the sender pay for creating the recipient’s token account.
$ spl-token create-account $TOKEN1 --owner $SOLADDR2
Creating account FFednTgQRKDbGYjXrXk8SWPfzSJW3Q8ApN5mGCpGdAtE
$ spl-token transfer $TOKEN1 1 $SOLADDR2 --owner ~/my_solana_wallet1.json
Transfer 1 tokens
Sender: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
Recipient: ES2C1YPzNh5JjQu7DdxrveaPUHj9CnrRWSdrFo4ku5Zh
Recipient associated token account: FFednTgQRKDbGYjXrXk8SWPfzSJW3Q8ApN5mGCpGdAtE
$ spl-token balance $TOKEN1 --owner $SOLADDR2
1
$ spl-token wrap 1 ~/my_solana_wallet1.json
Wrapping 1 SOL into DXWzvX3RtZbito65G8ZgqmEzJM6Z6Fqy7NHkbpEmCUZD
$ spl-token unwrap DXWzvX3RtZbito65G8ZgqmEzJM6Z6Fqy7NHkbpEmCUZD ~/my_solana_wallet1.json
Unwrapping DXWzvX3RtZbito65G8ZgqmEzJM6Z6Fqy7NHkbpEmCUZD
Amount: 1 SOL
Recipient: 3sdsSwWWjjGA7HpPBQfGaXRE2HqmdKicMXHRapqLAu4L

Wait, What About NFTS?

Now that we’ve walked through how fungible tokens work, understanding non-fungible tokens should be easy. I’ll just let you read the docs, since I wouldn’t add much anyways. The main important points are:

  • Use --decimals 0 when creating the token, since there should only be one of them.
  • After minting one token, disable future minting. This ensures there will only ever be one token.
  • A single program called the Token Program is able to create new token types, mint new tokens, and transfer tokens between accounts.
  • Unlike ERC20 tokens, where each new type of token is associated with a new ERC20 smart contract, there is only one Token Program. However, each new type of token is associated with a new mint account.
  • If a user wants to own some tokens of a particular type, they need to have a token account. In other words, if someone owns five different types of tokens, they will have five different token accounts.
  • You can wrap SOL in an SPL token, which allows it to be easily exchanged for other SPL tokens.
  • A non-fungible SPL token is a token with zero decimals that has minted one copy and disabled future minting.

--

--

Software Engineer. Tweeting @pencilflip. Mediocre boulderer, amateur tennis player, terrible at Avalon. https://www.mattlim.me/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Matt Lim

Software Engineer. Tweeting @pencilflip. Mediocre boulderer, amateur tennis player, terrible at Avalon. https://www.mattlim.me/