Flux de tokens, NFT et contrats intelligents · Leçon 5 sur 6
Prêts, ponts et comptabilité des protocoles
10 min de lecture
Les protocoles de prêt et les ponts détiennent les plus grands pools de valeur de la finance décentralisée, et leurs défaillances sont parmi les plus importantes. Pour un analyste, ils soulèvent deux types de questions : qu’a fait une adresse donnée (déposer, emprunter, se faire liquider, emprunter pour une seule transaction, déplacer des fonds vers une autre chaîne) et la comptabilité du protocole est-elle toujours équilibrée ? On peut répondre à ces questions à partir des événements et des lectures d’état, à condition de savoir quelle adresse agit au nom de qui.
Le modèle d’événements des prêts
La plupart des marchés de prêt suivent le même modèle : les utilisateurs fournissent un actif et reçoivent en échange une créance productive d’intérêts, empruntent en donnant cette garantie, remboursent, retirent leurs fonds et se font liquider si leur garantie passe sous un seuil. Aave v3 est une bonne référence, car son contrat de pool émet un événement par action :
| Événement | Champs clés | Note pour l’analyste |
|---|---|---|
Supply | reserve, user, onBehalfOf, amount | user a agi ; onBehalfOf reçoit la position |
Withdraw | reserve, user, to, amount | to peut différer du propriétaire de la position |
Borrow | reserve, user, onBehalfOf, amount, interestRateMode, borrowRate | L’emprunt peut être délégué à une autre adresse |
Repay | reserve, user, repayer, amount, useATokens | N’importe qui peut rembourser la dette d’une autre personne |
LiquidationCall | collateralAsset, debtAsset, user, debtToCover, liquidatedCollateralAmount, liquidator, receiveAToken | user est l’emprunteur liquidé |
La distinction entre la personne qui agit et la personne dont la position change est le principal piège. Un dépôt effectué par un routeur ou un compte intelligent au nom d’un utilisateur indique le routeur comme user et le véritable propriétaire dans onBehalfOf. Les remboursements effectués par des tiers sont un lien utile : une adresse qui rembourse la dette d’une autre adresse a généralement un lien avec elle.
Les positions de fourniture sont représentées par des jetons (aTokens dans Aave, cTokens dans les marchés de type Compound). Les soldes d’aTokens d’Aave augmentent continuellement à mesure que les intérêts s’accumulent, sans événement Transfer pour chaque hausse. Ils se comportent donc comme les jetons à rebasage décrits dans la leçon Lecture des transferts de jetons : consultez balanceOf à un bloc donné plutôt que de sommer les transferts. Les jetons de dette sont généralement non transférables, et la dette de l’emprunteur augmente également entre les événements.
Une liquidation décodée :
{
"event": "LiquidationCall",
"collateralAsset": "0x4d2e…77c1",
"debtAsset": "0x7d1f…c3a0",
"user": "0xd3b7…05fa",
"debtToCover": "10000000000",
"liquidatedCollateralAmount": "3500000000000000000",
"liquidator": "0x6c9a…e810",
"receiveAToken": false
}
Avec un stablecoin de dette à 6 décimales et du WETH en garantie à 3 000, le liquidateur a remboursé 10 000 de la dette de l’emprunteur et reçu 3,5 WETH d’une valeur de 10 500 : un bonus de liquidation de 5 %, correspondant à la perte de l’emprunteur et au bénéfice brut du liquidateur avant les frais de gaz et d’échange. Les liquidateurs sont généralement des bots et utilisent souvent des prêts éclair pour financer le remboursement. Pour étudier les tensions du marché, regroupez les événements de liquidation par bloc et par actif de garantie : les regroupements coïncident avec les fortes variations du prix de l’oracle utilisé par le protocole, et une série de liquidations du même emprunteur sur plusieurs blocs montre une position dénouée par étapes, chacune ne rétablissant sa santé que temporairement.
Les prêts éclair dans les traces
Un prêt éclair prête n’importe quel montant sans garantie, à une seule condition : il doit être remboursé, majoré d’une petite prime, avant la fin de la transaction, sinon la transaction entière est annulée. Les prêts éclair sont des outils légitimes pour les liquidations, les échanges de garanties et l’arbitrage ; ils sont aussi le moyen habituel pour les attaquants d’obtenir temporairement des capitaux. Dans les journaux, ils sont faciles à manquer ; dans la trace, ils suivent une structure fixe :
CALL Bot.run()
CALL LendingPool.flashLoanSimple(receiver=Bot, asset=USDC, amount=1,000,000)
CALL USDC.transfer(Bot, 1,000,000) -- les fonds quittent la réserve
CALL Bot.executeOperation(USDC, 1,000,000, premium, initiator, params)
CALL DEX / lending / governance contracts ... -- la stratégie proprement dite
CALL USDC.approve(LendingPool, 1,000,000 + premium)
CALL USDC.transferFrom(Bot, reserve, 1,000,000 + premium)
LOG FlashLoan(target=Bot, asset=USDC, amount=1,000,000, premium)
Le pool envoie les fonds, rappelle le contrat du bénéficiaire via executeOperation, le laisse faire ce qu’il veut, puis récupère le principal et la prime. Tout ce qui se passe entre le rappel et le remboursement constitue la stratégie à analyser. Les swaps éclair de type Uniswap et les autres coffres suivent le même modèle d’envoi, de rappel et de vérification, avec leurs propres noms de fonction.
Dans vos rapports, déduisez le prêt éclair : le principal entre et sort au cours de la même transaction et ne constitue pas l’argent de l’acteur. L’attaque de gouvernance de Beanstalk du 17 avril 2022 montre pourquoi cette distinction est importante : l’attaquant a emprunté environ 1 milliard de dollars par le biais de prêts éclair auprès de plusieurs protocoles, utilisé le pouvoir de vote ainsi obtenu pour faire adopter et exécuter une proposition malveillante dans la même transaction au moyen d’une fonction d’urgence, puis est reparti avec environ 24 830 ETH (environ 76 millions de dollars), à la suite d’une perte d’environ 181 millions de dollars. Le capital était emprunté ; le profit était bien réel.
Flux de pont de verrouillage et de frappe
La leçon Les couches 2, les chaînes latérales et les ponts comme sources de données présente les événements de dépôt et de retrait des ponts, et la leçon Traçage interchaînes explique comment suivre les fonds d’une chaîne à l’autre. Ici, l’accent est mis sur la comptabilité. Dans un pont à verrouillage et frappe :
- L’utilisateur dépose des jetons dans un contrat d’escrow du pont sur la chaîne source, qui émet un événement de dépôt indiquant la chaîne de destination, le destinataire et un nonce.
- Une couche de vérification (un ensemble de validateurs, un client léger, une période de contestation optimiste ou une preuve à divulgation nulle de connaissance) atteste que le dépôt a eu lieu.
- Un émetteur sur la chaîne de destination crée la même quantité d’un jeton enveloppé, sous la forme d’un transfert depuis l’adresse zéro vers le destinataire.
Les retraits inversent le processus : destruction sur la chaîne de destination, libération depuis l’escrow sur la chaîne source. Les noms des événements varient selon les ponts ; voici une paire illustrative, associée par nonce :
{
"source": {"chain_id": 1, "event": "Deposit", "escrow": "0x2b9e…a0c4",
"token": "0x7d1f…c3a0", "recipient": "0xa1b2…9f0e",
"amount": "250000000000", "dest_chain_id": 42161, "nonce": 88231},
"destination": {"chain_id": 42161, "event": "Transfer", "token": "0x51c8…9d02",
"from": "0x0000000000000000000000000000000000000000",
"to": "0xa1b2…9f0e", "value": "250000000000", "nonce": 88231}
}
Les deux étapes indiquent 250 000 000 000 unités de base, soit 250 000 jetons à 6 décimales. Vérifiez le nombre de décimales des deux côtés : les versions enveloppées ne conservent pas toujours le nombre de décimales de l’original. D’autres modèles de pont évitent complètement les jetons enveloppés : les réseaux de liquidité effectuent les paiements à partir de pools sur la chaîne de destination, et certains émetteurs détruisent et frappent leur propre jeton natif sur chaque chaîne. Chaque modèle laisse une piste de rapprochement différente.
Rapprochement des soldes de protocole
L’invariant fondamental d’un pont à verrouillage et frappe est simple : l’offre de jetons enveloppés sur la chaîne de destination ne doit jamais dépasser la garantie détenue dans l’escrow sur la chaîne source, à l’exception des dépôts en transit. La même rigueur s’applique aux prêts : les jetons représentant l’offre émise doivent correspondre aux actifs sous-jacents détenus par le protocole, auxquels s’ajoute l’encours de la dette.
-- Garantie cumulée en escrow et offre de jetons enveloppés, un jeton, par jour
SELECT day,
SUM(net_locked_units) OVER (ORDER BY day) AS cum_locked,
SUM(net_minted_units) OVER (ORDER BY day) AS cum_minted,
SUM(net_locked_units - net_minted_units) OVER (ORDER BY day) AS backing_gap
FROM daily_bridge_flows -- calculé à partir des transferts d’escrow et des frappes/destructions depuis l’adresse zéro
WHERE bridge = 'example-bridge' AND token_symbol = 'USDC'
ORDER BY day;
daily_bridge_flows contient, pour chaque jour, les transferts nets vers l’escrow sur la chaîne source et les frappes moins les destructions du jeton enveloppé sur la chaîne de destination, les deux étant déjà convertis en jetons entiers. Les sommes cumulées sur une fenêtre produisent des totaux courants. Un faible écart positif est normal tant que des dépôts sont en transit ; un écart négatif signifie qu’il existe des jetons non garantis. Mieux encore, comparez directement l’état : le balanceOf de l’escrow avec le totalSupply() du jeton enveloppé aux mêmes instants.
Les incidents publics montrent ce qui peut mal tourner :
| Cas | Ce qui s’est passé | Manifestation dans le rapprochement |
|---|---|---|
| Wormhole, 2 février 2022 | Une faille dans la vérification des signatures a permis à l’attaquant de frapper 120 000 wETH sur Solana sans dépôt ; 93 750 ont été transférés par pont et échangés contre de l’ETH sur Ethereum | L’offre de jetons enveloppés augmente sans entrée correspondante dans l’escrow, puis l’escrow se vide ; la société mère a ensuite remplacé l’ETH |
| Ronin, mars 2022 | Des attaquants ont pris le contrôle de cinq des neuf clés de validation et signé des retraits d’environ 173 600 ETH et 25,5 millions d’USDC | L’escrow diminue sans destructions correspondantes ; l’incident est passé inaperçu pendant six jours |
| Nomad, 1er août 2022 | L’initialisation d’une mise à niveau a marqué une valeur zéro comme racine de confiance, ce qui a permis à des messages non vérifiés de passer ; plus de 300 adresses ont dérobé environ 190 millions de dollars en copiant la première exploitation | Des centaines de libérations sans dépôts correspondants sur la chaîne source |
Le délai de détection de Ronin est l’argument le plus fort en faveur de vérifications continues : un système de surveillance comparant les soldes de l’escrow aux destructions aurait signalé les retraits en quelques blocs. Pour les protocoles de prêt, la vérification parallèle compare le total des jetons représentant l’offre aux actifs sous-jacents détenus par le protocole, auxquels s’ajoute la dette totale, et suit le taux d’utilisation (emprunts divisés par l’offre), qui grimpe lorsqu’un marché se vide ou qu’une ruée bancaire commence.
Points clés pour l'examen : les événements de fourniture, d’emprunt, de remboursement, de retrait et de liquidation, ainsi que la différence entre l’adresse qui agit etonBehalfOf; pourquoi les soldes de type aToken doivent être lus dans l’état ; le calcul d’un bonus de liquidation à partir d’unLiquidationCalldécodé ; la structure d’un prêt éclair dans une trace (envoi, rappelexecuteOperation, remboursement avec prime) et la déduction du principal dans les rapports ; l’attaque de gouvernance de Beanstalk par prêt éclair ; les flux de verrouillage et frappe et de destruction et libération, l’association des étapes par nonce et la vérification du nombre de décimales ; l’invariant de rapprochement (offre de jetons enveloppés ≤ garantie en escrow) et la façon dont les incidents de Wormhole, Ronin et Nomad s’y manifesteraient.