Anchor
Token2022 avec Anchor

Token2022 avec Anchor

L'Extension Memo Transfer

L'extension memo_transfer (Transfert avec mémo) est une extension de compte de Token qui impose que tous les transferts entrants d'un compte de token incluent un mémo, facilitant ainsi le suivi des transactions et l'identification des utilisateurs.

Initialisation du Compte de Jeton

Comme Anchor ne possède aucune macro pour l'extension memo_transfer nous allons créer un compte de Token à l'aide de CPIs bruts.

Voici comment créer un compte de jeton avec l'extension memo_transfer :

rust
use anchor_lang::prelude::*;
use anchor_lang::system_program::{create_account, CreateAccount};
use anchor_spl::{
    token_2022::{
        initialize_account3,
        spl_token_2022::{extension::ExtensionType, pod::PodAccount},
        InitializeAccount3,
    },
    token_interface::{memo_transfer_initialize, MemoTransfer, Mint, Token2022},
};
 
// There is currently not an anchor constraint to automatically initialize the MemoTransfer extension
// We can manually create and initialize the token account via CPIs in the instruction handler
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
    // Calculate space required for token and extension data
    let token_account_size =
        ExtensionType::try_calculate_account_len::<PodAccount>(&[ExtensionType::MemoTransfer])?;
 
    // Calculate minimum lamports required for size of token account with extensions
    let lamports = (Rent::get()?).minimum_balance(token_account_size);
 
    // Invoke System Program to create new account with space for token account and extension data
    create_account(
        CpiContext::new(
            ctx.accounts.system_program.to_account_info(),
            CreateAccount {
                from: ctx.accounts.payer.to_account_info(),
                to: ctx.accounts.token_account.to_account_info(),
            },
        ),
        lamports,                          // Lamports
        token_account_size as u64,         // Space
        &ctx.accounts.token_program.key(), // Owner Program
    )?;
 
    // Initialize the standard token account data
    initialize_account3(CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        InitializeAccount3 {
            account: ctx.accounts.token_account.to_account_info(),
            mint: ctx.accounts.mint_account.to_account_info(),
            authority: ctx.accounts.payer.to_account_info(),
        },
    ))?;
 
    // Initialize the memo transfer extension
    // This instruction must come after the token account initialization
    memo_transfer_initialize(CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        MemoTransfer {
            token_program_id: ctx.accounts.token_program.to_account_info(),
            account: ctx.accounts.token_account.to_account_info(),
            owner: ctx.accounts.payer.to_account_info(),
        },
    ))?;
 
    Ok(())
}
 
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
 
    #[account(mut)]
    pub token_account: Signer<'info>,
    pub mint_account: InterfaceAccount<'info, Mint>,
    pub token_program: Program<'info, Token2022>,
    pub system_program: Program<'info, System>,
}

Transfert de Jetons avec un Mémo

Pour utiliser le programme Mémo sur Solana, nous avons deux options possibles.

Créer notre propre instruction mémo "brute" comme ceci :

ts
const message = "Hello, Solana";
 
new TransactionInstruction({
    keys: [{ pubkey: keypair.publicKey, isSigner: true, isWritable: true }],
    data: Buffer.from(message, "utf-8"), // Memo message. In this case it is "Hello, Solana"
    programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"), // Memo program that validates keys and memo message
});

Ou nous pouvons utiliser le SDK du programme Mémo après avoir téléchargé le package comme ceci :

 
npm i @solana/spl-memo

Dans notre exemple, nous allons utiliser la deuxième option, ce qui donnera le résultat suivant :

ts
const memoInstruction = createMemoInstruction(
    "Hello, world!",
    [keypair.publicKey],
);
 
const transferInstruction = createTransferCheckedInstruction(
    tokenAccount,
    mint.publicKey,
    destinationTokenAccount.publicKey,
    keypair.publicKey,
    BigInt(100e6),
    6,
    undefined,
    TOKEN_2022_PROGRAM_ID,
);
 
const transferTransaction = new Transaction().add(
    memoInstruction,
    transferInstruction,
);
 
const transferSignature = await sendAndConfirmTransaction(connection, transferTransaction, [keypair]);
 
console.log(`Tokens transferred with memo! Check out your TX here: https://explorer.solana.com/tx/${transferSignature}?cluster=devnet`);

Désactiver et Activer le Mémo

Si nous ne voulons pas imposer l'ajout d'un mémo lors du transfert, nous pouvons utiliser la fonction memo_transfer_disable comme ceci :

rust
use anchor_lang::prelude::*;
use anchor_spl::token_interface::{memo_transfer_disable, MemoTransfer, Token, Mint, Token2022};
 
#[derive(Accounts)]
pub struct DisableMemo<'info> {
    #[account(mut)]
    pub owner: Signer<'info>,
 
    #[account(
        mut,
        token::authority = owner,
    )]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub token_program: Program<'info, Token2022>,
}
 
pub fn disable_memo(
    ctx: Context<DisableMemo>,
) -> Result<()> {
    memo_transfer_disable(CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        MemoTransfer {
            token_program_id: ctx.accounts.token_program.to_account_info(),
            account: ctx.accounts.token_account.to_account_info(),
            owner: ctx.accounts.owner.to_account_info(),
        },
    ))?;
    
    Ok(())
}
Blueshift © 2025Commit: 6d01265