Tokens SPL#

Sur une blockchain generique, la seule unite de valeur native est la monnaie du protocole — sur Solana, le SOL. Mais l’ecosysteme a besoin de bien plus : des stablecoins indexes sur le dollar, des jetons de gouvernance, des points de fidelite, des actifs synthetiques, ou encore des jetons non fongibles representant des oeuvres numeriques. Le SPL Token Program est le standard qui rend tout cela possible sur Solana, en fournissant un programme unique capable de gerer n’importe quel type de jeton avec un cout negligeable et une finalite quasi instantanee.

Ce chapitre explore le fonctionnement des tokens SPL : structures de donnees fondamentales (Mint, Token Account, autorites), creation et manipulation de tokens fongibles, mecanisme des Associated Token Accounts (ATA), programme Token-2022 et ses extensions, et enfin un programme Anchor complet integrant creation, emission, transfert et destruction de tokens.

Le lecteur est suppose maitriser les bases du framework Anchor (chapitres 8 et 9) ainsi que le modele de comptes Solana (chapitre 5). Les exemples de code Rust utilisent le crate anchor_spl, qui offre une interface typee et ergonomique au-dessus des programmes SPL natifs.

Le standard SPL Token#

Le SPL Token Program est deploye a une adresse fixe (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA). Contrairement a Ethereum ou chaque token necessite le deploiement d’un contrat ERC-20 distinct, Solana mutualise la logique dans un programme unique : seules les donnees (les comptes) varient.

Définition 102 (SPL Token Program)

Le SPL Token Program est le programme natif de Solana qui implemente le standard de gestion des tokens. Il prend en charge a la fois les tokens fongibles (divisibles et interchangeables, comme un stablecoin) et les tokens non fongibles (uniques, avec une offre de 1 et 0 decimales).

Le programme expose des instructions standardisees : InitializeMint, InitializeAccount, MintTo, Transfer, Burn, Approve/Revoke, FreezeAccount/ThawAccount, et CloseAccount. Deployer un nouveau token revient a creer un nouveau compte Mint, sans deployer de programme supplementaire.

Définition 103 (Mint Account)

Un Mint Account (compte de frappe) est le compte on-chain qui represente un type de token. L’adresse du Mint sert d’identifiant universel du token.

Ses champs principaux sont : mint_authority (Option<Pubkey>, cle autorisee a emettre ; si None, l’offre est figee), supply (u64, total en circulation), decimals (u8, nombre de decimales — 6 signifie 1 000 000 unites de base = 1 token), is_initialized (bool), et freeze_authority (Option<Pubkey>, cle autorisee a geler des Token Accounts). La taille d’un Mint Account est fixe : 82 octets.

Définition 104 (Token Account)

Un Token Account (compte de jetons) stocke le solde d’un utilisateur pour un token specifique. Chaque couple (proprietaire, token) necessite un Token Account distinct.

Les champs principaux sont : mint (adresse du Mint associe), owner (proprietaire du solde), amount (solde en unites indivisibles), delegate et delegated_amount (delegation de depense), state (Initialized, Frozen ou Uninitialized), et close_authority (cle autorisee a fermer le compte). La taille d’un Token Account est fixe : 165 octets.

Définition 105 (Authority)

Une Authority (autorite) est une cle publique disposant de droits privilegies sur un Mint ou un Token Account. Le SPL Token Program distingue quatre types d’autorites :

  1. Mint Authority — autorisee a emettre de nouveaux tokens via MintTo. Revocable definitivement en la mettant a None, ce qui fige l’offre.

  2. Freeze Authority — autorisee a geler ou degeler des Token Accounts. Utilisee par les stablecoins reglementes.

  3. Account Owner — proprietaire d’un Token Account, autorise a transferer ou fermer le compte.

  4. Close Authority — autorisee a fermer un Token Account et a recuperer le loyer en SOL.

Revoquer la Mint Authority est un signal fort de confiance envers les detenteurs du token.

La visualisation ci-dessous illustre cette architecture : un Mint est connecte a plusieurs Token Accounts, chacun detenu par un wallet distinct.

Hide code cell source

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import networkx as nx
import seaborn as sns

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

