← Blog
10 min de lecture
|
24 oct. 2026

Événement ERC-20 Transfer : structure, topics et comment l'écouter

Comment fonctionne l'événement ERC-20 Transfer : sa signature, son hash de topic, comment décoder les logs, les pièges d'eth_getLogs, et comment obtenir des alertes de dépôt sans faire tourner de nœuds.

Chapitres
C
Chaingateway Team
Experts blockchain

Illustration abstraite de la structure d'événement de transfert de jeton

L’événement ERC-20 Transfer est le log qu’un contrat de jeton émet chaque fois que des jetons sont déplacés. Sa signature est Transfer(address indexed from, address indexed to, uint256 value), et c’est le seul moyen fiable pour une application hors chaîne d’apprendre qu’un paiement a eu lieu. Pour l’intercepter, vous filtrez les logs par le hachage du topic de l’événement, décodez l’expéditeur, le destinataire et le montant, et attendez que le bloc soit suffisamment sûr pour être fiable. Ce guide parcourt chaque étape, les pièges qui coûtent de l’argent réel, et quand il est judicieux d’arrêter de sonder les logs vous-même.

TL;DR

  • La norme définit Transfer(address indexed from, address indexed to, uint256 value). Il doit se déclencher à chaque mouvement de jetons, y compris les transferts de valeur nulle.
  • Pour filtrer les logs, utilisez le hachage keccak256 de Transfer(address,address,uint256) comme topic 0. Les topics 1 et 2 sont les adresses from et to complétées par du padding ; le montant se trouve dans le champ data.
  • eth_getLogs sur les points de terminaison publics a des limites et peut retourner des résultats incomplets pour de larges plages de blocs sans erreur. Paginez, et utilisez une infrastructure dédiée pour la production.
  • L’ERC-20 n’a pas de rappel (callback) vers le destinataire, donc l’écoute hors chaîne n’est pas optionnelle si vous créditez les soldes des utilisateurs.
  • Si vous préférez ne pas exécuter d’indexeurs de logs, un service de webhook peut pousser des notifications de transfert décodées vers votre backend.

Table des matières

Ce qu’est réellement l’événement Transfer

Un contrat intelligent peut émettre des événements, qui sont des signaux écrits dans le reçu de transaction sous forme de logs. Ils ne sont pas stockés dans l’état du contrat, ils sont donc peu coûteux à écrire et à lire, mais un contrat ne peut pas les lire par la suite. Leur public est le monde extérieur : portefeuilles, explorateurs de blocs, indexeurs et votre backend. Le tutoriel d’Ethereum sur les événements les décrit comme des signaux que les dapps et tout ce qui est connecté à l’API JSON-RPC peuvent écouter et sur lesquels agir.

Reçu d'événement séparé de l'état du contrat

Pour les jetons, la spécification ERC-20 définit l’événement ainsi :

event Transfer(address indexed _from, address indexed _to, uint256 _value)

Les règles qui comptent en pratique :

  • Il DOIT être émis lorsque des jetons sont transférés, et cela inclut les transferts de valeur nulle.
  • La fonction transfer doit le déclencher, et transferFrom également, la fonction qu’un dépensier utilise après qu’une autorisation a été accordée.
  • La création (minting) doit l’émettre avec l’adresse from définie sur l’adresse nulle. La destruction (burning) fait l’inverse, avec l’adresse nulle comme destinataire (de nombreuses implémentations suivent cette convention).

Ce dernier point est une source fréquente de bogues. Un écouteur qui ne surveille que les « transferts vers mon adresse » fonctionnera correctement, mais celui qui considère chaque Transfer comme un paiement entre utilisateurs mal interprétera les créations et les destructions.

Le solde du jeton est ce qui compte sur la chaîne, et l’événement en est un rapport. Si vous souhaitez des informations générales sur l’interface complète, nous l’abordons dans notre article sur la norme de jeton ERC-20.

Anatomie d’un log Transfer

