251 lines
7.0 KiB
Markdown
251 lines
7.0 KiB
Markdown
---
|
|
title: SPL Token Program
|
|
description: Kit-compatible @solana-program/token client for mint creation, transfers, ATAs, delegation, burning, and instruction plans.
|
|
---
|
|
|
|
# SPL Token Program
|
|
|
|
Solana's standard token program. Defines Mints (token config + supply) and Token Accounts (per-owner balances). For tokens that need extensions, use [Token-2022](token-2022.md) instead.
|
|
|
|
If using plugin clients, prefer `client.use(tokenProgram())` for a fluent API that auto-derives ATAs and defaults the payer. The low-level instructions below are for manual `pipe()` transaction building. See [overview.md](../overview.md) and [plugins.md](../plugins.md).
|
|
|
|
Program address: `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`
|
|
|
|
```ts
|
|
import { TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
|
|
```
|
|
|
|
## Account Types
|
|
|
|
### Mint (82 bytes)
|
|
|
|
```ts
|
|
import { fetchMint, fetchMaybeMint, getMintSize } from '@solana-program/token';
|
|
|
|
const mint = await fetchMint(rpc, mintAddress);
|
|
// mint.data.decimals, mint.data.supply, mint.data.mintAuthority, mint.data.freezeAuthority
|
|
```
|
|
|
|
### Token Account (165 bytes)
|
|
|
|
```ts
|
|
import { fetchToken, fetchMaybeToken, getTokenSize } from '@solana-program/token';
|
|
|
|
const token = await fetchToken(rpc, tokenAddress);
|
|
// token.data.mint, token.data.owner, token.data.amount
|
|
```
|
|
|
|
### Multisig
|
|
|
|
```ts
|
|
import { fetchMultisig } from '@solana-program/token';
|
|
|
|
const multisig = await fetchMultisig(rpc, multisigAddress);
|
|
// multisig.data.m, multisig.data.n, multisig.data.signers
|
|
```
|
|
|
|
## PDA: Associated Token Account
|
|
|
|
One deterministic token account per owner per mint. Derived from owner + mint + token program — no on-chain lookup needed. Always prefer ATAs for user-facing flows.
|
|
|
|
```ts
|
|
import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
|
|
|
|
const [ata] = await findAssociatedTokenPda({
|
|
owner: walletAddress,
|
|
mint: mintAddress,
|
|
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
});
|
|
```
|
|
|
|
## Key Instructions
|
|
|
|
### Initialize Mint
|
|
|
|
```ts
|
|
import { getInitializeMintInstruction } from '@solana-program/token';
|
|
|
|
const ix = getInitializeMintInstruction({
|
|
mint: mintKeypair.address,
|
|
decimals: 9,
|
|
mintAuthority: authority.address,
|
|
freezeAuthority: authority.address, // optional
|
|
});
|
|
```
|
|
|
|
### Transfer
|
|
|
|
Prefer `getTransferCheckedInstruction` — validates mint and decimals on-chain, preventing wrong-token transfers.
|
|
|
|
```ts
|
|
import { getTransferInstruction, getTransferCheckedInstruction } from '@solana-program/token';
|
|
|
|
// Simple transfer
|
|
const ix = getTransferInstruction({
|
|
source: sourceTokenAccount,
|
|
destination: destTokenAccount,
|
|
authority: owner,
|
|
amount: 1_000_000n,
|
|
});
|
|
|
|
// Checked transfer (validates decimals)
|
|
const ix = getTransferCheckedInstruction({
|
|
source: sourceTokenAccount,
|
|
mint: mintAddress,
|
|
destination: destTokenAccount,
|
|
authority: owner,
|
|
amount: 1_000_000n,
|
|
decimals: 9,
|
|
});
|
|
```
|
|
|
|
### Mint To
|
|
|
|
```ts
|
|
import { getMintToInstruction, getMintToCheckedInstruction } from '@solana-program/token';
|
|
|
|
const ix = getMintToInstruction({
|
|
mint: mintAddress,
|
|
token: destinationTokenAccount,
|
|
mintAuthority: authority,
|
|
amount: 1_000_000_000n,
|
|
});
|
|
```
|
|
|
|
### Approve Delegate
|
|
|
|
```ts
|
|
import { getApproveInstruction } from '@solana-program/token';
|
|
|
|
const ix = getApproveInstruction({
|
|
source: tokenAccount,
|
|
delegate: delegateAddress,
|
|
owner: owner,
|
|
amount: 500_000n,
|
|
});
|
|
```
|
|
|
|
### Burn
|
|
|
|
```ts
|
|
import { getBurnInstruction } from '@solana-program/token';
|
|
|
|
const ix = getBurnInstruction({
|
|
account: tokenAccount,
|
|
mint: mintAddress,
|
|
authority: owner,
|
|
amount: 100_000n,
|
|
});
|
|
```
|
|
|
|
## Instruction Plans
|
|
|
|
Handle multi-step operations (e.g., create ATA if needed). Auto-check preconditions and only include necessary instructions — use for user-facing flows. Build a plan with the `get*InstructionPlan` builders, then execute it through a plugin client that has planning + sending capability via `client.sendTransaction(plan)` (see [plugins.md](../plugins.md)). For a shorter form, `client.use(tokenProgram())` exposes `client.token.instructions.createMint(...)` / `client.token.instructions.mintToATA(...)`; call `.sendTransaction()` on the returned plan to submit (e.g. `await client.token.instructions.createMint({ newMint, decimals, mintAuthority }).sendTransaction()`).
|
|
|
|
### Create Mint
|
|
|
|
```ts
|
|
import { getCreateMintInstructionPlan } from '@solana-program/token';
|
|
|
|
const plan = getCreateMintInstructionPlan({
|
|
payer,
|
|
newMint: mintKeypair,
|
|
decimals: 9,
|
|
mintAuthority: authority.address,
|
|
});
|
|
|
|
await client.sendTransaction(plan);
|
|
```
|
|
|
|
### Mint to ATA (Creates ATA if needed)
|
|
|
|
Use the async builder — it derives the ATA automatically.
|
|
|
|
```ts
|
|
import { getMintToATAInstructionPlanAsync } from '@solana-program/token';
|
|
|
|
const plan = await getMintToATAInstructionPlanAsync({
|
|
payer,
|
|
owner: recipientAddress,
|
|
mint: mintAddress,
|
|
mintAuthority: authority,
|
|
amount: 1_000_000_000n,
|
|
decimals: 9,
|
|
});
|
|
|
|
await client.sendTransaction(plan);
|
|
```
|
|
|
|
## Complete Pattern: Create Token + Mint
|
|
|
|
```ts
|
|
import {
|
|
pipe, createTransactionMessage, setTransactionMessageFeePayerSigner,
|
|
setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions,
|
|
signTransactionMessageWithSigners, sendAndConfirmTransactionFactory,
|
|
assertIsTransactionWithBlockhashLifetime, generateKeyPairSigner, lamports,
|
|
} from '@solana/kit';
|
|
import {
|
|
getInitializeMintInstruction, getMintToInstruction,
|
|
getMintSize, TOKEN_PROGRAM_ADDRESS, findAssociatedTokenPda,
|
|
getCreateAssociatedTokenInstruction,
|
|
} from '@solana-program/token';
|
|
import { getCreateAccountInstruction } from '@solana-program/system';
|
|
|
|
// 1. Generate mint keypair
|
|
const mintKeypair = await generateKeyPairSigner();
|
|
|
|
// 2. Get rent
|
|
const mintRent = await rpc.getMinimumBalanceForRentExemption(BigInt(getMintSize())).send();
|
|
|
|
// 3. Derive ATA
|
|
const [ata] = await findAssociatedTokenPda({
|
|
owner: recipient.address,
|
|
mint: mintKeypair.address,
|
|
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
});
|
|
|
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
|
|
const message = pipe(
|
|
createTransactionMessage({ version: 0 }),
|
|
m => setTransactionMessageFeePayerSigner(payer, m),
|
|
m => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
|
|
m => appendTransactionMessageInstructions([
|
|
// Create mint account
|
|
getCreateAccountInstruction({
|
|
payer,
|
|
newAccount: mintKeypair,
|
|
lamports: lamports(mintRent),
|
|
space: getMintSize(),
|
|
programAddress: TOKEN_PROGRAM_ADDRESS,
|
|
}),
|
|
// Initialize mint
|
|
getInitializeMintInstruction({
|
|
mint: mintKeypair.address,
|
|
decimals: 9,
|
|
mintAuthority: payer.address,
|
|
}),
|
|
// Create ATA
|
|
getCreateAssociatedTokenInstruction({
|
|
payer,
|
|
ata,
|
|
owner: recipient.address,
|
|
mint: mintKeypair.address,
|
|
}),
|
|
// Mint tokens
|
|
getMintToInstruction({
|
|
mint: mintKeypair.address,
|
|
token: ata,
|
|
mintAuthority: payer,
|
|
amount: 1_000_000_000n,
|
|
}),
|
|
], m),
|
|
);
|
|
|
|
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
|
|
const signed = await signTransactionMessageWithSigners(message);
|
|
assertIsTransactionWithBlockhashLifetime(signed);
|
|
await sendAndConfirm(signed, { commitment: 'confirmed' });
|
|
```
|