Sol.College

SPL Tokens: mints, accounts and transfers

Fungible tokens on Solana are accounts owned by the Token Program. A mint account defines supply and decimals, and a token account holds a balance of one mint for one owner. The Associated Token Account (ATA) is the canonical token account address for a wallet and mint. Transfers are instructions to the Token Program. The checked variants also pass the mint and decimals so mistakes fail loudly.

Reference: Solana docs: Tokens on Solana · Anchor docs: SPL Token basics

Real code, line by line

6 excerpts from different projects. Each caption links the exact lines at the fork's pinned commit. The notes below each excerpt explain the lines that matter.

restacked-dev/valhalla · anchor/programs/valhalla/src/instructions/disburse.rs L1–26 @ 7aa9ddf · MIT · original
use anchor_lang::prelude::*;use anchor_spl::{    associated_token::AssociatedToken,    token_interface::{        mint_to, transfer_checked, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked,    },}; use crate::{constants, errors::ValhallaError, state::Vault, Config}; #[derive(Accounts)]pub struct DisburseVault<'info> {    #[account(mut)]    pub signer: Signer<'info>,     pub creator: SystemAccount<'info>,     pub recipient: SystemAccount<'info>,     #[account(mut)]    pub dev_treasury: SystemAccount<'info>,     #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]    pub config: Box<Account<'info, Config>>,     #[account(
  1. Line 5 mint_to, transfer_checked, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked,: A checked token transfer, which also verifies the mint and decimals.
  2. Line 5 mint_to, transfer_checked, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked,: Mints new tokens. The mint authority must sign.
  3. Line 14 pub signer: Signer<'info>,: Requires this account to have signed the transaction.
  4. Line 23 #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  5. Line 23 #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  6. Line 23 #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]: Checks that a stored pubkey field matches the passed account.
KryptPay/kryptpay-solana-contract · programs/kryptpay/src/instructions/swap_exact_tokens_for_tokens.rs L72–97 @ a528c51 · MIT · original
        &[authority_bump],    ];    let signer_seeds = &[&authority_seeds[..]];    if swap_a {        token::transfer(            CpiContext::new(                ctx.accounts.token_program.to_account_info(),                Transfer {                    from: ctx.accounts.trader_account_a.to_account_info(),                    to: ctx.accounts.pool_account_a.to_account_info(),                    authority: ctx.accounts.trader.to_account_info(),                },            ),            input,        )?;        token::transfer(            CpiContext::new_with_signer(                ctx.accounts.token_program.to_account_info(),                Transfer {                    from: ctx.accounts.pool_account_b.to_account_info(),                    to: ctx.accounts.trader_account_b.to_account_info(),                    authority: ctx.accounts.pool_authority.to_account_info(),                },                signer_seeds,            ),            output,
  1. Line 74 let signer_seeds = &[&authority_seeds[..]];: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  2. Line 76 token::transfer(: An SPL token transfer through the Token Program.
  3. Line 77 CpiContext::new(: Makes a cross-program invocation into another program.
  4. Line 88 CpiContext::new_with_signer(: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.
ProofOfEstimate/poe-solana · programs/poe/src/contexts/register_user.rs L43–64 @ 29a7c77 · MIT · original
        Ok(())    }     pub fn mint_tokens(&mut self, bumps: &RegisterUserBumps) -> Result<()> {        mint_to(            CpiContext::new_with_signer(                self.token_program.to_account_info(),                MintTo {                    authority: self.mint.to_account_info(),                    to: self.token_account.to_account_info(),                    mint: self.mint.to_account_info(),                },                &[&["poeken_mint".as_bytes(), &[bumps.mint]]],            ),            1000 * 1000000000,        )?;         msg!("Minted tokens");         Ok(())    }}
  1. Line 46 pub fn mint_tokens(&mut self, bumps: &RegisterUserBumps) -> Result<()> {: Mints new tokens. The mint authority must sign.
  2. Line 48 CpiContext::new_with_signer(: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.
  3. Line 60 msg!("Minted tokens");: Logs to the program log, which is visible in explorers and the test output.
e-mk/peer-pay · frontend/src/hooks/useSolana.ts L53–78 @ ca496be · Apache-2.0 · original
    );     const { mintAddress = "" } = (mintedProducts as IMints)[type!!] ?? {};     const buyers_token_init = await getOrCreateAssociatedTokenAccount(      connection,      sellerAccept,      new PublicKey(mintAddress),      buyerWalletPK    );     const buyers_usdc_token = await getOrCreateAssociatedTokenAccount(      connection,      sellerAccept,      USDC_MINT,      buyerWalletPK    );     const sellers_usdc_token = await getOrCreateAssociatedTokenAccount(      connection,      sellerAccept,      USDC_MINT,      sellerAccept.publicKey    );    const xRequested = +quantity * 1000000;    const yAmount = +xRequested * YPRICE;
asimnecip/gree · anchor/programs/gree/src/lib.rs L29–54 @ 7ef8c89 · MIT · original
                to: ctx.accounts.associated_token_account.to_account_info(),                authority: ctx.accounts.signer.to_account_info(),            };            let cpi_context = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts);            mint_to(cpi_context, 1)?;        }         // Create metadata with minimized data structure scope        let data_v2 = DataV2 {            name,            symbol,            uri,            seller_fee_basis_points: 0,            creators: None,            collection: None,            uses: None,        };        {            let cpi_accounts = CreateMetadataAccountsV3 {                metadata: ctx.accounts.metadata_account.to_account_info(),                mint: ctx.accounts.mint.to_account_info(),                mint_authority: ctx.accounts.signer.to_account_info(),                update_authority: ctx.accounts.signer.to_account_info(),                payer: ctx.accounts.signer.to_account_info(),                system_program: ctx.accounts.system_program.to_account_info(),                rent: ctx.accounts.rent.to_account_info(),
  1. Line 32 let cpi_context = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accoun: Makes a cross-program invocation into another program.
  2. Line 33 mint_to(cpi_context, 1)?;: Mints new tokens. The mint authority must sign.
s11leo/elyse · app_page/token_faucet/faucet.js L62–87 @ 77b39eb · MIT · original
    }     let recipientTokenAccount;    try {        recipientTokenAccount = await getOrCreateAssociatedTokenAccount(            connection,            sender,            mintAddress,            recipientPublicKey,        );        // console.log("recipient Token Account:", recipientTokenAccount.address.toString());    } catch (error) {        console.error("Не удалось найти или создать токеновый аккаунт получателя:", error);        return res.status(500).send("Не удалось найти или создать токеновый аккаунт получателя");    }        let amount = 50000000000;     const transferInstruction = createTransferInstruction(        senderTokenAccount.address,        recipientTokenAccount.address,        sender.publicKey,        amount,        [],        TOKEN_PROGRAM_ID    );
  1. Line 80 const transferInstruction = createTransferInstruction(: An SPL token transfer through the Token Program.

Go further