Lorsque l’événement est émis, le nœud stocke un log composé de trois parties : l’adresse du contrat émetteur, une liste de topics et un bloc de données (data blob). L’indexation détermine où chaque argument se trouve.

Champ du logContenu pour un Transfer ERC-20
addressLe contrat de jeton qui a émis l’événement (par exemple un contrat de stablecoin)
topics[0]Hash de la signature de l’événement
topics[1]Expéditeur (from), complété à gauche sur 32 octets
topics[2]Destinataire (to), complété à gauche sur 32 octets
dataLa value, un entier non signé sur 32 octets

Les deux adresses sont indexées, elles sont donc recherchables via le filtre de log. La valeur n’est pas indexée, elle est donc stockée dans le champ data, ce qui réduit les coûts sur la chaîne et signifie que vous ne pouvez pas filtrer par montant au niveau du nœud. Si vous avez besoin des « transferts de plus de 1 000 jetons », vous les récupérez et les filtrez dans votre propre code.

Le hash du topic 0

Pour interroger via JSON-RPC, vous avez besoin du hash keccak256 de la chaîne de signature canonique Transfer(address,address,uint256). Sans espaces, sans noms de paramètres, sans mot-clé indexed. Le résultat est :

0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

La documentation sur les événements d’ethers.js utilise ce même mécanisme : le topic 0 est le hash de la signature, et les topics 1 et 2 correspondent aux adresses from et to indexées, chacune complétée sur 32 octets.

Deux détails font gagner du temps de débogage :

  1. Les décimales sont votre responsabilité. La value est un entier brut. Un jeton avec 6 décimales et une valeur de 2500000 correspond à 2,5 jetons. Lisez decimals() depuis le contrat (ou mettez-le en cache par jeton) avant d’afficher les montants.
  2. Même hash, normes différentes. ERC-721 utilise le même nom d’événement et les mêmes types d’arguments, donc son topic 0 est identique. La différence est que l’identifiant de jeton (token ID) est indexé, donc un log de transfert NFT comporte quatre topics et un champ data vide, tandis qu’un transfert fongible en comporte trois. Vérifiez le nombre de topics avant de décoder, sinon vous analyserez les déplacements de NFT comme des paiements de jetons. Notre article sur la norme ERC-721 traite ce sujet.

Lire les transferts avec eth_getLogs

L’approche classique est un appel JSON-RPC direct. Un filtre qui trouve tous les transferts d’un jeton vers un portefeuille ressemble à ceci :

{
"method": "eth_getLogs",
"params": [{
"fromBlock": "0x10d4f00",
"toBlock": "0x10d4f64",
"address": "0xTokenContractAddress",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
null,
"0x000000000000000000000000YourDepositAddressWithoutThe0x"
]
}]
}

Un null à une position de topic signifie « n’importe quelle valeur ». Ici, nous acceptons n’importe quel expéditeur et fixons le destinataire. Notez que le destinataire est complété par des zéros pour atteindre 32 octets.

Si vous préférez une bibliothèque, une courte démonstration telle que ce gist web3.py pour les logs de transfert ERC-20 montre le même appel encapsulé en Python. Pour des mises à jour en temps réel au lieu de l’historique, eth_subscribe avec le type logs envoie les nouvelles correspondances via un WebSocket.

Là où eth_getLogs vous mord

Cela semble simple, et cela fonctionne dans un tutoriel. La production est différente. L’article de SQD sur les limites de eth_getLogs note que les fournisseurs de nœuds standards plafonnent souvent le nombre de logs retournés, et qu’interroger une grande plage de blocs peut produire des données incomplètes sans aucune erreur. Leur conseil : paginer les requêtes et utiliser des nœuds d’archive pour les données historiques. Le même article avertit que compter sur des endpoints publics pour une utilisation intensive en production est peu fiable, en raison des limitations des filtres de Bloom et des lacunes d’index qui diffèrent d’un nœud à l’autre.

