Programmes DeFi#

La finance decentralisee — DeFi — est sans doute l’application la plus transformatrice des blockchains programmables. En eliminant les intermediaires traditionnels (banques, courtiers, chambres de compensation), la DeFi reconstruit les services financiers fondamentaux — echange, pret, emprunt, assurance — directement sous forme de programmes on-chain. Sur Solana, la combinaison de frais minimes et de latence sub-seconde a permis l’emergence d’un ecosysteme DeFi particulierement dynamique, ou les protocoles interagissent entre eux avec une fluidite impossible sur les blockchains plus lentes.

Le chapitre 13 a pose les bases theoriques des pools de liquidite, de la formule du produit constant et des mecanismes DLMM. Ce chapitre elargit la perspective a l’ensemble de l’ecosysteme DeFi et, surtout, passe a l’implementation concrete. Nous construirons un programme AMM complet en Anchor, puis nous etudierons les protocoles de pret, le yield farming et les flash loans — quatre piliers qui, assembles, forment un systeme financier entier, ouvert et composable.

L’objectif est double : comprendre les mecanismes economiques qui sous-tendent chaque brique DeFi, et maitriser les contraintes techniques specifiques a leur implementation sur Solana. Chaque section combine theorie, code Anchor et visualisations pour offrir une vision complete du paysage.

Principes de la DeFi#

Définition 123 (Finance decentralisee (DeFi))

La finance decentralisee (Decentralized Finance, DeFi) designe l’ensemble des services financiers construits sur des blockchains programmables, fonctionnant sans intermediaire centralise. Les utilisateurs interagissent directement avec des programmes (smart contracts) qui executent les operations de maniere deterministe et transparente. Les services couverts incluent l’echange de tokens, le pret et l’emprunt, la gestion d’actifs, les produits derives et l’assurance.

La DeFi repose sur quatre principes fondamentaux qui la distinguent de la finance traditionnelle :

  • Sans permission (permissionless) : toute personne disposant d’un portefeuille peut interagir avec n’importe quel protocole, sans verification d’identite ni approbation prealable. Il n’existe ni horaires d’ouverture, ni barriere geographique.

  • Composabilite (composability) : les programmes DeFi sont interoperables. Un utilisateur peut, dans une seule transaction, emprunter sur un protocole de lending, echanger les fonds sur un AMM et fournir le resultat a un vault de yield farming. Cette propriete est surnommee money legos.

  • Transparence : le code source des programmes, l’etat des pools et l’historique des transactions sont publiquement verifiables. Chaque utilisateur peut auditer le comportement exact d’un protocole.

  • Non-custodial : les utilisateurs conservent le controle de leurs actifs. Les fonds ne sont jamais confies a une entite centralisee ; ils sont detenus par des comptes programmes (PDA) dont la logique est immuable ou gouvernee collectivement.

Remarque 89 (Risques de la DeFi)

Malgre ses promesses, la DeFi comporte des risques significatifs. Les bugs de smart contracts peuvent entrainer la perte irremediable de fonds (cf. les exploits de Wormhole, Mango Markets). La manipulation d’oracle permet a un attaquant de fausser les prix utilises par les protocoles. L”impermanent loss affecte les fournisseurs de liquidite lorsque les prix divergent. Les rug pulls surviennent lorsqu’une equipe malveillante retire la liquidite d’un protocole. Enfin, l”incertitude reglementaire plane sur l’ensemble de l’ecosysteme, avec des cadres juridiques qui varient selon les juridictions et evoluent rapidement.

Remarque 90 (TVL : la metrique cle de la DeFi)

La Total Value Locked (TVL) mesure la valeur totale des actifs deposes dans les programmes DeFi d’une blockchain. C’est la metrique la plus utilisee pour evaluer la taille et la sante d’un ecosysteme DeFi. La TVL de Solana a atteint plusieurs milliards de dollars, la placant parmi les ecosystemes les plus actifs apres Ethereum. La TVL fluctue avec les prix des actifs sous-jacents et les cycles de marche : elle ne reflete pas directement l’activite, mais donne une indication de la confiance des utilisateurs dans les protocoles.

