- Creating an SPL token takes three on-chain steps with
@solana/web3.jsand@solana/spl-token: create the mint account that defines the token (createMint), create a token account to hold a balance (getOrCreateAssociatedTokenAccount), then mint the supply into it (mintTo). - Pass both your HTTPS and WSS endpoints to
new Connection(). With the HTTPS endpoint alone,@solana/web3.jsderives a WebSocket URL that does not match a Chainstack node, and confirmations fail withTransactionExpiredBlockheightExceededError. - Amounts are integers in base units — with 9 decimals, 1,000 tokens is
1000 * 10^9. - Verified on Solana devnet against a Chainstack node: mint created, 1,000 tokens minted, and the balance read back.
This tutorial uses
@solana/web3.js with @solana/spl-token — the maintained classic Solana JavaScript stack. A @solana/kit version will follow once the Kit SPL client (@solana-program/token) supports kit v7.How minting an SPL token works
An SPL token lives in two kinds of account: one mint account that defines the token — its decimals, current supply, and the authorities allowed to mint or freeze — and one token account per holder that stores that holder’s balance. Creating a token and giving someone a balance therefore takes three steps:- Create and initialize the mint account with
createMint. This sets the number of decimals and the mint authority. - Create a token account owned by the recipient with
getOrCreateAssociatedTokenAccount. The associated token account (ATA) is a deterministic address derived from the owner and the mint. - Mint tokens from the mint into that token account with
mintTo, signed by the mint authority.
- Rent — every Solana account must hold enough lamports to be rent-exempt. A mint account is a fixed 82 bytes (
MINT_SIZE), which costs 1,461,600 lamports (about 0.00146 SOL) to keep alive. ThecreateMinthelper adds this automatically; if you build the transaction yourself, you supply it withconnection.getMinimumBalanceForRentExemption(MINT_SIZE). - Base units — token amounts are integers, not decimals. A token with 9 decimals means 1 token equals 1,000,000,000 (
10^9) base units, so minting 1,000 tokens uses an amount of1000 * 10^9.
Prerequisites
- Node.js 18 or later.
- A Chainstack Solana node. Start for free — no credit card required. This tutorial uses a Solana devnet node so you can mint freely with faucet SOL.
- The node’s HTTPS and WSS endpoints. On Chainstack, open your node and copy both from the Access and credentials tab.
- A funded devnet account to pay for the transactions and act as the mint authority.
dotenv. Save each file with a .mjs extension (or add "type": "module" to your package.json) so Node treats it as an ES module, and store your endpoints and secret key in a .env file:
.env
Set up and fund the payer
The payer signs and pays for every transaction and is the mint authority in this tutorial. If you do not already have a devnet keypair, generate one and print its base58 secret key to paste into.env as PRIVATE_KEY:
generate-keypair.mjs
Connect to your Chainstack Solana node
Create the connection with both your HTTPS and WSS endpoints.@solana/web3.js opens a WebSocket to confirm transactions; if you pass only the HTTPS endpoint, it derives a WebSocket URL from that host, which does not match a Chainstack node’s separate WSS endpoint. Confirmations then fall back and time out with TransactionExpiredBlockheightExceededError. Passing wsEndpoint explicitly avoids this.
Create the mint
createMint allocates the mint account, makes it rent-exempt, and initializes it in a single call. The arguments are the connection, the fee payer, the mint authority, the freeze authority (null for none), and the number of decimals:
mint is the PublicKey of the new token. The mint authority you set here is the only account allowed to mint more of this token later.
Create the associated token account
Balances live in token accounts, not in the mint.getOrCreateAssociatedTokenAccount returns the payer’s associated token account for this mint, creating it on-chain if it does not exist yet:
owner argument — the function derives and creates the associated token account they own.
Mint the tokens
mintTo mints new tokens from the mint into a token account. It must be signed by the mint authority. The amount is in base units, so multiply the token count by 10^decimals:
BigInt for the amount keeps the math exact for any supply, including tokens with high decimals whose base-unit counts exceed the safe integer range.
Verify the mint and balance
Read the state back from the chain to confirm the mint succeeded.getMint returns the mint’s on-chain supply and decimals, and getAccount returns the token account’s balance:
Full script
The complete, runnable script combines every step. Save it asmint-token.mjs and run it with node mint-token.mjs:
mint-token.mjs
Send everything in one transaction
ThecreateMint, getOrCreateAssociatedTokenAccount, and mintTo helpers each send a separate transaction, which is the simplest approach. When you want the mint to be created and funded atomically — all or nothing, in one signature — build the instructions yourself and send them in a single transaction. This is also where you supply the rent-exempt lamports explicitly with connection.getMinimumBalanceForRentExemption(MINT_SIZE):
mint-token-atomic.mjs
Move to mainnet
The code is identical on mainnet — point the connection at a Chainstack Solana mainnet node’s HTTPS and WSS endpoints and fund the payer with real SOL. Two authority decisions matter in production:- Mint authority — whoever holds it can mint more tokens at any time. To cap the supply permanently after your initial mint, revoke it with
setAuthorityusingAuthorityType.MintTokensand anullnew authority. - Freeze authority — set to
nullat creation (as in this tutorial) if you never want to freeze holder accounts; the choice cannot be added back later.
Additional resources
- Transferring SPL tokens on Solana: a step-by-step TypeScript tutorial — move the tokens you just minted between accounts.
- Solana: how to handle the transaction expiry error — more on the confirmation error the WSS endpoint prevents.
- Solana: token extensions — create tokens with Token-2022 features such as transfer fees and on-chain metadata.
- Solana tooling — the libraries, SDKs, and tools for building on Solana with Chainstack.
Verified with
@solana/web3.js 1.98.4, @solana/spl-token 0.4.15, and bs58 6.0.0.