Intermediate
Token program on the Solana blockchain using CLI
This tutorial provides a comprehensive guide to using the SPL Token Program in Rust. It covers both fungible and non-fungible token creation, including step-by-step instructions on how to create a token type, set up an account, mint tokens, and check the balance. The tutorial also explains the usage of multisig accounts and their role in SPL Token CLI. Before starting, users should set up a Solana cluster, obtain SOL for account rent deposits and transaction fees, and either set up a default keypair or use a hardware wallet. The source code is available on Github, the program is available on crates.io: Rust Package Registry and Docs.rs , and it can be interacted with using CLI, C bindings, JavaScript bindings, and a spl-token command-line utility.
This program defines a common implementation for Fungible and Non Fungible tokens.
Source
The Token Program’s source is available on github
Interface
The Token Program is written in Rust and available on crates.io and docs.rs.
Auto-generated C bindings are also available here
JavaScript bindings are available that support loading the Token Program on to a chain and issue instructions.
See the SPL Associated Token Account program for convention around wallet address to token account mapping and funding.
Reference Guide
Setup
-
CLI
The spl-token
command-line utility can be used to experiment with SPL tokens. Once you have Rust installed, run:
$ cargo install spl-token-cli
Run spl-token --help
for a full description of available commands.
Configuration
The spl-token
configuration is shared with the solana
command-line tool.
Current Configuration
$ solana config get Config File: ${HOME}/.config/solana/cli/config.yml RPC URL: https://api.mainnet-beta.solana.com WebSocket URL: wss://api.mainnet-beta.solana.com/ (computed) Keypair Path: ${HOME}/.config/solana/id.json
Cluster RPC URL
See Solana clusters for cluster-specific RPC URLs
$ solana config set --url https://api.devnet.solana.com
Default Keypair
See Keypair conventions for information on how to setup a keypair if you don’t already have one.
Keypair File
$ solana config set --keypair ${HOME}/new-keypair.json
Hardware Wallet URL (See URL spec)
$ solana config set --keypair usb://ledger/
Airdrop SOL
Creating tokens and accounts requires SOL for account rent deposits and transaction fees. If the cluster you are targeting offers a faucet, you can get a little SOL for testing:
CLI
$ solana airdrop 1
Example: Creating your own fungible token
CLI
$ spl-token create-token Creating token AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM Signature: 47hsLFxWRCg8azaZZPSnQR8DNTRsGyPNfUK7jqyzgt7wf9eag3nSnewqoZrVZHKm8zt3B6gzxhr91gdQ5qYrsRG4
The unique identifier of the token is AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM
.
Tokens when initially created by spl-token
have no supply:
CLI
$ spl-token supply AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 0
Let’s mint some. First create an account to hold a balance of the new AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM
token:
CLI
$ spl-token create-account AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM Creating account 7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi Signature: 42Sa5eK9dMEQyvD9GMHuKxXf55WLZ7tfjabUKDhNoZRAxj9MsnN7omriWMEHXLea3aYpjZ862qocRLVikvkHkyfy
7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi
is now an empty account:
CLI
$ spl-token balance AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 0
Mint 100 tokens into the account:
CLI
$ spl-token mint AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 100 Minting 100 tokens Token: AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM Recipient: 7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi Signature: 41mARH42fPkbYn1mvQ6hYLjmJtjW98NXwd6pHqEYg9p8RnuoUsMxVd16RkStDHEzcS2sfpSEpFscrJQn3HkHzLaa
The token supply
and account balance
now reflect the result of minting:
CLI
$ spl-token supply AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 100
$ spl-token balance AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 100
Example: View all Tokens that you own
CLI
$ spl-token accounts Token Balance ------------------------------------------------------------ 7e2X5oeAAJyUTi4PfSGXFLGhyPw2H8oELm1mx87ZCgwF 84 AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 100 AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 0 (Aux-1*) AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 1 (Aux-2*)
Example: Wrapping SOL in a Token
When you want to wrap SOL, you can send SOL to an associated token account on the native mint and call syncNative
. syncNative
updates the amount
field on the token account to match the amount of wrapped SOL available. That SOL is only retrievable by closing the token account and choosing the desired address to send the token account’s lamports.
CLI
$ spl-token wrap 1 Wrapping 1 SOL into GJTxcnA5Sydy8YRhqvHxbQ5QNsPyRKvzguodQEaShJje Signature: 4f4s5QVMKisLS6ihZcXXPbiBAzjnvkBcp2A7KKER7k9DwJ4qjbVsQBKv2rAyBumXC1gLn8EJQhwWkybE4yJGnw2Y
To unwrap the Token back to SOL:
CLI
$ spl-token unwrap GJTxcnA5Sydy8YRhqvHxbQ5QNsPyRKvzguodQEaShJje Unwrapping GJTxcnA5Sydy8YRhqvHxbQ5QNsPyRKvzguodQEaShJje Amount: 1 SOL Recipient: vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg Signature: f7opZ86ZHKGvkJBQsJ8Pk81v8F3v1VUfyd4kFs4CABmfTnSZK5BffETznUU3tEWvzibgKJASCf7TUpDmwGi8Rmh
Example: Transferring tokens to another user
First the receiver uses spl-token create-account
to create their associated token account for the Token type. Then the receiver obtains their wallet address by running solana address
and provides it to the sender.
The sender then runs:
CLI
$ spl-token transfer AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 50 vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg Transfer 50 tokens Sender: 7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi Recipient: vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg Recipient associated token account: F59618aQB8r6asXeMcB9jWuY6NEx1VduT9yFo1GTi1ks Signature: 5a3qbvoJQnTAxGPHCugibZTbSu7xuTgkxvF4EJupRjRXGgZZrnWFmKzfEzcqKF2ogCaF4QKVbAtuFx7xGwrDUcGd
Example: Transferring tokens to another user, with sender-funding
If the receiver does not yet have an associated token account, the sender may choose to fund the receiver’s account.
The receiver obtains their wallet address by running solana address
and provides it to the sender.
The sender then runs to fund the receiver’s associated token account, at the sender’s expense, and then transfers 50 tokens into it:
CLI
$ spl-token transfer --fund-recipient AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 50 vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg Transfer 50 tokens Sender: 7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi Recipient: vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg Recipient associated token account: F59618aQB8r6asXeMcB9jWuY6NEx1VduT9yFo1GTi1ks Funding recipient: F59618aQB8r6asXeMcB9jWuY6NEx1VduT9yFo1GTi1ks (0.00203928 SOL) Signature: 5a3qbvoJQnTAxGPHCugibZTbSu7xuTgkxvF4EJupRjRXGgZZrnWFmKzfEzcqKF2ogCaF4QKVbAtuFx7xGwrDUcGd
Example: Transferring tokens to an explicit recipient token account
Tokens may be transferred to a specific recipient token account. The recipient token account must already exist and be of the same Token type.
CLI
$ spl-token create-account AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM /path/to/auxiliary_keypair.json Creating account CqAxDdBRnawzx9q4PYM3wrybLHBhDZ4P6BTV13WsRJYJ Signature: 4yPWj22mbyLu5mhfZ5WATNfYzTt5EQ7LGzryxM7Ufu7QCVjTE7czZdEBqdKR7vjKsfAqsBdjU58NJvXrTqCXvfWW
$ spl-token accounts AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM -v Account Token Balance -------------------------------------------------------------------------------------------------------- 7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 100 CqAxDdBRnawzx9q4PYM3wrybLHBhDZ4P6BTV13WsRJYJ AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 0 (Aux-1*)
$ spl-token transfer AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 50 CqAxDdBRnawzx9q4PYM3wrybLHBhDZ4P6BTV13WsRJYJ Transfer 50 tokens Sender: 7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi Recipient: CqAxDdBRnawzx9q4PYM3wrybLHBhDZ4P6BTV13WsRJYJ Signature: 5a3qbvoJQnTAxGPHCugibZTbSu7xuTgkxvF4EJupRjRXGgZZrnWFmKzfEzcqKF2ogCaF4QKVbAtuFx7xGwrDUcGd
$ spl-token accounts AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM -v Account Token Balance -------------------------------------------------------------------------------------------------------- 7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 50 CqAxDdBRnawzx9q4PYM3wrybLHBhDZ4P6BTV13WsRJYJ AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 50 (Aux-1*)
Example: Create a non-fungible token
Create the token type with zero decimal place,
CLI
$ spl-token create-token --decimals 0 Creating token 559u4Tdr9umKwft3yHMsnAxohhzkFnUBPAFtibwuZD9z Signature: 4kz82JUey1B9ki1McPW7NYv1NqPKCod6WNptSkYqtuiEsQb9exHaktSAHJJsm4YxuGNW4NugPJMFX9ee6WA2dXts
then create an account to hold tokens of this new type:
CLI
$ spl-token create-account 559u4Tdr9umKwft3yHMsnAxohhzkFnUBPAFtibwuZD9z Creating account 7KqpRwzkkeweW5jQoETyLzhvs9rcCj9dVQ1MnzudirsM Signature: sjChze6ecaRtvuQVZuwURyg6teYeiH8ZwT6UTuFNKjrdayQQ3KNdPB7d2DtUZ6McafBfEefejHkJ6MWQEfVHLtC
and disable future minting:
CLI
$ spl-token authorize 559u4Tdr9umKwft3yHMsnAxohhzkFnUBPAFtibwuZD9z mint --disable Updating 559u4Tdr9umKwft3yHMsnAxohhzkFnUBPAFtibwuZD9z Current mint authority: vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg New mint authority: disabled Signature: 5QpykLzZsceoKcVRRFow9QCdae4Dp2zQAcjebyEWoezPFg2Np73gHKWQicHG1mqRdXu3yiZbrft3Q8JmqNRNqhwU
Now the 7KqpRwzkkeweW5jQoETyLzhvs9rcCj9dVQ1MnzudirsM
account holds the one and only 559u4Tdr9umKwft3yHMsnAxohhzkFnUBPAFtibwuZD9z
token:
CLI
$ spl-token account-info 559u4Tdr9umKwft3yHMsnAxohhzkFnUBPAFtibwuZD9z Address: 7KqpRwzkkeweW5jQoETyLzhvs9rcCj9dVQ1MnzudirsM Balance: 1 Mint: 559u4Tdr9umKwft3yHMsnAxohhzkFnUBPAFtibwuZD9z Owner: vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg State: Initialized Delegation: (not set) Close authority: (not set)
$ spl-token supply 559u4Tdr9umKwft3yHMsnAxohhzkFnUBPAFtibwuZD9z 1
Multisig usage
CLI
The main difference in spl-token
command line usage when referencing multisig accounts is in specifying the --owner
argument. Typically the signer specified by this argument directly provides a signature granting its authority, but in the multisig case it just points to the address of the multisig account. Signatures are then provided by the multisig signer-set members specified by the --multisig-signer
argument.
Multisig accounts can be used for any authority on an SPL Token mint or token account.
Mint account mint authority:spl-token mint ...
,spl-token authorize ... mint ...
Mint account freeze authority:spl-token freeze ...
,spl-token thaw ...
,spl-token authorize ... freeze ...
Token account owner authority:spl-token transfer ...
,spl-token approve ...
,spl-token revoke ...
,spl-token burn ...
,spl-token wrap ...
,spl-token unwrap ...
,spl-token authorize ... owner ...
Token account close authority:spl-token close ...
,spl-token authorize ... close ...
Example: Mint with multisig authority
First create keypairs to act as the multisig signer-set. In reality, these can be any supported signer, like: a Ledger hardware wallet, a keypair file, or a paper wallet. For convenience, generated keypairs will be used in this example.
CLI
$ for i in $(seq 3); do solana-keygen new --no-passphrase -so "signer-${i}.json"; done Wrote new keypair to signer-1.json Wrote new keypair to signer-2.json Wrote new keypair to signer-3.json
In order to create the multisig account, the public keys of the signer-set must be collected.
CLI
$ for i in $(seq 3); do SIGNER="signer-${i}.json"; echo "$SIGNER: $(solana-keygen pubkey "$SIGNER")"; done signer-1.json: BzWpkuRrwXHq4SSSFHa8FJf6DRQy4TaeoXnkA89vTgHZ signer-2.json: DhkUfKgfZ8CF6PAGKwdABRL1VqkeNrTSRx8LZfpPFVNY signer-3.json: D7ssXHrZJjfpZXsmDf8RwfPxe1BMMMmP1CtmX3WojPmG
Now the multisig account can be created with the spl-token create-multisig
subcommand. Its first positional argument is the minimum number of signers (M
) that must sign a transaction affecting a token/mint account that is controlled by this multisig account. The remaining positional arguments are the public keys of all keypairs allowed (N
) to sign for the multisig account. This example will use a “2 of 3” multisig account. That is, two of the three allowed keypairs must sign all transactions.
NOTE: SPL Token Multisig accounts are limited to a signer-set of eleven signers (1 <= N
<= 11) and minimum signers must be no more than N
(1 <= M
<= N
)
CLI
$ spl-token create-multisig 2 BzWpkuRrwXHq4SSSFHa8FJf6DRQy4TaeoXnkA89vTgHZ \ DhkUfKgfZ8CF6PAGKwdABRL1VqkeNrTSRx8LZfpPFVNY D7ssXHrZJjfpZXsmDf8RwfPxe1BMMMmP1CtmX3WojPmG
Creating 2/3 multisig 46ed77fd4WTN144q62BwjU2B3ogX3Xmmc8PT5Z3Xc2re Signature: 2FN4KXnczAz33SAxwsuevqrD1BvikP6LUhLie5Lz4ETt594X8R7yvMZzZW2zjmFLPsLQNHsRuhQeumExHbnUGC9A
Next create the token mint and receiving accounts as previously described and set the mint account’s minting authority to the multisig account
CLI
$ spl-token create-token Creating token 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o Signature: 3n6zmw3hS5Hyo5duuhnNvwjAbjzC42uzCA3TTsrgr9htUonzDUXdK1d8b8J77XoeSherqWQM8mD8E1TMYCpksS2r $ spl-token create-account 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o Creating account EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC Signature: 5mVes7wjE7avuFqzrmSCWneKBQyPAjasCLYZPNSkmqmk2YFosYWAP9hYSiZ7b7NKpV866x5gwyKbbppX3d8PcE9s $ spl-token authorize 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o mint 46ed77fd4WTN144q62BwjU2B3ogX3Xmmc8PT5Z3Xc2re Updating 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o Current mint authority: 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE New mint authority: 46ed77fd4WTN144q62BwjU2B3ogX3Xmmc8PT5Z3Xc2re Signature: yy7dJiTx1t7jvLPCRX5RQWxNRNtFwvARSfbMJG94QKEiNS4uZcp3GhhjnMgZ1CaWMWe4jVEMy9zQBoUhzomMaxC
To demonstrate that the mint account is now under control of the multisig account, attempting to mint with one multisig signer fails
CLI
$ spl-token mint 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o 1 EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC \ --owner 46ed77fd4WTN144q62BwjU2B3ogX3Xmmc8PT5Z3Xc2re \ --multisig-signer signer-1.json
Minting 1 tokens Token: 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o Recipient: EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC RPC response error -32002: Transaction simulation failed: Error processing Instruction 0: missing required signature for instruction
But repeating with a second multisig signer, succeeds
CLI
$ spl-token mint 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o 1 EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC \ --owner 46ed77fd4WTN144q62BwjU2B3ogX3Xmmc8PT5Z3Xc2re \ --multisig-signer signer-1.json \ --multisig-signer signer-2.json
Minting 1 tokens Token: 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o Recipient: EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC Signature: 2ubqWqZb3ooDuc8FLaBkqZwzguhtMgQpgMAHhKsWcUzjy61qtJ7cZ1bfmYktKUfnbMYWTC1S8zdKgU6m4THsgspT
Example: Offline signing with multisig
Sometimes online signing is not possible or desireable. Such is the case for example when signers are not in the same geographic location or when they use air-gapped devices not connected to the network. In this case, we use offline signing which combines the previous examples of multisig with offline signing and a nonce account.
This example will use the same mint account, token account, multisig account, and multisig signer-set keypair filenames as the online example, as well as a nonce account that we create here:
CLI
$ solana-keygen new -o nonce-keypair.json ... ====================================================================== pubkey: Fjyud2VXixk2vCs4DkBpfpsq48d81rbEzh6deKt7WvPj ======================================================================
$ solana create-nonce-account nonce-keypair.json 1 Signature: 3DALwrAAmCDxqeb4qXZ44WjpFcwVtgmJKhV4MW5qLJVtWeZ288j6Pzz1F4BmyPpnGLfx2P8MEJXmqPchX5y2Lf3r
$ solana nonce-account Fjyud2VXixk2vCs4DkBpfpsq48d81rbEzh6deKt7WvPj Balance: 0.01 SOL Minimum Balance Required: 0.00144768 SOL Nonce blockhash: 6DPt2TfFBG7sR4Hqu16fbMXPj8ddHKkbU4Y3EEEWrC2E Fee: 5000 lamports per signature Authority: 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE
For the fee-payer and nonce-authority roles, a local hot wallet at 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE
will be used.
CLI
First a template command is built by specifying all signers by their public key. Upon running this command, all signers will be listed as “Absent Signers” in the output. This command will be run by each offline signer to generate the corresponding signature.
NOTE: The argument to the --blockhash
parameter is the “Nonce blockhash:” field from the designated durable nonce account.
$ spl-token mint 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o 1 EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC \ --owner 46ed77fd4WTN144q62BwjU2B3ogX3Xmmc8PT5Z3Xc2re \ --multisig-signer BzWpkuRrwXHq4SSSFHa8FJf6DRQy4TaeoXnkA89vTgHZ \ --multisig-signer DhkUfKgfZ8CF6PAGKwdABRL1VqkeNrTSRx8LZfpPFVNY \ --blockhash 6DPt2TfFBG7sR4Hqu16fbMXPj8ddHKkbU4Y3EEEWrC2E \ --fee-payer 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE \ --nonce Fjyud2VXixk2vCs4DkBpfpsq48d81rbEzh6deKt7WvPj \ --nonce-authority 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE \ --sign-only \ --mint-decimals 9
Minting 1 tokens Token: 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o Recipient: EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC Blockhash: 6DPt2TfFBG7sR4Hqu16fbMXPj8ddHKkbU4Y3EEEWrC2E Absent Signers (Pubkey): 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE BzWpkuRrwXHq4SSSFHa8FJf6DRQy4TaeoXnkA89vTgHZ DhkUfKgfZ8CF6PAGKwdABRL1VqkeNrTSRx8LZfpPFVNY
Next each offline signer executes the template command, replacing each instance of their public key with the corresponding keypair.
$ spl-token mint 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o 1 EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC \ --owner 46ed77fd4WTN144q62BwjU2B3ogX3Xmmc8PT5Z3Xc2re \ --multisig-signer signer-1.json \ --multisig-signer DhkUfKgfZ8CF6PAGKwdABRL1VqkeNrTSRx8LZfpPFVNY \ --blockhash 6DPt2TfFBG7sR4Hqu16fbMXPj8ddHKkbU4Y3EEEWrC2E \ --fee-payer 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE \ --nonce Fjyud2VXixk2vCs4DkBpfpsq48d81rbEzh6deKt7WvPj \ --nonce-authority 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE \ --sign-only \ --mint-decimals 9
Minting 1 tokens Token: 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o Recipient: EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC Blockhash: 6DPt2TfFBG7sR4Hqu16fbMXPj8ddHKkbU4Y3EEEWrC2E Signers (Pubkey=Signature): BzWpkuRrwXHq4SSSFHa8FJf6DRQy4TaeoXnkA89vTgHZ=2QVah9XtvPAuhDB2QwE7gNaY962DhrGP6uy9zeN4sTWvY2xDUUzce6zkQeuT3xg44wsgtUw2H5Rf8pEArPSzJvHX Absent Signers (Pubkey): 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE DhkUfKgfZ8CF6PAGKwdABRL1VqkeNrTSRx8LZfpPFVNY
$ spl-token mint 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o 1 EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC \ --owner 46ed77fd4WTN144q62BwjU2B3ogX3Xmmc8PT5Z3Xc2re \ --multisig-signer BzWpkuRrwXHq4SSSFHa8FJf6DRQy4TaeoXnkA89vTgHZ \ --multisig-signer signer-2.json \ --blockhash 6DPt2TfFBG7sR4Hqu16fbMXPj8ddHKkbU4Y3EEEWrC2E \ --fee-payer 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE \ --nonce Fjyud2VXixk2vCs4DkBpfpsq48d81rbEzh6deKt7WvPj \ --nonce-authority 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE \ --sign-only \ --mint-decimals 9
Minting 1 tokens Token: 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o Recipient: EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC Blockhash: 6DPt2TfFBG7sR4Hqu16fbMXPj8ddHKkbU4Y3EEEWrC2E Signers (Pubkey=Signature): DhkUfKgfZ8CF6PAGKwdABRL1VqkeNrTSRx8LZfpPFVNY=2brZbTiCfyVYSCp6vZE3p7qCDeFf3z1JFmJHPBrz8SnWSDZPjbpjsW2kxFHkktTNkhES3y6UULqS4eaWztLW7FrU Absent Signers (Pubkey): 5hbZyJ3KRuFvdy5QBxvE9KwK17hzkAUkQHZTxPbiWffE BzWpkuRrwXHq4SSSFHa8FJf6DRQy4TaeoXnkA89vTgHZ
Finally, the offline signers communicate the Pubkey=Signature
pair from the output of their command to the party who will broadcast the transaction to the cluster. The broadcasting party then runs the template command after modifying it as follows:
Replaces any corresponding public keys with their keypair (--fee-payer ...
and --nonce-authority ...
in this example)
Removes the --sign-only
argument, and in the case of the mint
subcommand, the --mint-decimals ...
argument as it will be queried from the cluster
Adds the offline signatures to the template command via the --signer
argument
$ spl-token mint 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o 1 EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC \ --owner 46ed77fd4WTN144q62BwjU2B3ogX3Xmmc8PT5Z3Xc2re \ --multisig-signer BzWpkuRrwXHq4SSSFHa8FJf6DRQy4TaeoXnkA89vTgHZ \ --multisig-signer DhkUfKgfZ8CF6PAGKwdABRL1VqkeNrTSRx8LZfpPFVNY \ --blockhash 6DPt2TfFBG7sR4Hqu16fbMXPj8ddHKkbU4Y3EEEWrC2E \ --fee-payer hot-wallet.json \ --nonce Fjyud2VXixk2vCs4DkBpfpsq48d81rbEzh6deKt7WvPj \ --nonce-authority hot-wallet.json \ --signer BzWpkuRrwXHq4SSSFHa8FJf6DRQy4TaeoXnkA89vTgHZ=2QVah9XtvPAuhDB2QwE7gNaY962DhrGP6uy9zeN4sTWvY2xDUUzce6zkQeuT3xg44wsgtUw2H5Rf8pEArPSzJvHX \ --signer DhkUfKgfZ8CF6PAGKwdABRL1VqkeNrTSRx8LZfpPFVNY=2brZbTiCfyVYSCp6vZE3p7qCDeFf3z1JFmJHPBrz8SnWSDZPjbpjsW2kxFHkktTNkhES3y6UULqS4eaWztLW7FrU
Minting 1 tokens Token: 4VNVRJetwapjwYU8jf4qPgaCeD76wyz8DuNj8yMCQ62o Recipient: EX8zyi2ZQUuoYtXd4MKmyHYLTjqFdWeuoTHcsTdJcKHC Signature: 2AhZXVPDBVBxTQLJohyH1wAhkkSuxRiYKomSSXtwhPL9AdF3wmhrrJGD7WgvZjBPLZUFqWrockzPp9S3fvzbgicy
JSON RPC methods
There is a rich set of JSON RPC methods available for use with SPL Token:
-
getTokenAccountBalance
-
getTokenAccountsByDelegate
-
getTokenAccountsByOwner
-
getTokenLargestAccounts
-
getTokenSupply
See JSON RPC HTTP Methods | Solana Docs for more details.
Additionally the versatile getProgramAccounts
JSON RPC method can be employed in various ways to fetch SPL Token accounts of interest.
Finding all token accounts for a specific mint
To find all token accounts for the TESTpKgj42ya3st2SQTKiANjTBmncQSCqLAZGcSPLGM
mint:
curl http://api.mainnet-beta.solana.com -X POST -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getProgramAccounts", "params": [ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", { "encoding": "jsonParsed", "filters": [ { "dataSize": 165 }, { "memcmp": { "offset": 0, "bytes": "TESTpKgj42ya3st2SQTKiANjTBmncQSCqLAZGcSPLGM" } } ] } ] } '
The "dataSize": 165
filter selects all Token Accounts, and then the "memcmp": ...
filter selects based on the mint address within each token account.
Finding all token accounts for a wallet
Find all token accounts owned by the vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg
user:
curl http://api.mainnet-beta.solana.com -X POST -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getProgramAccounts", "params": [ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", { "encoding": "jsonParsed", "filters": [ { "dataSize": 165 }, { "memcmp": { "offset": 32, "bytes": "vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg" } } ] } ] } '
The "dataSize": 165
filter selects all Token Accounts, and then the "memcmp": ...
filter selects based on the owner address within each token account