Remarque 91 (Composabilite et « money legos »)

La composabilite est la propriete la plus puissante de la DeFi. Chaque programme on-chain expose une interface publique que n’importe quel autre programme peut appeler via CPI (cf. chapitre 15). En pratique, cela signifie qu’un developpeur peut construire un nouveau protocole en assemblant des briques existantes : un flash loan de Solend, un swap sur Jupiter, un depot sur Kamino — le tout dans une seule transaction atomique. Cette composabilite sans permission accelere l’innovation mais amplifie aussi les risques : une vulnerabilite dans un protocole peut se propager a tous les protocoles qui en dependent.

Programme AMM simplifie en Anchor#

Nous construisons ici un AMM a produit constant complet en Anchor. Le chapitre 13 a explique la theorie (\(x \cdot y = k\)) ; nous passons maintenant a l’implementation. Le programme definit trois instructions : creation d’une pool, ajout de liquidite et swap.

Structure de la pool#

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

declare_id!("AMM1111111111111111111111111111111111111111");

#[account]
pub struct Pool {
    pub token_x_mint: Pubkey,       // Mint du token X
    pub token_y_mint: Pubkey,       // Mint du token Y
    pub token_x_reserve: u64,       // Reserve de token X dans la pool
    pub token_y_reserve: u64,       // Reserve de token Y dans la pool
    pub lp_mint: Pubkey,            // Mint du token LP
    pub fee_rate: u16,              // Frais en points de base (ex. 30 = 0.30%)
    pub bump: u8,                   // Bump de la PDA de la pool
}

Le compte Pool stocke l’etat complet de la paire : les adresses des mints, les reserves courantes, le mint du token LP et le taux de frais. Le champ bump conserve le bump de la PDA pour les signatures par le programme.

Instruction create_pool#

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

    pub fn create_pool(ctx: Context<CreatePool>, fee_rate: u16) -> Result<()> {
        require!(fee_rate <= 1000, AmmError::FeeTooHigh); // max 10%

        let pool = &mut ctx.accounts.pool;
        pool.token_x_mint = ctx.accounts.token_x_mint.key();
        pool.token_y_mint = ctx.accounts.token_y_mint.key();
        pool.token_x_reserve = 0;
        pool.token_y_reserve = 0;
        pool.lp_mint = ctx.accounts.lp_mint.key();
        pool.fee_rate = fee_rate;
        pool.bump = ctx.bumps.pool;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct CreatePool<'info> {
    #[account(
        init,
        payer = authority,
        space = 8 + 32 + 32 + 8 + 8 + 32 + 2 + 1,
        seeds = [b"pool", token_x_mint.key().as_ref(), token_y_mint.key().as_ref()],
        bump,
    )]
    pub pool: Account<'info, Pool>,
    pub token_x_mint: Account<'info, Mint>,
    pub token_y_mint: Account<'info, Mint>,
    #[account(
        init,
        payer = authority,
        mint::decimals = 6,
        mint::authority = pool,
    )]
    pub lp_mint: Account<'info, Mint>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub token_program: Program<'info, Token>,
    pub system_program: Program<'info, System>,
    pub rent: Sysvar<'info, Rent>,
}

L’instruction create_pool initialise la PDA de la pool en utilisant les mints des deux tokens comme seeds, garantissant l’unicite de chaque paire. Le mint LP est cree avec la pool comme autorite : seul le programme pourra emettre ou bruler des tokens LP.

Instruction add_liquidity#

