Skip to content

Working with SPL Tokens ​

Tokens on the SVM are managed by on-chain programs — not by the runtime itself. The SPL Token Program is the standard implementation for fungible and non-fungible tokens. Its successor, Token-2022 (Token Extensions), adds advanced features while maintaining the same core account model. Both are available on Zink.

Upstream-compatible

The Token Program, Token-2022, and Associated Token Account program on Zink are identical to Solana mainnet. Existing token operations, client code, and CPIs work without modification.

Core concepts ​

The token model revolves around three account types:

Account typeProgramPurpose
Mint accountToken Program or Token-2022Defines a token: supply, decimals, authorities. One per token.
Token accountToken Program or Token-2022Holds a balance of a specific mint for a specific owner. Many per token.
Associated Token Account (ATA)ATA ProgramA deterministically-derived token account for a given wallet + mint pair.
┌──────────────┐       ┌─────────────────────┐       ┌──────────────────────┐
│ Mint Account │       │  Token Account       │       │  Token Account       │
│              │       │                      │       │                      │
│ supply: 1M   │◄──────│ mint: <this mint>    │       │ mint: <this mint>    │
│ decimals: 6  │       │ owner: Alice         │       │ owner: Bob           │
│ mint_auth: X │       │ amount: 400,000      │       │ amount: 600,000      │
│ freeze_auth: Y│      │                      │       │                      │
└──────────────┘       └─────────────────────┘       └──────────────────────┘

Mint accounts ​

A mint account defines a token type. Key fields:

FieldDescription
supplyTotal number of tokens currently in circulation
decimalsNumber of decimal places (e.g., 6 means 1 token = 1,000,000 base units)
mint_authorityThe public key (or PDA) authorized to mint new tokens. Can be set to None to cap supply permanently.
freeze_authorityThe public key authorized to freeze/thaw token accounts. Can be None.
is_initializedWhether the mint has been initialized

Creating a mint ​

bash
# CLI
spl-token create-token --decimals 6
rust
// On-chain via CPI
invoke(
    &spl_token::instruction::initialize_mint(
        token_program.key,
        mint.key,
        mint_authority.key,
        Some(freeze_authority.key),
        decimals,
    )?,
    &[mint.clone(), rent_sysvar.clone()],
)?;

Token accounts ​

A token account holds a balance of a specific token (mint) for a specific owner. Key fields:

FieldDescription
mintThe mint this token account is associated with
ownerThe wallet that controls this token account (can transfer, burn, close)
amountCurrent token balance in base units
delegateOptional delegated authority for a one-time transfer/burn up to delegated_amount
stateInitialized, Frozen, or Uninitialized
close_authorityOptional authority that can close this account (defaults to owner)

The owner of a token account (the wallet) is distinct from the account's on-chain owner field (which is the Token Program). The Token Program uses the owner field in the token account data to authorize operations.

Associated Token Accounts (ATAs) ​

Manually creating token accounts is cumbersome — you need to generate a keypair, allocate space, and initialize. The Associated Token Account (ATA) program solves this by deterministically deriving a token account address for any wallet + mint pair:

ATA address = PDA(wallet_pubkey, token_program_id, mint_pubkey)

This means every wallet has exactly one canonical ATA per token. If it doesn't exist yet, anyone can create it (the ATA program's create instruction is permissionless — the payer funds the rent-exempt minimum).

typescript
// TypeScript
import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction } from "@solana/spl-token";

const ata = await getAssociatedTokenAddress(mint, wallet);
// If ata doesn't exist yet:
transaction.add(createAssociatedTokenAccountInstruction(payer, ata, wallet, mint));

Zink recommendation

Always use ATAs when sending tokens to user wallets. This ensures consistent token account addresses across ecosystem programs and frontends.

Token operations ​

Transfer ​

rust
// CPI from your program
invoke(
    &spl_token::instruction::transfer(
        token_program.key,
        source.key,
        destination.key,
        authority.key,
        &[],
        amount,
    )?,
    &[source.clone(), destination.clone(), authority.clone()],
)?;