G = nx.DiGraph()
for i in range(1, 5):
    G.add_edges_from([("Mint\n(USDC)", f"Token Acc. {i}"), (f"Token Acc. {i}", f"Wallet {i}")])

pos = {"Mint\n(USDC)": (0, 2),
       "Token Acc. 1": (-3, 0), "Token Acc. 2": (-1, 0),
       "Token Acc. 3": (1, 0),  "Token Acc. 4": (3, 0),
       "Wallet 1": (-3, -2), "Wallet 2": (-1, -2),
       "Wallet 3": (1, -2),  "Wallet 4": (3, -2)}

palette = sns.color_palette("muted")
colors = [palette[3] if "Mint" in n else palette[0] if "Token" in n else palette[2] for n in G.nodes()]
sizes  = [2200 if "Mint" in n else 1600 if "Token" in n else 1400 for n in G.nodes()]

fig, ax = plt.subplots(figsize=(12, 7))
nx.draw_networkx_edges(G, pos, ax=ax, edge_color="#888888", width=1.5,
                       arrows=True, arrowsize=18, arrowstyle="-|>")
nx.draw_networkx_nodes(G, pos, ax=ax, node_color=colors,
                       node_size=sizes, edgecolors="white", linewidths=2)
nx.draw_networkx_labels(G, pos, ax=ax, font_size=8, font_weight="bold")

legend_items = [
    mpatches.Patch(color=palette[3], label="Mint Account"),
    mpatches.Patch(color=palette[0], label="Token Account"),
    mpatches.Patch(color=palette[2], label="Wallet (Owner)"),
]
ax.legend(handles=legend_items, loc="upper right", fontsize=9, framealpha=0.9)
ax.set_title("Architecture des comptes SPL Token", fontweight="bold")
ax.axis("off")
plt.show()
_images/d7ea9191b57cb8759a5166396b2cbb483f55b31fa390e8ea864d375720056aa5.png

Creation d’un token fongible#

La creation d’un token fongible suit une sequence definie : creation du Mint, creation d’un Token Account, emission, transfert et destruction. Le CLI spl-token permet de realiser ces operations en quelques commandes.

Remarque 70 (Choix du nombre de decimales)

Le champ decimals du Mint determine la granularite du token. Les conventions de l’ecosysteme sont les suivantes :

  • 9 decimales — convention pour les tokens natifs de type SOL. Un solde de 1_000_000_000 unites de base represente 1 token.

  • 6 decimales — convention pour les stablecoins (USDC, USDT). Un solde de 1_000_000 unites de base represente 1 token.

  • 0 decimale — convention pour les NFTs et les tokens non divisibles. Un solde de 1 represente exactement 1 jeton entier.

Ce choix est irreversible une fois le Mint initialise. Il est donc crucial de le fixer correctement des la creation.

Les commandes CLI suivantes illustrent le cycle de vie complet d’un token fongible.

# Creer un nouveau token (Mint) avec 6 decimales
spl-token create-token --decimals 6

# Creer un Token Account pour ce Mint
spl-token create-account <MINT_ADDRESS>

# Emettre 1 000 000 tokens
spl-token mint <MINT_ADDRESS> 1000000

# Verifier le solde
spl-token balance <MINT_ADDRESS>

# Transferer 500 tokens (--fund-recipient cree le Token Account du destinataire)
spl-token transfer <MINT_ADDRESS> 500 <RECIPIENT_WALLET> --fund-recipient

# Bruler 100 tokens
spl-token burn <TOKEN_ACCOUNT_ADDRESS> 100

# Revoquer la Mint Authority (figer l'offre definitivement)
spl-token authorize <MINT_ADDRESS> mint --disable

Exemple 33 (Creation d’un Mint en Anchor avec CPI)

La creation d’un Mint Account via un programme Anchor utilise une Cross-Program Invocation (CPI) vers le SPL Token Program. Le crate anchor_spl fournit les types et les fonctions necessaires.

use anchor_lang::prelude::*;
use anchor_spl::token::{self, Mint, Token};

#[derive(Accounts)]
pub struct CreateToken<'info> {
    #[account(
        init,
        payer = authority,
        mint::decimals = 6,
        mint::authority = authority.key(),
        mint::freeze_authority = authority.key(),
    )]
    pub mint: Account<'info, Mint>,

    #[account(mut)]
    pub authority: Signer<'info>,

    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
    pub rent: Sysvar<'info, Rent>,
}