pub fn add_liquidity(
    ctx: Context<AddLiquidity>,
    amount_x: u64,
    amount_y: u64,
) -> Result<()> {
    let pool = &mut ctx.accounts.pool;

    let lp_to_mint = if pool.token_x_reserve == 0 {
        // Premier depot : LP = sqrt(amount_x * amount_y)
        (amount_x as u128)
            .checked_mul(amount_y as u128)
            .unwrap()
            .integer_sqrt() as u64
    } else {
        // Depots suivants : proportionnel aux reserves existantes
        let lp_supply = ctx.accounts.lp_mint.supply;
        let lp_x = (amount_x as u128)
            .checked_mul(lp_supply as u128).unwrap()
            .checked_div(pool.token_x_reserve as u128).unwrap() as u64;
        let lp_y = (amount_y as u128)
            .checked_mul(lp_supply as u128).unwrap()
            .checked_div(pool.token_y_reserve as u128).unwrap() as u64;
        lp_x.min(lp_y) // On prend le minimum pour eviter la dilution
    };

    // Transfert des tokens X et Y vers la pool
    token::transfer(ctx.accounts.transfer_x_ctx(), amount_x)?;
    token::transfer(ctx.accounts.transfer_y_ctx(), amount_y)?;

    // Mint des tokens LP vers le deposant
    let seeds = &[
        b"pool",
        pool.token_x_mint.as_ref(),
        pool.token_y_mint.as_ref(),
        &[pool.bump],
    ];
    let signer = &[&seeds[..]];
    token::mint_to(
        ctx.accounts.mint_lp_ctx().with_signer(signer),
        lp_to_mint,
    )?;

    pool.token_x_reserve += amount_x;
    pool.token_y_reserve += amount_y;
    Ok(())
}

Lors du premier depot, la quantite de LP emise est la racine carree du produit des deux montants, conformement a la convention d’Uniswap V2. Pour les depots suivants, les LP sont calcules proportionnellement a la part la plus faible, ce qui incite les deposants a fournir des montants equilibres.

Remarque 92 (Frais de swap)

Les frais de swap sont typiquement de 0.3 % (30 points de base), deduits de l’input avant le calcul du swap. Cette convention, popularisee par Uniswap, est adoptee par la plupart des AMM. Les frais s’accumulent dans les reserves de la pool, augmentant mecaniquement la valeur des tokens LP detenus par les fournisseurs de liquidite. Sur Solana, certains protocoles comme Raydium ou Orca appliquent des taux differents selon les paires.

Instruction swap#

pub fn swap(
    ctx: Context<Swap>,
    amount_in: u64,
    minimum_out: u64,
) -> Result<()> {
    let pool = &mut ctx.accounts.pool;

    // Deduction des frais
    let fee = (amount_in as u128)
        .checked_mul(pool.fee_rate as u128).unwrap()
        .checked_div(10_000).unwrap() as u64;
    let effective_in = amount_in - fee;

    // Calcul du produit constant : x * y = k
    let k = (pool.token_x_reserve as u128)
        .checked_mul(pool.token_y_reserve as u128).unwrap();

    let new_x_reserve = pool.token_x_reserve + effective_in;
    let new_y_reserve = k
        .checked_div(new_x_reserve as u128).unwrap() as u64;

    let amount_out = pool.token_y_reserve - new_y_reserve;
    require!(amount_out >= minimum_out, AmmError::SlippageExceeded);

    // Transfert du token X vers la pool
    token::transfer(ctx.accounts.transfer_in_ctx(), amount_in)?;

    // Transfert du token Y vers l'utilisateur (signe par la PDA)
    let seeds = &[
        b"pool",
        pool.token_x_mint.as_ref(),
        pool.token_y_mint.as_ref(),
        &[pool.bump],
    ];
    let signer = &[&seeds[..]];
    token::transfer(
        ctx.accounts.transfer_out_ctx().with_signer(signer),
        amount_out,
    )?;

    pool.token_x_reserve = new_x_reserve + fee; // Les frais restent dans la pool
    pool.token_y_reserve = new_y_reserve;
    Ok(())
}

#[error_code]
pub enum AmmError {
    #[msg("Le taux de frais depasse le maximum autorise (10%)")]
    FeeTooHigh,
    #[msg("Le montant recu est inferieur au minimum demande (slippage)")]
    SlippageExceeded,
}

