Cross-Program Invocation et composabilite#
Le chapitre 6 a introduit le mecanisme de Cross-Program Invocation (CPI) et ses regles fondamentales : heritage des privileges, limite de profondeur et signature PDA. Nous y avions presente invoke() et invoke_signed() du SDK bas niveau. Mais dans un projet Anchor, on ne manipule presque jamais ces fonctions directement. Anchor fournit une abstraction — le CpiContext — qui encapsule la construction des appels inter-programmes de maniere type-safe. Ce chapitre est entierement consacre a la maitrise pratique de cette abstraction.
Les CPI sont le ciment de l’ecosysteme Solana. Chaque transfert de SOL, chaque emission de token, chaque creation de compte passe par un appel au System Program ou au Token Program. Un programme DeFi realiste effectue des dizaines de CPI par transaction : transferts de tokens vers un pool, emission de LP tokens, redistribution de recompenses. Comprendre les CPI en profondeur est donc un prealable a tout programme non trivial.
Ce chapitre detaille d’abord les mecaniques internes des CPI (regles de privileges, limites), puis presente le CpiContext d’Anchor. Nous construirons ensuite quatre patterns d’integration complets et corrects — transfert de SOL, transfert de tokens SPL, emission de tokens, creation de comptes — avant de developper un programme de coffre-fort (vault) illustrant la signature PDA. Nous terminerons par les principes d’architecture modulaire et de composabilite.
CPI en detail#
Définition 128 (Cross-Program Invocation (CPI))
Un Cross-Program Invocation (CPI) est le mecanisme par lequel un programme Solana invoque une instruction sur un autre programme pendant sa propre execution. Le programme appelant (caller) construit une structure Instruction (contenant le program_id cible, la liste des AccountMeta et les donnees serialisees) puis la soumet au runtime via l’une des deux fonctions :
invoke(instruction, account_infos): effectue un appel standard. Les privileges de signature et d’ecriture sont herites du caller. Si un compte est marque comme signataire dans la transaction originale, le callee le voit comme signataire egalement.invoke_signed(instruction, account_infos, signer_seeds): effectue un appel avec une signature PDA supplementaire. Le runtime verifie que les seeds fournies derivent bien l’adresse du PDA a partir duprogram_idde l’appelant, et ajoute cette adresse a la liste des signataires pour le callee.
Le programme appele (callee) ne peut pas distinguer un CPI d’un appel de transaction direct : il recoit un contexte d’execution identique et applique ses propres validations.
Définition 129 (Regles d’escalade de privileges)
Les regles d’escalade de privileges sont determinantes pour la securite des CPI. Elles se resument en trois principes :
Heritage des signataires : si le caller detient le privilege de signature sur un compte (parce que l’utilisateur a signe la transaction, ou parce qu’un CPI precedent a ajoute une signature PDA), alors le callee herite de ce privilege automatiquement.
Heritage de l’ecriture : si le caller a declare un compte comme writable, le callee peut egalement ecrire dans ce compte.
Pas d’escalade : le callee ne peut jamais obtenir un privilege que le caller ne possede pas. Si un compte n’est pas signataire pour le caller, il ne le sera pas non plus pour le callee (sauf ajout explicite via
invoke_signed).
Ces regles garantissent que les CPI ne peuvent pas contourner les permissions declarees dans la transaction originale. Le seul moyen d’ajouter un nouveau signataire est invoke_signed, qui ne fonctionne qu’avec des PDA derives du programme appelant.
Remarque 98 (Limite de profondeur des CPI)
Solana impose une profondeur maximale de 4 niveaux pour les CPI. Si le programme A appelle B qui appelle C qui appelle D qui appelle E, l’appel a E echouera avec une erreur CallDepth. Cette limite existe pour garantir des temps d’execution bornes et prevenir les boucles infinies. En pratique, la majorite des programmes n’utilisent qu’un ou deux niveaux de CPI. Le budget de compute units etant partage sur toute la chaine d’appels, chaque niveau supplementaire reduit le budget disponible pour les niveaux suivants.
Remarque 99 (Protection contre la reentrance)
Solana previent la reentrance par construction : un programme ne peut pas effectuer un CPI vers lui-meme, ni directement ni indirectement via une chaine d’appels intermediaires. Si le programme A appelle B et que B tente d’appeler A, le runtime rejette l’appel. Cette protection elimine toute une classe de vulnerabilites bien connue sur Ethereum (l’attaque par reentrance du DAO hack de 2016). Le developpeur Solana n’a pas besoin d’implementer de reentrancy guard : le runtime s’en charge.
Remarque 100 (Cout en compute units des CPI)
Chaque CPI consomme un minimum de 1 000 compute units (CU) rien que pour l’overhead de l’appel (changement de contexte, verification des privileges). A cela s’ajoutent les CU consommees par l’instruction appelee elle-meme. Un transfert SOL via le System Program coute environ 2 500 CU au total, tandis qu’un token::transfer coute environ 4 500 CU. Avec le budget par defaut de 200 000 CU par instruction, on peut effectuer plusieurs dizaines de CPI dans une seule instruction, mais il est prudent de mesurer et d’optimiser lorsque la logique est complexe. Utilisez solana_program::log::sol_log_compute_units() pour mesurer la consommation a differents points de votre programme.
CpiContext dans Anchor#
Définition 130 (CpiContext)
Le CpiContext est l’abstraction Anchor qui encapsule les elements necessaires a un appel CPI : le programme cible et les comptes requis. Il existe en deux variantes :
CpiContext::new(program, accounts): pour un CPI standard ou les signatures existantes suffisent. Le parametreprogramest uneAccountInforepresentant le programme cible, etaccountsest une structure implementant le traitToAccountMetas+ToAccountInfos(typiquement une structure CPI fournie par le crate du programme cible).CpiContext::new_with_signer(program, accounts, signer_seeds): pour un CPI ou le programme appelant doit signer au nom d’un PDA. Le parametresigner_seedsest un&[&[&[u8]]]contenant les seeds et le bump de chaque PDA signataire.
Le CpiContext est passe aux fonctions CPI helpers d’Anchor (comme token::transfer, system_program::transfer, etc.) qui construisent et soumettent l’instruction correspondante.
Le pattern general pour un CPI avec Anchor est le suivant :
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, Transfer};
// CPI standard : l'utilisateur est signataire
pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
let cpi_accounts = Transfer {
from: ctx.accounts.source.to_account_info(),
to: ctx.accounts.destination.to_account_info(),
authority: ctx.accounts.owner.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::transfer(cpi_ctx, amount)?;
Ok(())
}
// CPI avec signature PDA : le programme signe au nom d'un PDA
pub fn transfer_from_vault(ctx: Context<VaultTransfer>, amount: u64) -> Result<()> {
let seeds = &[
b"vault".as_ref(),
ctx.accounts.authority.key.as_ref(),
&[ctx.accounts.vault.bump],
];
let signer_seeds = &[&seeds[..]];
let cpi_accounts = Transfer {
from: ctx.accounts.vault_token_account.to_account_info(),
to: ctx.accounts.destination.to_account_info(),
authority: ctx.accounts.vault.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new_with_signer(
cpi_program,
cpi_accounts,
signer_seeds,
);
token::transfer(cpi_ctx, amount)?;
Ok(())
}
Remarque 101 (Structure des signer seeds)
Le parametre signer_seeds de CpiContext::new_with_signer a le type &[&[&[u8]]]. La premiere dimension represente la liste des PDA signataires (on peut en avoir plusieurs). La deuxieme dimension represente les seeds d’un PDA individuel (incluant le bump). La troisieme dimension represente chaque seed individuel sous forme de tranche d’octets. En pratique, pour un seul PDA avec les seeds [b"vault", authority_key, bump], on ecrit :
let seeds = &[
b"vault".as_ref(),
authority.key.as_ref(),
&[bump],
];
let signer_seeds = &[&seeds[..]];
## Patterns d'integration
Cette section presente quatre patterns CPI complets avec Anchor. Chaque pattern inclut la structure de comptes et la logique d'instruction.
### Pattern 1 : Transfert de SOL via System Program
Le transfert de SOL natif passe par le System Program. Le signataire de la transaction est l'expediteur.
````{prf:example} Transfert de SOL
:label: ch15-ex-transfer-sol
Le programme ci-dessous transfere des lamports du compte de l'utilisateur vers un destinataire via un CPI au System Program. L'utilisateur doit etre signataire et son compte doit etre mutable (ses lamports diminuent).
````rust
use anchor_lang::prelude::*;
use anchor_lang::system_program;
declare_id!("TransferSo1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
#[program]
pub mod sol_transfer {
use super::*;
pub fn send_sol(ctx: Context<SendSol>, amount: u64) -> Result<()> {
let cpi_accounts = system_program::Transfer {
from: ctx.accounts.sender.to_account_info(),
to: ctx.accounts.recipient.to_account_info(),
};
let cpi_program = ctx.accounts.system_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
system_program::transfer(cpi_ctx, amount)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct SendSol<'info> {
#[account(mut)]
pub sender: Signer<'info>,
/// CHECK: Le destinataire est un portefeuille quelconque.
#[account(mut)]
pub recipient: AccountInfo<'info>,
pub system_program: Program<'info, System>,
}
Le compte `sender` est de type `Signer`, ce qui garantit qu'il a signe la transaction. Son privilege de signature est automatiquement herite par le System Program via le CPI. Le compte `recipient` est annote `/// CHECK:` car Anchor n'effectue aucune validation supplementaire sur un `AccountInfo` brut --- le commentaire documente la raison.
### Pattern 2 : Transfert de tokens SPL
Le transfert de tokens SPL passe par le Token Program. L'autorite du compte source doit etre signataire.
````{prf:example} Transfert de tokens SPL
:label: ch15-ex-transfer-spl
Ce programme transfere des tokens SPL du compte source vers le compte destination. L'autorite (proprietaire) du compte source signe la transaction.
````rust
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, TokenAccount, Transfer};
declare_id!("TokenTrXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
#[program]
pub mod spl_transfer {
use super::*;
pub fn transfer_tokens(
ctx: Context<TransferTokens>,
amount: u64,
) -> Result<()> {
let cpi_accounts = Transfer {
from: ctx.accounts.from_ata.to_account_info(),
to: ctx.accounts.to_ata.to_account_info(),
authority: ctx.accounts.owner.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::transfer(cpi_ctx, amount)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct TransferTokens<'info> {
#[account(
mut,
token::authority = owner,
)]
pub from_ata: Account<'info, TokenAccount>,
#[account(mut)]
pub to_ata: Account<'info, TokenAccount>,
pub owner: Signer<'info>,
pub token_program: Program<'info, Token>,
}
````{prf:remark} Autorite du compte de token
:label: ch15-rem-token-authority
La contrainte `token::authority = owner` verifie que le champ `authority` du compte de token source correspond bien a la cle publique du signataire `owner`. Cette verification est indispensable : sans elle, un attaquant pourrait passer un compte de token dont il n'est pas le proprietaire et le programme tenterait un transfert non autorise (qui echouerait au niveau du Token Program, mais il est preferable de rejeter l'instruction le plus tot possible). L'autorite peut etre soit un portefeuille utilisateur (signataire direct), soit un PDA pour lequel le programme peut signer via `invoke_signed`.
Pattern 3 : Emission de tokens (MintTo)#
L’emission de tokens utilise l’instruction mint_to du Token Program. L’autorite du mint doit signer. Lorsque le mint est controle par un PDA, le programme signe via CpiContext::new_with_signer.
Exemple 44 (Emission de tokens depuis un PDA)
Ce programme emet des tokens depuis un mint dont l’autorite est un PDA derive du programme. Les seeds incluent le prefixe "mint-authority" et le bump stocke dans le compte d’etat.
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Mint, MintTo, Token, TokenAccount};
declare_id!("MintToXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
#[program]
pub mod mint_program {
use super::*;
pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
let seeds = &[
b"mint-authority".as_ref(),
&[ctx.bumps.mint_authority],
];
let signer_seeds = &[&seeds[..]];
let cpi_accounts = MintTo {
mint: ctx.accounts.mint.to_account_info(),
to: ctx.accounts.destination.to_account_info(),
authority: ctx.accounts.mint_authority.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new_with_signer(
cpi_program,
cpi_accounts,
signer_seeds,
);
token::mint_to(cpi_ctx, amount)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct MintTokens<'info> {
#[account(
mut,
mint::authority = mint_authority,
)]
pub mint: Account<'info, Mint>,
#[account(mut)]
pub destination: Account<'info, TokenAccount>,
/// CHECK: PDA verifie par la contrainte seeds.
#[account(
seeds = [b"mint-authority"],
bump,
)]
pub mint_authority: AccountInfo<'info>,
pub token_program: Program<'info, Token>,
}
Le pattern est identique a un transfert de tokens, sauf que l'autorite n'est pas un signataire humain mais un PDA. Le runtime verifie que les seeds fournies dans `signer_seeds` derivent bien l'adresse de `mint_authority` a partir du `program_id` de notre programme. Si la verification reussit, le Token Program voit `mint_authority` comme un signataire valide.
### Pattern 4 : Creation de compte via System Program
La creation dynamique de comptes dans une instruction est utile lorsque le nombre de comptes a creer n'est pas connu a l'avance ou lorsque l'on ne peut pas utiliser la contrainte `init` d'Anchor.
````{prf:example} Creation de compte via CPI
:label: ch15-ex-create-account
Ce programme cree un nouveau compte via un CPI au System Program. Le nouveau compte est un PDA derive du programme appelant, qui signe via `invoke_signed`.
````rust
use anchor_lang::prelude::*;
use anchor_lang::system_program::{self, CreateAccount};
declare_id!("CreateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
#[program]
pub mod account_creator {
use super::*;
pub fn create_data_account(
ctx: Context<CreateDataAccount>,
space: u64,
) -> Result<()> {
let rent = Rent::get()?;
let lamports = rent.minimum_balance(space as usize);
let seeds = &[
b"data".as_ref(),
ctx.accounts.payer.key.as_ref(),
&[ctx.bumps.new_account],
];
let signer_seeds = &[&seeds[..]];
let cpi_accounts = CreateAccount {
from: ctx.accounts.payer.to_account_info(),
to: ctx.accounts.new_account.to_account_info(),
};
let cpi_program = ctx.accounts.system_program.to_account_info();
let cpi_ctx = CpiContext::new_with_signer(
cpi_program,
cpi_accounts,
signer_seeds,
);
system_program::create_account(
cpi_ctx,
lamports,
space,
&ctx.program_id,
)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct CreateDataAccount<'info> {
#[account(mut)]
pub payer: Signer<'info>,
/// CHECK: Sera initialise par le CPI create_account.
#[account(
mut,
seeds = [b"data", payer.key().as_ref()],
bump,
)]
pub new_account: AccountInfo<'info>,
pub system_program: Program<'info, System>,
}
La creation de compte via CPI est necessaire lorsque la contrainte `init` d'Anchor n'est pas utilisable --- par exemple si le programme doit creer des comptes avec une taille dynamique determinee par les parametres d'instruction. Le CPI `CreateAccount` alloue l'espace, transfere les lamports pour l'exemption de loyer, et assigne le proprietaire en une seule operation atomique.
## PDA comme signataire
Un PDA (Program Derived Address) est une adresse qui n'a pas de cle privee correspondante. Seul le programme qui l'a derive --- celui dont le `program_id` a servi au calcul --- peut « signer » pour ce PDA via `invoke_signed`. Cette propriete fait des PDA le mecanisme central pour le controle programmatique d'actifs sur Solana.
Le principe est le suivant : lorsqu'un programme appelle `invoke_signed` avec des seeds et un bump, le runtime Solana recalcule `hash(seeds || program_id || [bump] || "ProgramDerivedAddress")` et verifie que le resultat correspond a l'une des adresses dans la liste de comptes. Si c'est le cas, cette adresse est marquee comme signataire pour le callee. Le programme appelant n'a jamais besoin de posseder une cle privee.
````{prf:definition} PDA signer
:label: ch15-def-pda-signer
Un **PDA signer** est un PDA utilise comme signataire dans un CPI via `invoke_signed`. Le programme appelant fournit les seeds et le bump qui derivent l'adresse du PDA a partir de son propre `program_id`. Le runtime verifie la derivation et, si elle est correcte, ajoute le PDA a la liste des signataires pour l'instruction appelee. Ce mecanisme permet a un programme de controler des actifs (SOL, tokens) stockes dans un compte PDA sans jamais detenir de cle privee.
Le programme de coffre-fort ci-dessous illustre ce pattern. Il comprend deux instructions : initialize_vault cree un PDA qui recevra du SOL, et withdraw permet a l’autorite de retirer du SOL en faisant signer le PDA via CPI.
use anchor_lang::prelude::*;
use anchor_lang::system_program;
declare_id!("Vau1tXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
#[program]
pub mod vault {
use super::*;
pub fn initialize_vault(ctx: Context<InitializeVault>) -> Result<()> {
let vault = &mut ctx.accounts.vault;
vault.authority = ctx.accounts.authority.key();
vault.bump = ctx.bumps.vault;
Ok(())
}
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
let cpi_accounts = system_program::Transfer {
from: ctx.accounts.authority.to_account_info(),
to: ctx.accounts.vault.to_account_info(),
};
let cpi_ctx = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
cpi_accounts,
);
system_program::transfer(cpi_ctx, amount)?;
Ok(())
}
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
let seeds = &[
b"vault".as_ref(),
ctx.accounts.authority.key.as_ref(),
&[ctx.accounts.vault.bump],
];
let signer_seeds = &[&seeds[..]];
// Transfert direct de lamports depuis le PDA
// Le PDA est un compte possede par le programme, donc on
// peut reduire ses lamports directement.
let vault_info = ctx.accounts.vault.to_account_info();
let authority_info = ctx.accounts.authority.to_account_info();
**vault_info.try_borrow_mut_lamports()? -= amount;
**authority_info.try_borrow_mut_lamports()? += amount;
Ok(())
}
}
#[derive(Accounts)]
pub struct InitializeVault<'info> {
#[account(
init,
seeds = [b"vault", authority.key().as_ref()],
bump,
payer = authority,
space = 8 + 32 + 1,
)]
pub vault: Account<'info, Vault>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Deposit<'info> {
#[account(
mut,
seeds = [b"vault", authority.key().as_ref()],
bump = vault.bump,
has_one = authority,
)]
pub vault: Account<'info, Vault>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Withdraw<'info> {
#[account(
mut,
seeds = [b"vault", authority.key().as_ref()],
bump = vault.bump,
has_one = authority,
)]
pub vault: Account<'info, Vault>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct Vault {
pub authority: Pubkey,
pub bump: u8,
}
Le diagramme suivant visualise le flux d’un retrait depuis le coffre-fort : l’utilisateur appelle le programme, qui signe au nom du PDA pour transferer les SOL.
Remarque 102 (Securite des PDA : toujours rederiver)
Il est imperatif de toujours rederiver le PDA dans l’instruction a partir des seeds attendues, plutot que de faire confiance a l’adresse passee par le client. C’est exactement ce que font les contraintes seeds et bump d’Anchor : elles recalculent l’adresse a partir des seeds et verifient qu’elle correspond au compte fourni. Sans cette verification, un attaquant pourrait passer un compte arbitraire qui n’est pas le PDA attendu, et le programme pourrait ecrire dans un compte non autorise. La regle est simple : ne jamais faire confiance, toujours verifier.
Architecture modulaire#
Définition 131 (Composabilite)
La composabilite (composability) est la capacite des programmes Solana a s’appeler mutuellement via des CPI, construisant ainsi des fonctionnalites complexes a partir de briques elementaires. Un programme de staking compose le Token Program (pour les transferts de tokens), le System Program (pour les transferts de SOL) et eventuellement un programme de recompenses personnalise. Chaque programme expose une interface stable (definie par son IDL) que les autres programmes peuvent invoquer. Cette architecture est analogue aux appels de fonctions de bibliotheque : chaque programme est une « librairie » deployee on-chain, invocable par n’importe quel autre programme.
Pour qu’un programme soit veritablement composable, il doit respecter plusieurs principes de conception :
Interfaces stables : ne pas modifier la disposition des comptes (account layout) ni le format des donnees d’instruction entre les mises a jour. Ajouter de nouvelles instructions est acceptable, mais modifier les existantes casse les programmes qui en dependent.
PDA pour l’etat programme : utiliser des PDA pour tout etat controle par le programme. Cela permet a d’autres programmes d’interagir avec cet etat de maniere deterministe, sans coordination off-chain.
Emission d’evenements : emettre des evenements (
emit!en Anchor) pour l’indexation off-chain. Les indexeurs et les interfaces utilisateur dependent de ces evenements pour suivre l’activite du programme.Documentation de l’IDL : publier un IDL complet et a jour. L’IDL est le « contrat d’interface » du programme : il decrit les instructions, les comptes et les types de donnees. Sans IDL, l’integration par d’autres programmes est fastidieuse.
La visualisation suivante montre l’architecture en couches d’une application Solana composable.
Exemple 45 (Programme de staking composable)
Un programme de staking illustre parfaitement la composabilite. Il compose trois programmes :
Token Program : pour les transferts de tokens. L’utilisateur depose des tokens dans un compte PDA du programme de staking (CPI
token::transferavec l’utilisateur comme signataire). Lors du retrait, le programme signe au nom du PDA pour renvoyer les tokens (CPItoken::transferavecnew_with_signer).System Program : pour la creation de comptes d’etat. Le programme cree un compte PDA par staker pour enregistrer le montant stake et l’horodatage du depot (CPI implicite via la contrainte
initd’Anchor).Programme de recompenses (custom) : pour calculer et distribuer les recompenses. Le programme de staking effectue un CPI vers le programme de recompenses, qui lui-meme effectue un CPI vers le Token Program pour emettre les tokens de recompense depuis un mint PDA.
Cette composition cree un systeme de staking complet sans dupliquer la logique de gestion de tokens ou de creation de comptes. Chaque brique est reutilisable et auditable independamment.
Remarque 103 (Crates d’interface pour l’integration)
Pour faciliter l’integration de votre programme par d’autres programmes, publiez un crate d’interface contenant :
Les structures de comptes (
#[derive(Accounts)]) necessaires aux CPI vers votre programme.Les fonctions CPI helpers (analogues a
token::transferousystem_program::transferfournis paranchor_spl).Les types de donnees (
#[account]) pour que les programmes appelants puissent deserialiser vos comptes.
Le crate anchor_spl est l’exemple canonique de ce pattern : il fournit les structures et fonctions CPI pour le Token Program, le Associated Token Account Program, et d’autres programmes SPL. Votre propre programme peut suivre le meme modele en publiant un crate separe (par convention nomme <votre_programme>-cpi) sur crates.io.
Resume#
Ce chapitre a approfondi le mecanisme de Cross-Program Invocation et les patterns pratiques pour la composabilite avec Anchor.
Concept |
Description |
|---|---|
CPI |
Appel d’un programme vers un autre pendant l’execution ; |
Privilege escalation |
Le callee herite des privileges (signer, writable) du caller ; pas d’escalade possible |
Profondeur max |
4 niveaux de CPI imbriques ; au-dela, erreur |
Anti-reentrance |
Un programme ne peut pas s’appeler lui-meme via CPI, ni directement ni indirectement |
|
CPI standard avec heritage des signataires existants |
|
CPI avec signature PDA ; le programme signe au nom du PDA |
Signer seeds |
|
Transfert SOL |
CPI vers System Program avec |
Transfert SPL |
CPI vers Token Program avec |
MintTo |
CPI vers Token Program avec |
CreateAccount |
CPI vers System Program pour creer un compte dynamiquement |
PDA signer |
PDA dont le programme peut signer via |
Composabilite |
Programmes qui s’appellent mutuellement, construisant des systemes complexes |
Crate d’interface |
Crate publie contenant les structures CPI pour l’integration par d’autres programmes |