pub fn create_token(ctx: Context<CreateToken>) -> Result<()> {
    msg!("Mint cree : {}", ctx.accounts.mint.key());
    Ok(())
}

La macro init combinee aux contraintes mint::* genere automatiquement les CPI necessaires pour allouer le compte, l’initialiser via le Token Program et configurer les autorites.

Remarque 71 (Taille et loyer des comptes Token)

Un Mint Account occupe 82 octets et un Token Account occupe 165 octets. Le loyer exempt (rent-exempt balance) requis par Solana est proportionnel a la taille du compte. Au taux actuel, il faut environ 0.0015 SOL pour un Mint et 0.002 SOL pour un Token Account. Ces montants sont recuperables a la fermeture du compte via l’instruction CloseAccount.

Associated Token Accounts (ATA)#

Chaque utilisateur a besoin d’un Token Account distinct par type de token detenu. Comment un expediteur connait-il l’adresse du Token Account du destinataire pour un Mint donne ? Les Associated Token Accounts resolvent ce probleme en derivant l’adresse de maniere deterministe.

Définition 106 (Associated Token Account (ATA))

Un Associated Token Account (ATA) est un Token Account dont l’adresse est derivee de maniere deterministe a partir de trois parametres :

  1. L’adresse du wallet proprietaire.

  2. L’adresse du Token Program (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA).

  3. L’adresse du Mint du token.

L’adresse est calculee comme un Program Derived Address (PDA) du programme Associated Token Account (ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL) :

\[ \text{ATA} = \text{findProgramAddress}\bigl([\text{wallet},\; \text{token\_program},\; \text{mint}],\; \text{ATA\_program}\bigr) \]

Cette derivation est purement deterministe : connaissant le wallet et le mint, n’importe qui peut calculer l’adresse de l’ATA sans interaction reseau.

La visualisation ci-dessous montre le processus de derivation d’un ATA.

Hide code cell source

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import seaborn as sns

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
palette = sns.color_palette("muted")
fig, ax = plt.subplots(figsize=(12, 5))

# Boites d'entree
for label, x, color in [("Wallet Pubkey", -3.5, palette[2]),
                         ("Mint Pubkey", 0, palette[3]),
                         ("Token Program ID", 3.5, palette[0])]:
    ax.add_patch(mpatches.FancyBboxPatch((x-1.3, 2.15), 2.6, 0.7,
                 boxstyle="round,pad=0.1", facecolor=color, edgecolor="white", lw=2, alpha=0.85))
    ax.text(x, 2.5, label, ha="center", va="center", fontsize=9, fontweight="bold", color="white")
    ax.annotate("", xy=(0, 0.85), xytext=(x, 2.15),
                arrowprops=dict(arrowstyle="-|>", color="#888888", lw=1.5))

# Boite PDA
ax.add_patch(mpatches.FancyBboxPatch((-1.8, 0.15), 3.6, 0.7,
             boxstyle="round,pad=0.1", facecolor=palette[4], edgecolor="white", lw=2, alpha=0.85))
ax.text(0, 0.5, "findProgramAddress(seeds, ATA_program)",
        ha="center", va="center", fontsize=9, fontweight="bold", color="white")

# Fleche et boite ATA
ax.annotate("", xy=(0, -1.15), xytext=(0, 0.15),
            arrowprops=dict(arrowstyle="-|>", color="#888888", lw=1.5))
ax.add_patch(mpatches.FancyBboxPatch((-1.5, -1.85), 3.0, 0.7,
             boxstyle="round,pad=0.1", facecolor=palette[1], edgecolor="white", lw=2, alpha=0.85))
ax.text(0, -1.5, "Associated Token Account\n(adresse deterministe)",
        ha="center", va="center", fontsize=9, fontweight="bold", color="white")

ax.set_xlim(-6, 6); ax.set_ylim(-2.5, 3.5)
ax.set_title("Derivation d'un Associated Token Account (ATA)", fontweight="bold")
ax.axis("off")
plt.show()
_images/4a6c9d417a854d5d0fa81afc4af93f1133efa58f2154d16f01ef9aa44bc66626.png