Le swap applique la logique du produit constant etudiee au chapitre 13. Les frais sont preleves sur l’input, puis le nouvel etat des reserves est calcule en maintenant l’invariant \(k\). Le parametre minimum_out protege l’utilisateur contre le slippage excessif : si la quantite recue est inferieure a ce seuil, la transaction echoue.

Exemple 40 (Trace d’un swap)

Considerons une pool avec \(1\,000\) tokens X et \(1\,000\) tokens Y, soit \(k = 1\,000\,000\). Un utilisateur souhaite echanger \(100\) tokens X.

  1. Calcul des frais : \(\text{fee} = 100 \times 0.003 = 0.3\), donc \(\text{effective\_in} = 99.7\).

  2. Nouvelle reserve X : \(1\,000 + 99.7 = 1\,099.7\).

  3. Nouvelle reserve Y : \(1\,000\,000 / 1\,099.7 \approx 909.34\).

  4. Montant sorti : \(1\,000 - 909.34 = 90.66\) tokens Y.

L’utilisateur recoit environ \(90.66\) Y pour \(100\) X, soit un prix effectif de \(1.103\) X par Y. Le prix s’est deplace de \(1.0\) a \(1.21\) (\(1\,099.7 / 909.34\)), illustrant l’impact de prix (price impact) d’un swap important sur une pool de taille modeste.

Lending et borrowing#

Les protocoles de pret constituent le deuxieme pilier de la DeFi. Ils permettent aux detenteurs d’actifs de les faire travailler en les pretant, et aux emprunteurs d’acceder a des liquidites sans vendre leurs positions.

Définition 124 (Protocole de pret (lending protocol))

Un protocole de pret est un programme on-chain qui permet aux utilisateurs de deposer des actifs comme collateral et d’emprunter d’autres actifs contre ce collateral. Les taux d’interet sont determines algorithmiquement en fonction du taux d’utilisation (utilization rate) de chaque marche. Les depots sont mutualises dans des pools de liquidite, et les interets sont distribues proportionnellement aux preteurs.

Trois mecanismes cles gouvernent le fonctionnement d’un protocole de pret :

  • Facteur de collateral / LTV (Loan-to-Value) : le ratio maximum entre la valeur empruntee et la valeur du collateral. Un LTV de 50 % signifie qu’un utilisateur peut emprunter jusqu’a la moitie de la valeur de son depot.

  • Liquidation : si la valeur du collateral chute et que le ratio LTV depasse le seuil de liquidation, n’importe quel utilisateur (liquidateur) peut rembourser une partie de la dette et saisir le collateral correspondant avec une decote (liquidation bonus), typiquement de 5 a 10 %.

  • Taux d’interet algorithmique : le taux est une fonction du taux d’utilisation \(U = \text{emprunts} / \text{depots}\). A faible utilisation, le taux est bas pour attirer les emprunteurs ; a forte utilisation, il augmente de facon exponentielle pour inciter les preteurs a deposer davantage.

Courbe de taux d’interet#

La plupart des protocoles utilisent un modele a deux segments : un taux lineaire sous un seuil d’utilisation optimal, puis un taux exponentiel au-dessus. Cette structure est connue sous le nom de kink model.

Hide code cell source

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

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

utilization = np.linspace(0, 1, 500)
u_optimal = 0.80
base_rate = 0.02
slope1 = 0.04
slope2 = 0.75

interest_rate = np.where(
    utilization <= u_optimal,
    base_rate + slope1 * (utilization / u_optimal),
    base_rate + slope1 + slope2 * ((utilization - u_optimal) / (1 - u_optimal))
)

fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(utilization * 100, interest_rate * 100, linewidth=2.5, color="#4C72B0")
ax.axvline(x=u_optimal * 100, color="#C44E52", linestyle="--", linewidth=1.5, label=f"Utilisation optimale ({int(u_optimal*100)} %)")
ax.fill_between(utilization * 100, 0, interest_rate * 100, alpha=0.15, color="#4C72B0")

ax.set_xlabel("Taux d'utilisation (%)")
ax.set_ylabel("Taux d'interet annuel (%)")
ax.set_title("Courbe de taux d'interet — modele a seuil (kink model)")
ax.set_xlim(0, 100)
ax.set_ylim(0, max(interest_rate * 100) * 1.1)
ax.legend(fontsize=10)