Les règles pratiques :

  • Interrogez de petites fenêtres de blocs et avancez, plutôt que de demander une seule plage géante.
  • Comparez le nombre de résultats à ce que vous attendez. Une troncature silencieuse ressemble exactement à une journée calme.
  • Stockez le dernier numéro de bloc traité, afin qu’un redémarrage reprenne là où il s’est arrêté au lieu de sauter des blocs.
  • Conservez un chevauchement de quelques blocs à chaque passage et dédupliquez sur le hachage de transaction plus l’index de log.

Confirmations, réorganisations et idempotence

Un log apparaissant dans le dernier bloc n’est pas final. La chaîne peut se réorganiser, et un bloc qui contenait votre transfert peut être remplacé par un autre qui ne le contient pas. Un pipeline de dépôt solide traite l’événement comme une revendication, pas comme un fait, tant que suffisamment de blocs ne se sont pas construits au-dessus.

Une machine à états simple fonctionne bien :

  1. Vu. Le log est détecté. Enregistrez-le, affichez « en attente » à l’utilisateur, et ne créditez rien.
  2. Confirmé. Le bloc est plus ancien que votre seuil choisi. Créditez le solde une fois.
  3. Orphelin. Le log a disparu après une réorganisation. Marquez l’enregistrement comme invalide et alertez qui de droit.

Le seuil est une décision métier. Un petit achat tolère moins de confirmations qu’un dépôt important éligible au retrait. Choisissez-le par chaîne et par montant, et documentez-le.

Quoi que vous choisissiez, rendez le crédit idempotent. La clé unique pour un log est le hachage de transaction plus l’index de log, pas le hachage de transaction seul, car une transaction peut émettre plusieurs événements Transfer (un swap, par exemple, en émet au moins deux). Si votre gestionnaire reçoit le même événement deux fois, suite à une nouvelle tentative ou une seconde source, il ne doit rien faire la deuxième fois.

Pourquoi vous ne pouvez pas compter sur le destinataire pour remarquer

Les transferts ERC-20 sont silencieux du côté du destinataire. Une adresse de portefeuille n’exécute pas de code, et un contrat destinataire n’est informé de rien. La page ERC-20 d’ethereum.org souligne que les jetons sont perdus lorsqu’ils sont envoyés à des contrats qui ne reconnaissent pas le transfert entrant, car la norme ne prévoit pas de rappel natif, et elle indique que plus de 83 656 418 $ de jetons ERC-20 avaient été perdus de cette manière au 20 juin 2024. Une atténuation courante consiste en une vérification require dans le jeton qui bloque l’envoi vers le contrat du jeton lui-même.

Deux enseignements pour les développeurs :

  • Si vous gérez un contrat qui doit recevoir des jetons, vous avez besoin de votre propre comptabilité. Des normes comme les hooks de style ERC-1363 existent pour cela, mais vous ne pouvez pas supposer que le jeton de l’expéditeur les prend en charge.
  • Si vous gérez un backend, le journal Transfer est votre seul déclencheur. En manquer un signifie qu’un client a payé et que vous ne l’avez jamais su.

Il existe un deuxième manque : l’événement ne transporte aucune référence de paiement. Rien ne lie un transfert à la facture 4471. La plupart des entreprises résolvent ce problème en attribuant à chaque facture ou utilisateur une adresse de dépôt unique. Une proposition plus récente, ERC-7699, étendrait l’ERC-20 avec une référence de transfert pour qu’un message pour le destinataire voyage avec le paiement, mais c’est une proposition et les jetons populaires d’aujourd’hui ne l’implémentent pas.

Cas limites qui cassent les écouteurs naïfs

Jetons de style natif. Sur certaines chaînes, un stablecoin est intégré au protocole plutôt que d’être un contrat normal. La documentation d’Arc sur la détection des dépôts explique que les mouvements natifs d’USDC y émettent un journal Transfer ERC-20 standard depuis une adresse système, donc les développeurs filtrent sur cet émetteur plutôt que sur le contrat de jeton individuel. La leçon est générale : lisez la documentation sur les dépôts de chaque chaîne avant de supposer qu’un filtre sur l’adresse du contrat suffit.