Remarque 72 (Pourquoi les ATAs sont indispensables)

Sans les ATAs, un expediteur devrait demander hors-chaine l’adresse du Token Account du destinataire. Avec les ATAs, l’adresse est calculable par quiconque connait le wallet et le mint, ce qui simplifie les transferts, les airdrops et les interactions DeFi. Un wallet peut creer des Token Accounts non-ATA (dits auxiliary accounts), mais la convention de l’ecosysteme privilegie les ATAs.

Le code Anchor suivant montre comment utiliser la contrainte associated_token pour initialiser ou referencer un ATA.

use anchor_lang::prelude::*;
use anchor_spl::{
    associated_token::AssociatedToken,
    token::{Mint, Token, TokenAccount},
};

#[derive(Accounts)]
pub struct InitAta<'info> {
    #[account(
        init_if_needed,
        payer = payer,
        associated_token::mint = mint,
        associated_token::authority = owner,
    )]
    pub token_account: Account<'info, TokenAccount>,

    pub mint: Account<'info, Mint>,

    /// CHECK: Le proprietaire de l'ATA, pas necessairement le signataire.
    pub owner: UncheckedAccount<'info>,

    #[account(mut)]
    pub payer: Signer<'info>,

    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
}

Remarque 73 (Le flag init_if_needed)

La contrainte init_if_needed cree l’ATA automatiquement s’il n’existe pas encore, ou le reference s’il existe deja. Cela evite que la transaction echoue lorsque le destinataire n’a jamais detenu ce token. Cette contrainte necessite d’activer la feature correspondante dans le Cargo.toml :

[dependencies]
anchor-lang = { version = "0.30", features = ["init-if-needed"] }

## Token Extensions (Token-2022)

Le SPL Token Program original atteint ses limites face a des besoins avances.
Plutot que de le modifier (ce qui briserait la compatibilite), Solana Labs a developpe **Token-2022** (aussi appele *Token Extensions Program*).

````{prf:definition} Token-2022 (Token Extensions Program)
:label: ch11-def-token-2022

**Token-2022** est le programme de tokens de nouvelle generation de Solana, deploye a l'adresse `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`.
Il est entierement compatible avec les instructions du Token Program original tout en ajoutant des **extensions** configurables au niveau du Mint ou du Token Account.

Les extensions sont activees a l'initialisation du Mint et ne peuvent pas etre ajoutees apres coup.
Les extensions principales sont :

| Extension | Description |
|---|---|
| **Transfer Fees** | Preleve automatiquement un pourcentage sur chaque transfert |
| **Confidential Transfers** | Chiffre les montants des transferts |
| **Metadata Pointer** | Lie le Mint a des metadonnees on-chain |
| **Permanent Delegate** | Delegue permanent pouvant transferer ou bruler tout token |
| **Non-Transferable** | Tokens *soulbound* non transferables |
| **Interest-Bearing** | Solde ajuste par un taux d'interet configurable |
| **Transfer Hook** | Execute un programme externe a chaque transfert |

Exemple 34 (Token avec frais de transfert de 1 %)

Voici la configuration conceptuelle d’un Mint avec 1 % de frais de transfert :

use anchor_lang::prelude::*;
use anchor_spl::token_2022::spl_token_2022::{
    extension::transfer_fee::TransferFeeConfig,
};

// Configuration des frais de transfert
// transfer_fee_basis_points = 100 signifie 1 % (100 / 10_000)
// maximum_fee = 1_000_000 signifie 1 token max de frais (avec 6 decimales)

// Via CLI :
// spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
//     create-token --decimals 6 \
//     --transfer-fee 100 1000000

// Lors d'un transfert de 10 000 tokens :
// - Montant recu par le destinataire : 9 900 tokens
// - Frais retenus dans le Token Account : 100 tokens
// - Les frais accumules sont recoltables par la fee_authority

Les frais sont preleves dans le Token Account du destinataire et sont reclames separement par l’autorite configuree via l’instruction HarvestWithheldTokensToMint. Ce mecanisme est utilise par certains protocoles DeFi pour financer leur tresorerie de maniere transparente et automatique.