ax.annotate("Zone lineaire\n(emprunt peu couteux)", xy=(40, 3.5), fontsize=10,
            ha="center", color="#555555")
ax.annotate("Zone exponentielle\n(incitation a deposer)", xy=(92, 40), fontsize=10,
            ha="center", color="#C44E52")
plt.show()
_images/2347e163a217bf154a7ce1a548d10ada01106fad06a09d79c9bc2ea162a4f443.png

Exemple 41 (Liquidation d’une position)

Alice depose 10 SOL (cours : 20 $ chacun) comme collateral, soit une valeur de 200 $. Le protocole autorise un LTV maximal de 50 %, elle emprunte donc 100 USDC. Le seuil de liquidation est fixe a 65 %.

Le cours de SOL chute a 15 $. La valeur du collateral tombe a 150 $ et le ratio LTV passe a :

\[ \text{LTV} = \frac{100}{150} = 66.7\,\% > 65\,\% \quad \Rightarrow \text{liquidation declenchee} \]

Bob, un liquidateur, rembourse 50 USDC de la dette d’Alice. En echange, il recoit du SOL pour une valeur de 55 $ (50 $ + 10 % de bonus de liquidation), soit \(55 / 15 \approx 3.67\) SOL. Apres la liquidation, la dette d’Alice est reduite a 50 USDC et son collateral a \(10 - 3.67 = 6.33\) SOL (valeur : 95 $), ramenant le LTV a \(50 / 95 = 52.6\,\%\), sous le seuil.

Architecture d’un protocole de pret#

La visualisation suivante represente les interactions entre les acteurs et composants d’un protocole de pret.

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()

nodes = {
    "Preteur":     (0, 2),
    "Pool":        (2, 2),
    "Emprunteur":  (4, 2),
    "Oracle":      (2, 3.5),
    "Liquidateur": (2, 0.5),
    "Collateral":  (4, 3.5),
}

for node, pos in nodes.items():
    G.add_node(node, pos=pos)

edges = [
    ("Preteur", "Pool", "Depot"),
    ("Pool", "Preteur", "Interets"),
    ("Pool", "Emprunteur", "Pret"),
    ("Emprunteur", "Pool", "Remboursement"),
    ("Emprunteur", "Collateral", "Depot collateral"),
    ("Oracle", "Pool", "Prix"),
    ("Liquidateur", "Pool", "Remboursement\npartiel"),
    ("Pool", "Liquidateur", "Collateral\n(+ bonus)"),
]

for src, dst, label in edges:
    G.add_edge(src, dst, label=label)

pos = nx.get_node_attributes(G, "pos")

role_colors = {
    "Preteur": "#4C72B0",
    "Pool": "#DD8452",
    "Emprunteur": "#55A868",
    "Oracle": "#C44E52",
    "Liquidateur": "#8172B3",
    "Collateral": "#937860",
}
node_colors = [role_colors[n] for n in G.nodes()]

fig, ax = plt.subplots(figsize=(11, 7))

nx.draw_networkx_nodes(G, pos, ax=ax, node_size=2200, node_color=node_colors,
                       edgecolors="white", linewidths=2)
nx.draw_networkx_labels(G, pos, ax=ax, font_size=10, font_weight="bold", font_color="white")
nx.draw_networkx_edges(G, pos, ax=ax, edge_color="#888888", arrows=True,
                       arrowstyle="-|>", arrowsize=15,
                       connectionstyle="arc3,rad=0.15",
                       min_source_margin=22, min_target_margin=22)

edge_labels = nx.get_edge_attributes(G, "label")
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, ax=ax,
                              font_size=8, font_color="#333333",
                              bbox=dict(boxstyle="round,pad=0.2", fc="white", ec="none", alpha=0.8))