Jetons avec frais sur transfert et jetons à rebasage. Le montant dans l’événement peut différer de ce que le solde du destinataire change réellement, ou les soldes peuvent changer sans aucun Transfer. Si vous acceptez des jetons inconnus, réconciliez avec balanceOf au lieu de faire confiance à l’événement seul.

Transferts de valeur nulle. La norme exige qu’ils émettent l’événement. Les attaquants utilisent cela pour l’empoisonnement d’adresses, en envoyant des transferts de valeur nulle qui placent une adresse similaire dans l’historique d’une victime. Ignorez les journaux de valeur nulle dans tout flux de dépôt.

Jetons usurpés. N’importe qui peut déployer un contrat qui émet un événement Transfer avec n’importe quel nom de jeton. Filtrez sur les adresses de contrats que vous reconnaissez, pas seulement sur le topic 0.

Cinq cas limites courants des écouteurs ERC-20

Autres chaînes. La même forme d’événement apparaît sur les réseaux compatibles EVM tels que Polygon, BNB Chain (BEP-20) et Arbitrum. Les jetons TRC-20 de TRON suivent la même idée avec leurs propres outils ; consultez notre comparaison des TRC10 et TRC20 pour voir en quoi ils diffèrent.

Construire ou acheter : exécuter votre propre écouteur

Un écouteur fait maison est un bon projet et un choix judicieux lorsque les données blockchain sont votre produit principal. Pour la plupart des équipes qui acceptent des paiements, c’est une quête annexe avec une longue traîne de maintenance. Vous possédez le nœud ou le contrat du fournisseur, le curseur de bloc, la gestion des réorganisations, les nouvelles tentatives, le décodage pour chaque jeton, et la même pile à nouveau pour chaque chaîne supplémentaire.

Une méthode approximative pour décider :

QuestionSi oui, penchez vers la constructionSi non, penchez vers une API
Les données on-chain sont-elles votre produit ?OuiNon
Avez-vous besoin d’analyses de logs personnalisées au-delà des paiements ?OuiNon
Disposez-vous d’un ingénieur qui assurera la disponibilité des nœuds et la logique de réorganisation ?OuiNon
Prenez-vous en charge plusieurs chaînes dès le premier jour ?NonOui

Si vous atterrissez dans la colonne de droite, une API gérée qui vous envoie des événements décodés supprime toute la couche d’indexation.

Perspective : ce que nous observons lorsque les équipes construisent la détection de dépôts

Nous développons une API unifiée pour les paiements en crypto, donc nous voyons le même schéma se répéter inlassablement. Une équipe commence avec une boucle eth_getLogs pour un token sur une chaîne. Cela fonctionne. Puis ils ajoutent une deuxième chaîne, une deuxième norme de token, un job de relance et un correctif de réorganisation, et la fonctionnalité de paiement est devenue silencieusement un projet d’infrastructure.

Notre avis est que l’événement Transfer est la bonne primitive mais le mauvais endroit pour investir votre temps d’ingénierie. Ce dont votre application a besoin, c’est d’une affirmation claire : cette adresse a reçu ce montant de ce token dans cette transaction. C’est pourquoi nous fournissons des données pré-décodées plutôt que des topics bruts, et pourquoi les notifications arrivent via des webhooks signés HMAC, afin que votre serveur puisse vérifier qu’un message provient vraiment de nous avant de créditer qui que ce soit. Nous pensons également que vous ne devriez pas avoir à exécuter vos propres nœuds ou à jongler avec des endpoints spécifiques à chaque chaîne pour y parvenir.

Les conseils que nous donnons quel que soit l’outillage : utilisez une adresse de dépôt par payeur ou par facture, créditez de manière idempotente, attendez des confirmations adaptées au montant en jeu, et vérifiez l’adresse du contrat du token. Ces quatre habitudes préviennent la plupart des incidents dont nous entendons parler.

— Bitblade

Recevoir des notifications de transfert sans exécuter d’indexeurs