````{prf:remark} Coexistence Token Program et Token-2022
:label: ch11-rem-token-2022-migration

Token-2022 **coexiste** avec le Token Program original ; il ne le remplace pas.
Les deux programmes fonctionnent en parallele, et les applications doivent gerer les deux cas.
Les programmes Anchor doivent specifier `token_program` explicitement, le crate `anchor_spl` fournit le module `token_2022` en complement de `token`, et la migration d'un token existant necessite la creation d'un nouveau Mint avec un mecanisme d'echange.

Remarque 74 (Extensions et taille des comptes)

Chaque extension augmente la taille du Mint Account ou du Token Account. Par exemple, l’extension Transfer Fees ajoute 108 octets au Mint Account. Puisque le loyer est proportionnel a la taille du compte, les tokens avec extensions coutent plus cher a creer.

Il est recommande d’utiliser l’instruction GetMinimumBalanceForRentExemption avec la taille exacte du Mint (base + extensions) pour calculer le loyer requis avant l’initialisation.

Programme Anchor complet#

Cette section presente un programme Anchor complet illustrant les quatre operations fondamentales : creation du Mint et de l’ATA, emission, transfert et destruction de tokens.

Remarque 75 (CPI vers le Token Program avec anchor_spl)

Le crate anchor_spl::token fournit des wrappers type-safe pour les CPI vers le SPL Token Program : token::mint_to, token::transfer, token::burn, token::approve, token::revoke, et token::close_account. Chaque fonction attend un CpiContext contenant les comptes requis et le programme cible. Le CpiContext peut inclure des signer seeds pour les CPI signees par un PDA.

use anchor_lang::prelude::*;
use anchor_spl::{
    associated_token::AssociatedToken,
    token::{self, Burn, Mint, MintTo, Token, TokenAccount, Transfer},
};

declare_id!("11111111111111111111111111111111");

#[program]
pub mod spl_token_demo {
    use super::*;

    /// Cree un nouveau Mint et l'ATA de l'autorite.
    pub fn create_token(ctx: Context<CreateToken>) -> Result<()> {
        msg!(
            "Mint cree : {}. ATA initialise : {}",
            ctx.accounts.mint.key(),
            ctx.accounts.token_account.key()
        );
        Ok(())
    }

    /// Emet `amount` tokens vers le Token Account du destinataire.
    pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
        token::mint_to(
            CpiContext::new(ctx.accounts.token_program.to_account_info(), MintTo {
                mint: ctx.accounts.mint.to_account_info(),
                to: ctx.accounts.token_account.to_account_info(),
                authority: ctx.accounts.authority.to_account_info(),
            }),
            amount,
        )
    }

    /// Transfere `amount` tokens de l'expediteur vers le destinataire.
    pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
        token::transfer(
            CpiContext::new(ctx.accounts.token_program.to_account_info(), Transfer {
                from: ctx.accounts.from.to_account_info(),
                to: ctx.accounts.to.to_account_info(),
                authority: ctx.accounts.authority.to_account_info(),
            }),
            amount,
        )
    }

    /// Brule (detruit) `amount` tokens du Token Account de l'autorite.
    pub fn burn_tokens(ctx: Context<BurnTokens>, amount: u64) -> Result<()> {
        token::burn(
            CpiContext::new(ctx.accounts.token_program.to_account_info(), Burn {
                mint: ctx.accounts.mint.to_account_info(),
                from: ctx.accounts.token_account.to_account_info(),
                authority: ctx.accounts.authority.to_account_info(),
            }),
            amount,
        )
    }
}

// --- Comptes pour chaque instruction ---

#[derive(Accounts)]
pub struct CreateToken<'info> {
    #[account(init, payer = authority,
              mint::decimals = 6,
              mint::authority = authority.key(),
              mint::freeze_authority = authority.key())]
    pub mint: Account<'info, Mint>,
    #[account(init, payer = authority,
              associated_token::mint = mint,
              associated_token::authority = authority)]
    pub token_account: Account<'info, TokenAccount>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
}

