learn.sol
Week 4 • Spl Token And Token 2022 Client Flows

SPL Token & Token-2022 Client Flows

Build token client flows safely with explicit token program selection and validation checks.

This lesson is dev-only and educational. Do not use production funds while learning.

Step 1: 0-to-1 Theory

Primitives

  • mint: token definition account
  • token account: balance holder for owner+mint
  • token program id: classic SPL vs Token-2022

Mental Model

If SOL is “one native balance per address”, tokens are “many balances per address”.

Those balances live in token accounts. Your client must always be clear about:

  • which mint you are using
  • which token program owns the accounts
  • how to convert UI amounts into raw integer amounts using decimals

Invariants

  1. Token program choice must be explicit.
  2. Mint/account pairing must be validated before transfer logic.
  3. Amount handling must respect decimals.

Quick Checks

  1. Why is "assume token program" dangerous?
  2. Why validate mint/account before send?

Step 2: Real-World Implementation (Code Solution)

Install token clients:

pnpm add @solana-program/token @solana-program/token-2022

Create lib/solana/tokenFlow.ts:

// Keep token program choice explicit in your app.
// Classic SPL Token and Token-2022 are different programs.
export type TokenProgramKind = "spl" | "token2022";

export type TokenTransferInput = {
  // Which token program are we using for this mint + token accounts?
  programKind: TokenProgramKind;
  // Base58 strings for simplicity in this beginner module.
  mint: string;
  sourceTokenAccount: string;
  destinationTokenAccount: string;
  // Raw integer amount (already scaled by decimals).
  amountRaw: bigint;
  // Number of decimals used by the mint (for UI conversion).
  decimals: number;
};

export function validateTokenTransferInput(input: TokenTransferInput) {
  // Fail early with clear messages before you ask the wallet to sign.
  if (!input.mint) throw new Error("Missing mint");
  if (!input.sourceTokenAccount) throw new Error("Missing source token account");
  if (!input.destinationTokenAccount) throw new Error("Missing destination token account");
  if (input.amountRaw <= 0n) throw new Error("Amount must be positive");
  if (input.decimals < 0 || input.decimals > 18) throw new Error("Invalid decimals");
}

export async function buildTokenTransferPlan(input: TokenTransferInput) {
  validateTokenTransferInput(input);

  // In a full implementation, create token program instructions here.
  // Keep programKind explicit to route SPL vs Token-2022 instruction builders.
  return {
    ok: true,
    programKind: input.programKind,
    summary: {
      mint: input.mint,
      from: input.sourceTokenAccount,
      to: input.destinationTokenAccount,
      amountRaw: input.amountRaw.toString(),
      decimals: input.decimals,
    },
  };
}

Create components/solana/TokenTransferPreview.tsx:

"use client";

import React, { useState } from "react";
import { buildTokenTransferPlan } from "@/lib/solana/tokenFlow";

export function TokenTransferPreview() {
  // Show the plan output as JSON so beginners can see what will be signed/sent.
  const [result, setResult] = useState<any>(null);

  const onPreview = async () => {
    const plan = await buildTokenTransferPlan({
      programKind: "spl",
      // These are placeholder strings. Replace with real addresses when you wire up a real mint.
      mint: "Mint111111111111111111111111111111111111111",
      sourceTokenAccount: "Src1111111111111111111111111111111111111111",
      destinationTokenAccount: "Dst1111111111111111111111111111111111111111",
      // Raw amount: for a 6-decimal mint, 1 token = 1_000_000 raw units.
      amountRaw: 1_000_000n,
      decimals: 6,
    });
    setResult(plan);
  };

  return (
    <div>
      <button onClick={onPreview} className="px-3 py-2 rounded bg-indigo-600 text-white">Preview Transfer</button>
      {result && <pre className="mt-3 text-xs bg-zinc-950 p-3 rounded">{JSON.stringify(result, null, 2)}</pre>}
    </div>
  );
}

Expected result:

  • explicit token flow validation and preview output before transaction assembly.

Step 3: Mastery Test

  • Easy: where do you enforce program kind selection?
  • Medium: what field must be checked before decimals conversion?
  • Hard: how do you prevent cross-program account mixups at client layer?

Sources

Solana Assistant

AI-powered documentation helper

Welcome to Solana Assistant

Ask specific questions about Solana development:

Ask specific questions for better results400px
    SPL Token & Token-2022 Client Flows | learn.sol