Flux de tokens, NFT et contrats intelligents · Leçon 2 sur 6
NFT et normes multi-jetons
10 min de lecture
Les jetons non fongibles sont les éléments dont les données sur la chaîne sont le plus souvent mal interprétées par le public. Un transfert d’un NFT à lui seul ne dit rien sur son prix, une « vente record » peut correspondre à un prêt remboursé au cours de la même transaction, et les chiffres des redevances dépendent de la politique de la place de marché plutôt que du jeton. Un analyste doit savoir distinguer les transferts simples des ventes, décoder les événements propres à la place de marché pour obtenir le prix et la répartition des frais, et reconnaître les signes du wash trading.
Événements ERC-721
Un jeton ERC-721 (EIP-721) attribue un seul propriétaire à chaque identifiant de jeton. La norme définit trois événements :
| Événement | Topics | Données |
|---|---|---|
Transfer(address indexed from, address indexed to, uint256 indexed tokenId) | topic0, from, to, tokenId | vide |
Approval(address indexed owner, address indexed approved, uint256 indexed tokenId) | topic0, owner, approved, tokenId | vide |
ApprovalForAll(address indexed owner, address indexed operator, bool approved) | topic0, owner, operator | indicateur approved |
Comme la signature canonique Transfer(address,address,uint256) est identique à celle d’ERC-20, le topic0 est le même (0xddf252ad…b3ef). Le mot-clé indexed ne fait pas partie de la signature et ne modifie donc pas le hash ; il change uniquement l’emplacement où la valeur est stockée. Un événement Transfer ERC-721 comporte quatre topics et des données vides ; un événement Transfer ERC-20 comporte trois topics et la quantité dans les données. Un décodeur qui se base uniquement sur topic0 lira les identifiants de jetons comme des quantités, ce qui a conduit certains tableaux de bord à signaler des « transferts » de milliards d’unités d’une collection d’art.
La création de jetons suit la convention ERC-20 : from est l’adresse zéro. ApprovalForAll est l’équivalent, pour les NFT, d’une approbation ERC-20 illimitée : elle permet à un opérateur, généralement un contrat de place de marché, de déplacer tous les jetons détenus par le propriétaire dans cette collection. Les campagnes d’hameçonnage qui obtiennent une signature ApprovalForAll peuvent vider la collection d’un portefeuille en une seule transaction ; cet événement est donc la première chose à vérifier lorsqu’une victime signale le vol de NFT.
Pour confirmer la norme qu’un contrat déclare implémenter, appelez supportsInterface (ERC-165) : l’identifiant d’interface d’ERC-721 est 0x80ac58cd, celui d’ERC-1155 est 0xd9b67a26 et celui de la norme sur les redevances ERC-2981 est 0x2a55205a. Certaines collections anciennes sont antérieures aux normes. CryptoPunks (2017) utilise son propre contrat, avec des événements personnalisés tels que PunkTransfer et PunkBought ; ses transactions sont invisibles pour un décodeur qui ne lit que les événements Transfer ERC-721.
Multi-jetons ERC-1155
ERC-1155 (EIP-1155) regroupe de nombreux types de jetons dans un même contrat. Chaque type a un id, et chaque détenteur possède une quantité de cet identifiant ; un même contrat peut donc contenir une monnaie de jeu fongible (id 1, des millions d’unités) et des objets uniques (quantité 1). Les événements de transfert sont :
TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value)TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values)
L’operator est le compte autorisé à effectuer le transfert et devrait être l’appelant ; lors d’une vente sur une place de marché, le contrat de la place de marché apparaît donc comme opérateur et le vendeur comme from. Comme pour les autres normes, la création de jetons définit from sur l’adresse zéro et la destruction définit to sur l’adresse zéro. Les valeurs de topic0 sont 0xc3d58168…0f62 pour TransferSingle et 0x4a39dc06…f7fb pour TransferBatch.
Les événements de lot contiennent deux tableaux dynamiques dans les data, encodés avec des décalages. Un lot transférant trois unités de l’id 7 et une unité de l’id 12 se décode ainsi :
mot 0 : 0x…40 décalage du tableau ids (64 octets depuis le début des données)
mot 1 : 0x…a0 décalage du tableau values (160 octets)
mot 2 : 0x…02 longueur de ids = 2
mot 3 : 0x…07 ids[0] = 7
mot 4 : 0x…0c ids[1] = 12
mot 5 : 0x…02 longueur de values = 2
mot 6 : 0x…03 values[0] = 3
mot 7 : 0x…01 values[1] = 1
Les deux premiers mots indiquent où commence chaque tableau ; chaque tableau commence ensuite par sa longueur. Les paires se lisent par position : (id 7, 3 unités) et (id 12, 1 unité). Dans un tableau plat, un TransferBatch devient autant de lignes qu’il y a d’identifiants ; si l’on oublie de développer les tableaux, on perd silencieusement la majeure partie du volume des contrats qui utilisent beaucoup les lots. ERC-1155 définit aussi ApprovalForAll et URI(string value, uint256 indexed id), ce dernier signalant les modifications des métadonnées.
Ventes et transferts simples
Un événement Transfer, à lui seul, ne contient aucun prix. Les propriétaires déplacent des NFT entre leurs propres portefeuilles, les prêtent, les distribuent par airdrop et les vendent contre des paiements effectués hors chaîne. Une vente se déduit d’autres éléments de preuve dans la même transaction : un flux de paiement (monnaie native dans les traces, ou transfert ERC-20 tel que WETH) et, idéalement, l’événement de règlement propre à la place de marché.
Seaport, le protocole open source utilisé par OpenSea et d’autres, émet OrderFulfilled avec une offre (ce que le créateur de l’ordre donne) et une contrepartie (ce que le créateur et les éventuels destinataires de frais doivent recevoir). Exemple simplifié décodé d’une annonce vendue 10 ETH :
{
"event": "OrderFulfilled",
"offerer": "0x5e11…a7b3",
"recipient": "0xb7e2…19cd",
"offer": [
{"itemType": 2, "token": "0xc011…77ab", "identifier": "4321", "amount": "1"}
],
"consideration": [
{"itemType": 0, "amount": "9450000000000000000", "recipient": "0x5e11…a7b3"},
{"itemType": 0, "amount": "50000000000000000", "recipient": "0xfee0…2c1d"},
{"itemType": 0, "amount": "500000000000000000", "recipient": "0xc4ea…b0b1"}
]
}
Le type d’élément 2 correspond à un jeton ERC-721 et le type 0 à la monnaie native. L’offreur (le vendeur) cède le jeton 4321 ; la contrepartie verse 9,45 ETH au vendeur, 0,05 ETH (0,5 %) à une adresse de frais et 0,5 ETH (5 %) au créateur. Le prix de vente est la somme, soit 10 ETH. Le sens des flux est important : lorsqu’un vendeur accepte une offre d’achat, l’offreur est l’acheteur, l’offre est en WETH et le NFT figure dans la contrepartie. Autres complications :
- Lots : un paiement pour plusieurs NFT ; tout prix par élément est une répartition et doit être indiqué comme tel.
- Agrégateurs : un routeur achète sur plusieurs places de marché en une seule transaction ; attribuez chaque élément à l’ordre qui l’a exécuté.
- Jetons de paiement : les prix en WETH, en stablecoins ou dans d’autres jetons doivent être convertis à l’aide du cours au bloc concerné.
| Ce que vous voyez dans la transaction | Signification probable |
|---|---|
| Un événement NFT Transfer uniquement | Déplacement de portefeuille, cadeau ou vente hors chaîne ; aucun prix |
Un événement NFT Transfer et un paiement à l’adresse from | Vente probable ; confirmez avec un événement de place de marché |
| Événement de règlement de la place de marché | Vente avec prix et répartition des frais |
| Création (depuis l’adresse zéro) avec paiement au contrat | Vente primaire au prix de création |
Redevances
ERC-2981 normalise une requête, royaltyInfo(tokenId, salePrice), qui renvoie un destinataire et un montant. Elle est informative : rien dans le jeton n’oblige une place de marché à payer. Certaines collections ont tenté d’imposer le paiement en bloquant les transferts depuis les places de marché qui ne versaient pas de redevances, mais cette pratique a perdu du terrain. OpenSea, par exemple, a désactivé son outil d’application sur la chaîne et rendu facultatifs les frais des créateurs pour les nouvelles collections à partir du 31 août 2023, puis pour les anciennes collections qui s’appuyaient sur l’application de cette règle après le 29 février 2024. Pour l’analyse, mesurez les redevances effectivement versées (les éléments de la contrepartie envoyés au destinataire des redevances) plutôt que le taux annoncé par le jeton, et prévoyez que le taux effectif varie selon la place de marché et au fil du temps.
Signaux de wash trading
Le wash trading consiste à effectuer des transactions avec soi-même ou avec des comptes complices afin de simuler du volume, de gonfler un prix ou de récolter des récompenses. L’exemple le plus célèbre est CryptoPunk 9998, le 28 octobre 2021 : une « vente » d’environ 124 457 ETH (environ 532 millions de dollars à l’époque) au cours de laquelle, comme l’ont expliqué les créateurs de la collection, l’acheteur s’est acheté le punk à lui-même avec de l’argent emprunté et a remboursé le prêt dans la même transaction. Les récompenses de volume créent des incitations permanentes : une étude évaluée par les pairs sur les marchés NFT d’Ethereum (La Morgia et al., IEEE ICDCS 2023) a constaté que le wash trading représentait environ 85 % du volume sur LooksRare, une place de marché qui versait des récompenses en jetons proportionnelles au volume des transactions.
Les signaux utilisés par les chercheurs et les analystes comprennent :
- Cycles fermés : le même jeton passe de A → B → A, ou circule dans un petit groupe de comptes.
- Source de financement commune : l’acheteur et le vendeur ont été financés par la même source peu avant la transaction.
- Destination commune : le produit des deux côtés aboutit à la même adresse.
- Position sans risque : les comptes complices terminent avec le même solde combiné, déduction faite des frais.
- Autofinancement : prêts éclair ou paiements dans la même transaction qui reviennent au payeur.
- Des prix bien supérieurs à la fourchette récente de la collection et des transactions répétées rapides sur le même jeton.
Un filtre simple pour repérer les allers-retours dans une table nft_sales décodée :
-- Jetons rachetés par leur vendeur précédent dans les 30 jours
SELECT a.collection, a.token_id, a.seller, a.buyer,
a.price_eth AS first_price, b.price_eth AS return_price
FROM nft_sales a
JOIN nft_sales b
ON b.collection = a.collection
AND b.token_id = a.token_id
AND b.seller = a.buyer
AND b.buyer = a.seller
AND b.block_time > a.block_time
AND b.block_time <= a.block_time + INTERVAL '30' DAY
ORDER BY a.price_eth DESC;
La jointure de la table avec elle-même associe chaque vente à une vente ultérieure du même jeton dans laquelle les rôles sont inversés. La syntaxe des intervalles varie selon les moteurs. Un résultat constitue une piste, pas une conclusion : il arrive que de vrais collectionneurs rachètent une pièce qu’ils ont vendue. La confiance augmente lorsque plusieurs signaux indépendants concordent, par exemple une source de financement commune, un aller-retour et une réclamation de récompense ; consultez la leçon Heuristiques fondées sur le modèle de compte pour l’analyse du financement.
Points clés pour l'examen : Transfer, Approval et ApprovalForAll d’ERC-721, et pourquoi un Transfer ERC-721 comporte quatre topics et des données vides ; TransferSingle et TransferBatch d’ERC-1155, le champ operator et le développement des tableaux de lots ; création et destruction via l’adresse zéro ; pourquoi un transfert n’a pas de prix et comment un événement de place de marché (offre et contrepartie) fournit le prix et la répartition des frais ; lots et sens des offres d’achat ; ERC-2981 comme norme informative et passage à des frais facultatifs pour les créateurs ; signaux de wash trading (cycles fermés, source de financement commune, destination commune, position sans risque) et cas de CryptoPunk 9998.