ax.set_title("Architecture d'un protocole de pret decentralise", fontsize=13, fontweight="bold")
ax.set_xlim(-1, 5.5)
ax.set_ylim(-0.3, 4.3)
ax.axis("off")
plt.show()
_images/e5ec4717e1c52ca09ca51265e67aaa15879a30c0c4d46349039ff41a683179a7.png

Remarque 93 (Protocoles de pret sur Solana)

L’ecosysteme Solana dispose de plusieurs protocoles de pret matures. Kamino Finance (anciennement Kamino Lend) est devenu le leader en TVL grace a son integration avec les vaults de liquidite. MarginFi propose un modele de risque segmente avec des groupes de banques (banks) isoles. Solend a ete l’un des pionniers du lending sur Solana. Chacun implemente des variantes du modele a seuil pour les taux d’interet, avec des parametres de risque (LTV, bonus de liquidation, facteurs de reserve) ajustes par gouvernance.

Yield farming et staking#

Le yield farming et le staking representent les mecanismes par lesquels les participants de la DeFi sont recompenses pour leur contribution au fonctionnement des protocoles.

Définition 125 (Yield farming)

Le yield farming (ou liquidity mining) consiste a fournir du capital a des protocoles DeFi — liquidite dans un AMM, depots dans un protocole de pret, etc. — en echange de recompenses. Ces recompenses prennent la forme de frais de trading, de tokens de gouvernance du protocole, ou d’autres incitations. L’objectif est de maximiser le rendement sur le capital deploye, souvent en combinant plusieurs couches de recompenses.

Définition 126 (Staking)

Le staking consiste a verrouiller des tokens pour contribuer au fonctionnement d’un reseau ou d’un protocole et recevoir des recompenses en retour. On distingue le staking natif (deleguer du SOL a un validateur pour securiser le reseau) du staking de protocole (verrouiller des tokens de gouvernance pour obtenir des droits de vote ou des revenus du protocole).

APY vs APR#

La distinction entre APR et APY est essentielle pour comparer les rendements :

  • APR (Annual Percentage Rate) : le taux annuel simple, sans recomposition. Un APR de 12 % rapporte 1 % par mois sur le capital initial.

  • APY (Annual Percentage Yield) : le taux annuel compose. Si les interets sont reinvestis mensuellement, un APR de 12 % donne un APY de \((1 + 0.12/12)^{12} - 1 \approx 12.68\,\%\).

La formule generale est :

\[ \text{APY} = \left(1 + \frac{\text{APR}}{n}\right)^{n} - 1 \]

ou \(n\) est le nombre de periodes de recomposition par an. A la limite, la recomposition continue donne \(\text{APY} = e^{\text{APR}} - 1\).

Hide code cell source

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

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

apr_values = np.linspace(0, 1.0, 200)
n_values = [1, 4, 12, 365]
labels = ["Annuel (n=1)", "Trimestriel (n=4)", "Mensuel (n=12)", "Journalier (n=365)"]
colors = ["#4C72B0", "#DD8452", "#55A868", "#C44E52"]

fig, ax = plt.subplots(figsize=(10, 5))

for n, label, c in zip(n_values, labels, colors):
    apy = (1 + apr_values / n) ** n - 1
    ax.plot(apr_values * 100, apy * 100, linewidth=2, label=label, color=c)

# Recomposition continue
apy_cont = np.exp(apr_values) - 1
ax.plot(apr_values * 100, apy_cont * 100, linewidth=2, linestyle="--",
        label="Continue", color="#8172B3")

ax.plot(apr_values * 100, apr_values * 100, linewidth=1, linestyle=":",
        label="APR = APY (reference)", color="#999999")

ax.set_xlabel("APR (%)")
ax.set_ylabel("APY (%)")
ax.set_title("Effet de la frequence de recomposition sur le rendement")
ax.legend(fontsize=9, loc="upper left")
ax.set_xlim(0, 100)
ax.set_ylim(0, 180)
plt.show()
_images/50ec11f6e405e873d47a266652867924d4172ace9295a879718f033d93c80227.png

Remarque 94 (Staking natif sur Solana)