#[derive(Accounts)]
pub struct MintTokens<'info> {
    #[account(mut, mint::authority = authority.key())]
    pub mint: Account<'info, Mint>,
    #[account(mut, token::mint = mint)]
    pub token_account: Account<'info, TokenAccount>,
    pub authority: Signer<'info>,
    pub token_program: Program<'info, Token>,
}

#[derive(Accounts)]
pub struct TransferTokens<'info> {
    pub mint: Account<'info, Mint>,
    #[account(mut, token::mint = mint, token::authority = authority)]
    pub from: Account<'info, TokenAccount>,
    #[account(mut, token::mint = mint)]
    pub to: Account<'info, TokenAccount>,
    pub authority: Signer<'info>,
    pub token_program: Program<'info, Token>,
}

#[derive(Accounts)]
pub struct BurnTokens<'info> {
    #[account(mut)]
    pub mint: Account<'info, Mint>,
    #[account(mut, token::mint = mint, token::authority = authority)]
    pub token_account: Account<'info, TokenAccount>,
    pub authority: Signer<'info>,
    pub token_program: Program<'info, Token>,
}

Exemple 35 (Flux complet d’utilisation)

Sequence typique d’appels : (1) create_token cree le Mint (6 decimales) et l’ATA avec une supply de 0 ; (2) mint_tokens(1_000_000_000) emet 1 000 tokens (1 milliard d’unites de base) ; (3) transfer_tokens(500_000_000) envoie 500 tokens vers un autre Token Account ; (4) burn_tokens(100_000_000) detruit 100 tokens, decrementant la supply du Mint. A la fin, la supply totale est de 900 tokens (1 000 emis - 100 brules).

Exemple 36 (CPI signee par un PDA)

Lorsqu’un programme possede des tokens via un PDA (coffre-fort de tresorerie), les CPI doivent etre signees par ce PDA via CpiContext::new_with_signer :

let seeds = &[b"vault".as_ref(), &[ctx.bumps.vault_authority]];
let signer = &[&seeds[..]];
token::transfer(
    CpiContext::new_with_signer(
        ctx.accounts.token_program.to_account_info(),
        Transfer {
            from: ctx.accounts.vault_token_account.to_account_info(),
            to: ctx.accounts.recipient_token_account.to_account_info(),
            authority: ctx.accounts.vault_authority.to_account_info(),
        },
        signer,
    ),
    amount,
)?;

Le runtime Solana verifie que les seeds produisent bien l’adresse du PDA avant d’autoriser la signature.


````{prf:remark} Securite des operations Token
:label: ch11-rem-security

Plusieurs points de securite sont critiques lors de la manipulation de tokens SPL en Anchor :

- **Valider le Mint** : toujours verifier que le Token Account est associe au bon Mint via la contrainte `token::mint = mint`. Sans cette verification, un attaquant pourrait substituer un token sans valeur.
- **Valider l'autorite** : utiliser `token::authority = authority` pour s'assurer que le signataire est bien le proprietaire du Token Account.
- **Verifier la supply** : avant une emission, verifier que le montant ne provoque pas de depassement de `u64` (overflow).
- **Fermer les comptes inutilises** : les Token Accounts vides continuent de consommer du loyer. L'instruction `CloseAccount` recupere le SOL bloque.

Resume#

Le tableau ci-dessous recapitule les concepts fondamentaux introduits dans ce chapitre.

Concept

Description

SPL Token Program

Programme natif gerant creation, emission, transfert et destruction de tokens

Mint Account

Compte (82 octets) representant un type de token : supply, decimals, autorites

Token Account

Compte (165 octets) stockant le solde d’un utilisateur pour un Mint

Authority

Cle disposant de droits privilegies : Mint, Freeze, Owner, Close

Decimales

Granularite : 9 (SOL-like), 6 (USDC-like), 0 (NFT). Irreversible

Associated Token Account

Adresse derivee de maniere deterministe a partir du wallet, du mint et du token program

Token-2022

Programme de nouvelle generation avec extensions configurables

anchor_spl::token

Wrappers type-safe pour les CPI vers le SPL Token Program

CPI signee par PDA

Signature d’operations Token via CpiContext::new_with_signer

Le chapitre suivant explorera les NFTs et le protocole Metaplex, qui s’appuient sur le standard SPL Token pour representer des actifs numeriques uniques avec des metadonnees enrichies.