Chaingateway offre aux développeurs une API REST unique pour les portefeuilles, les transferts de tokens et les données de transactions décodées sur Ethereum, Bitcoin, Tron, Solana, Polyggon, BNB Chain et Arbitrum. Pour ce sujet, l’élément pertinent est Chaingateway Webhooks : vous abonnez une adresse, et lorsqu’un transfert de token la concerne, une notification en temps réel atteint votre endpoint avec les données déjà décodées, vous évitant ainsi le hachage et le padding des topics. Notre page Ethereum API montre comment envoyer des tokens ERC-20 via REST sans web3.js, et le guide d’adresse ERC-20 traite du format d’adresse et de la vérification des soldes.

Les plans sont publiés sur la page de tarification : Plus à 49 € par mois, Pro à 99 €, Premium à 249 € et Enterprise à 499 € par mois, avec une facturation annuelle à 490 €, 990 €, 2 490 € et 4 990 € respectivement. L’accès à la Blockchain API commence par un essai gratuit.

Un gestionnaire minimal, étape par étape

Que vous utilisiez des logs bruts ou des webhooks, la logique côté serveur reste la même.

  1. Vérifier la source. Pour les webhooks, contrôlez la signature HMAC sur le corps brut de la requête avant de l’analyser. Pour votre propre lecteur de logs, vous faites déjà confiance à votre nœud.
  2. Vérifier le token. Comparez l’adresse du contrat avec votre liste d’autorisation.
  3. Vérifier le destinataire. Recherchez l’adresse to dans votre base de données d’adresses de dépôt. Ignorez tout ce qui est inconnu.
  4. Éliminer le bruit. Supprimez les transferts de valeur nulle ainsi que les mint et burn qui ne vous intéressent pas.
  5. Dédupliquer. Insérez le hachage de transaction et l’index du log sous une contrainte d’unicité. Si l’insertion échouez, arrêtez-vous.
  6. Convertir le montant. Divisez la valeur brute par 10 à la puissance des décimales du token, en utilisant des types entiers ou décimaux, jamais des nombres à virgule flottante.
  7. Enregistrer comme en attente, puis confirmé. Créditez uniquement après que votre seuil de confirmation est atteint.
  8. Répondre rapidement. Acquittez un webhook avec un statut de succès immédiatement et effectuez le travail lourd dans une file d’attente.

Sources

Recommandé

Questions fréquentes

C’est le hachage keccak256 de Transfer(address,address,uint256), à savoir 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. Il se trouve toujours dans le sujet 0 du log. Les sujets 1 et 2 contiennent l’expéditeur et le destinataire.

Les arguments indexés deviennent des sujets que les nœuds peuvent rechercher, mais chacun ajoute un coût de log, et la norme ne conserve que les deux adresses indexées. La valeur va dans le champ data, vous la décodez donc vous-même et filtrez les montants dans votre propre code.

La norme l’exige, y compris pour les transferts de valeur nulle. En pratique, les contrats conformes le font, mais les tokens non standards peuvent se comporter différemment, et certaines chaînes gèrent les actifs natifs de manière particulière. Traitez l’événement comme le signal principal et réconciliez avec balanceOf lorsque le token est inconnu.

Uniquement avec précaution. Les fournisseurs peuvent plafonner les logs retournés, et de larges plages peuvent renvoyer des données incomplètes sans erreur. Paginez par petites fenêtres, suivez votre dernier bloc traité, et utilisez une infrastructure dédiée ou un service de notification pour tout ce qui gère de l’argent réel.

Il n’y a pas de nombre universel. Cela dépend de la chaîne, du montant et de votre tolérance au risque. Définissez un seuil par chaîne et par tranche de valeur, et créditez une seule fois, après qu’il est franchi.

Prêt à le construire vous-même ? Obtenir votre clé API — essai de 7 jours, sans carte bancaire — ou consultez API Blockchain pour la référence complète des endpoints.

C
Chaingateway Team
Experts blockchain

L'équipe Chaingateway s'attache à simplifier l'intégration blockchain pour les développeurs du monde entier.