Mint ​

Requires the mint_authority to sign:

rust
invoke_signed(
    &spl_token::instruction::mint_to(
        token_program.key,
        mint.key,
        destination.key,
        mint_authority_pda.key,
        &[],
        amount,
    )?,
    &[mint.clone(), destination.clone(), mint_authority_pda.clone()],
    &[&[b"mint_authority", &[bump]]],
)?;

Burn ​

Destroys tokens, reducing the mint's supply:

rust
invoke(
    &spl_token::instruction::burn(
        token_program.key,
        token_account.key,
        mint.key,
        authority.key,
        &[],
        amount,
    )?,
    &[token_account.clone(), mint.clone(), authority.clone()],
)?;

Close account ​

Closes a token account and reclaims the rent-exempt lamports. The token account must have a zero balance:

rust
invoke(
    &spl_token::instruction::close_account(
        token_program.key,
        token_account.key,
        destination.key,  // receives the lamports
        authority.key,
        &[],
    )?,
    &[token_account.clone(), destination.clone(), authority.clone()],
)?;

Authorities ​

Tokens have two key authorities:

AuthorityControlsSet on
Mint authorityCan mint new tokens. Set to None to make supply fixed.Mint account
Freeze authorityCan freeze/thaw individual token accounts, preventing transfers.Mint account

Both can be transferred to a new key or revoked (set_authority instruction). For program-controlled tokens, these authorities are typically PDAs so the program can sign mint/freeze operations via CPI.

bash
# Transfer mint authority to a PDA or multisig
spl-token authorize <MINT> mint <NEW_AUTHORITY>

# Revoke mint authority permanently
spl-token authorize <MINT> mint --disable

Token-2022 (Token Extensions) ​

Token-2022 is the next-generation token program. It is a separate program (TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb) that supports all original Token Program functionality plus extensions — optional features that are enabled per-mint or per-token-account at creation time:

ExtensionDescription
Transfer feesAutomatic fee on every transfer, collected in a withheld balance
Interest-bearingUI-layer interest rate for display purposes (does not actually accrue tokens)
Non-transferableSoulbound tokens that cannot be transferred after minting
Permanent delegateA delegate that can never be revoked — can always transfer or burn
Transfer hookCalls a custom program on every transfer for validation or side effects
MetadataOn-chain token metadata stored directly in the mint account
Confidential transfersEncrypted balances and transfer amounts using zero-knowledge proofs
Default account stateNew token accounts start as frozen (require explicit thaw)
Group / memberToken grouping for collections

When to use Token-2022 ​

  • Use the original Token Program for maximum compatibility — it is supported by all wallets, DEXes, and tools.
  • Use Token-2022 when you need one or more of its extensions. Note that not all ecosystem tools support Token-2022 tokens yet, so verify compatibility with wallets and frontends in advance.

Interacting with Token-2022 via CPI ​

The instruction interface is the same as the original Token Program — you just target a different program ID:

rust
// Use the Token-2022 program ID instead
let token_program_id = spl_token_2022::ID;

invoke(
    &spl_token_2022::instruction::transfer_checked(
        &token_program_id,
        source.key,
        mint.key,
        destination.key,
        authority.key,
        &[],
        amount,
        decimals,
    )?,
    &[source.clone(), mint.clone(), destination.clone(), authority.clone()],
)?;

Note: Token-2022 prefers transfer_checked (which validates decimals) over plain transfer.

Token account sizes ​

Account typeSize (bytes)Notes
Mint (Token Program)82Fixed
Token account (Token Program)165Fixed
Mint (Token-2022)82 + extensionsVariable depending on enabled extensions
Token account (Token-2022)165 + extensionsVariable depending on enabled extensions

Query the exact size and rent-exempt minimum before creating accounts. See Accounts — Rent and Fees.

Zink is a general-purpose SVM network for programs, operators, and bridge integrations.