Le staking natif de SOL consiste a deleguer ses tokens a un validateur du reseau. Le rendement se situe typiquement entre 6 et 8 % APY, finance par l’inflation du reseau et les frais de transaction. Le liquid staking (staking liquide) permet de contourner la contrainte d’immobilisation : des protocoles comme Marinade (mSOL) ou Jito (jitoSOL) emettent un token de recu (LST, Liquid Staking Token) echangeable librement et utilisable dans la DeFi. Ainsi, un utilisateur peut staker son SOL et l’utiliser comme collateral dans un protocole de pret, cumulant les rendements.

Exemple 42 (Scenario de yield farming)

Un utilisateur dispose de 1 000 USDC et souhaite maximiser son rendement sur Solana.

  1. Il echange 500 USDC contre du SOL sur Jupiter.

  2. Il depose la paire SOL/USDC dans un pool Orca, recevant des tokens LP. Les frais de trading generent environ 15 % APR.

  3. Il stake ses tokens LP dans le programme de liquidity mining d’Orca, recevant des tokens ORCA en recompense (10 % APR supplementaire).

  4. Il stake les tokens ORCA recus dans le vault de gouvernance d’Orca pour un rendement additionnel.

Le rendement total combine les frais de trading, les recompenses de mining et les revenus de gouvernance. Cependant, l’utilisateur est expose a l”impermanent loss si le prix de SOL diverge significativement, et au risque de smart contract sur chaque protocole utilise.

Remarque 95 (Impermanent loss)

L”impermanent loss (perte non permanente) est le cout d’opportunite subi par un fournisseur de liquidite par rapport a une strategie de simple detention (hold). Elle survient lorsque le prix relatif des deux actifs de la pool change. Pour un mouvement de prix \(r\) (ratio prix final / prix initial), la perte est donnee par :

\[ \text{IL} = \frac{2\sqrt{r}}{1 + r} - 1 \]

Par exemple, si le prix de SOL double (\(r = 2\)), l’IL est de \(\frac{2\sqrt{2}}{3} - 1 \approx -5.7\,\%\). La perte est dite « non permanente » car elle disparait si les prix reviennent a leur valeur initiale. En pratique, si l’utilisateur retire sa liquidite apres un mouvement de prix, la perte est realisee.

Flash loans#

Les flash loans representent une innovation propre a la DeFi, sans equivalent dans la finance traditionnelle. Ils exploitent l’atomicite des transactions blockchain pour permettre des emprunts sans collateral.

Définition 127 (Flash loan)

Un flash loan est un pret non collateralise qui doit etre emprunte et rembourse au sein d’une seule transaction atomique. Si le remboursement (principal + frais) n’est pas effectue avant la fin de la transaction, l’integralite de la transaction est annulee (reverted), comme si l’emprunt n’avait jamais eu lieu. Cette propriete est rendue possible par l’atomicite des transactions blockchain : soit toutes les instructions reussissent, soit aucune ne s’execute.

Le fonctionnement d’un flash loan suit toujours le meme schema en trois etapes :

  1. Emprunt : le programme de flash loan transfere les fonds a l’emprunteur.

  2. Utilisation : l’emprunteur execute une ou plusieurs operations avec les fonds (arbitrage, swap de collateral, auto-liquidation…).

  3. Remboursement : l’emprunteur rembourse le montant emprunte plus les frais. Si cette etape echoue, toute la transaction est annulee.

Exemple 43 (Arbitrage par flash loan)

Supposons que le SOL se negocie a 20 $ sur l’AMM A et a 21 $ sur l’AMM B.

  1. L’arbitrageur emprunte 10 000 USDC via un flash loan (frais : 0.09 %, soit 9 USDC).

  2. Il achete 500 SOL sur l’AMM A (10 000 / 20).

  3. Il revend les 500 SOL sur l’AMM B pour 10 500 USDC (500 * 21).

  4. Il rembourse 10 009 USDC (principal + frais).

  5. Profit : 10 500 - 10 009 = 491 USDC, sans capital initial.

Toute cette sequence s’execute dans une seule transaction. Si a l’etape 3 le prix sur l’AMM B a deja converge (par exemple a cause d’un autre arbitrageur), le remboursement echoue et la transaction est entierement annulee : l’arbitrageur ne perd que les frais de transaction.

Usages legitimes et malveillants#

Les flash loans servent a des usages productifs :

  • Arbitrage : equilibrage des prix entre differentes places de marche, contribuant a l’efficience du marche.

  • Swap de collateral : un emprunteur peut changer le type de collateral d’une position sans la fermer (emprunter pour rembourser, retirer le collateral, deposer un nouveau collateral, re-emprunter).

  • Auto-liquidation : un utilisateur proche du seuil de liquidation peut se liquider lui-meme a moindre cout, evitant le bonus de liquidation paye a un tiers.

Mais ils peuvent aussi servir des attaques :

  • Manipulation d’oracle : emprunter massivement un token pour fausser son prix sur un AMM utilise comme oracle par un autre protocole, puis exploiter ce prix manipule.

  • Attaques de gouvernance : emprunter une grande quantite de tokens de gouvernance pour voter sur une proposition malveillante au sein d’une seule transaction.

Remarque 96 (Flash loans sur Solana)

Sur Solana, les flash loans sont rendus possibles par les CPI (Cross-Program Invocations) au sein d’une seule transaction. Le programme de flash loan transfere les fonds, l’emprunteur execute ses operations via CPI vers d’autres programmes, puis le programme verifie le remboursement. Jupiter, l’aggregateur d’echanges de Solana, utilise un mecanisme similaire pour router les swaps a travers plusieurs AMM dans une seule transaction. Les programmes comme Solend et MarginFi offrent des flash loans natifs.

Remarque 97 (Protection contre les attaques par flash loan)

Plusieurs strategies protegent les protocoles contre les attaques par flash loan :

  • Oracles TWAP (Time-Weighted Average Price) : utiliser un prix moyen pondere dans le temps plutot qu’un prix instantane rend la manipulation couteuse car l’attaquant devrait maintenir le prix manipule sur plusieurs blocs.

  • Pricing multi-blocs : exiger que les prix soient confirmes sur plusieurs slots avant d’etre acceptes.

  • Detection de flash loans : verifier si l’emprunteur a recu des fonds dans la meme transaction (en inspectant les instructions precedentes) et refuser l’operation le cas echeant.

  • Oracles externes : utiliser des oracles comme Pyth ou Switchboard, dont les prix sont alimentes par des sources off-chain independantes du DEX, rendant la manipulation on-chain inefficace.

Resume#

Ce chapitre a presente les quatre piliers de la DeFi sur Solana et leur implementation concrete.

Concept

Description

DeFi

Services financiers on-chain, sans intermediaire, permissionless et composables

AMM (implementation)

Programme Anchor avec produit constant, gestion des reserves, frais et slippage protection

Produit constant

Invariant \(x \cdot y = k\) maintenu a chaque swap, avec frais deduits de l’input

Lending / Borrowing

Depots mutualises, emprunts collateralises, taux algorithmiques (kink model)

Liquidation

Mecanisme de saisie du collateral lorsque le LTV depasse le seuil, avec bonus

Yield farming

Fourniture de capital en echange de recompenses (frais, tokens de gouvernance)

Staking

Verrouillage de tokens pour securiser le reseau ou participer a la gouvernance

Liquid staking

Staking avec token de recu (mSOL, jitoSOL) utilisable dans la DeFi

APY vs APR

APR = taux simple ; APY = taux compose tenant compte de la recomposition

Flash loan

Pret non collateralise atomique, emprunte et rembourse dans une seule transaction

Impermanent loss

Cout d’opportunite pour les LP lors de divergence des prix

TVL

Total Value Locked, metrique de reference pour mesurer la taille d’un ecosysteme DeFi

Le chapitre suivant explorera les Cross-Program Invocations (CPI), le mecanisme qui rend possible la composabilite entre programmes et qui est au coeur de chaque interaction DeFi etudiee ici.