Norme de jeton ERC-223
Dernière modification: @XofEE(opens in a new tab), 11 juin 2024
Introduction
Qu'est-ce que l'ERC-223 ?
L'ERC-223 est une norme relative aux jetons fongibles, similaire à la norme ERC-20. La principale différence est que l'ERC-223 définit non seulement l'API du jeton, mais aussi la logique de transfert des jetons de l'expéditeur au destinataire. Il introduit un modèle de communication qui permet de gérer les transferts de jetons du côté du destinataire.
Différences par rapport à l'ERC-20
L'ERC-223 résout certaines limitations de l'ERC-20 et introduit une nouvelle méthode d'interaction entre le contrat de jeton et un contrat susceptible de recevoir les jetons. Certaines choses sont possibles avec l'ERC-223 mais pas avec l'ERC-20 :
- Gestion du transfert de jetons du côté du destinataire : les destinataires peuvent détecter qu'un jeton ERC-223 est en cours de dépôt.
- Rejet des jetons envoyés incorrectement : si un utilisateur envoie des jetons ERC-223 à un contrat qui n'est pas censé recevoir de jetons, le contrat peut rejeter la transaction, empêchant ainsi la perte de jetons.
- Métadonnées dans les transferts : Les jetons ERC-223 peuvent inclure des métadonnées, permettant d'attacher des informations arbitraires aux transactions de jetons.
Prérequis
Corps
L'ERC-223 est une norme de jeton qui implémente une API pour les jetons au sein de contrats intelligents. Il définit également une API pour les contrats qui sont censés recevoir des jetons ERC-223. Les contrats qui ne prennent pas en charge l'API de réception ERC-223 ne peuvent pas recevoir de jetons ERC-223, évitant ainsi les erreurs d'utilisation.
Si un contrat intelligent implémente les méthodes et événements suivants, il peut être considéré comme un contrat de jeton compatible avec l'ERC-223. Une fois déployé, il sera responsable de suivre les jetons créés sur Ethereum.
Le contrat n'est pas obligé de ne comporter que ces fonctions et un développeur peut ajouter à ce contrat n'importe quelle autre fonction issue de différentes normes de jetons. Par exemple, les fonctions approve
et transferFrom
ne font pas partie de la norme ERC-223, mais elles pourraient être implémentées si nécessaire.
Provenant de l'EIP-223(opens in a new tab):
Méthodes
Le jeton ERC-223 doit implémenter les méthodes suivantes :
1function name() public view returns (string)2function symbol() public view returns (string)3function decimals() public view returns (uint8)4function totalSupply() public view returns (uint256)5function balanceOf(address _owner) public view returns (uint256 balance)6function transfer(address _to, uint256 _value) public returns (bool success)7function transfer(address _to, uint256 _value, bytes calldata _data) public returns (bool success)Copier
Un contrat qui est censé recevoir des jetons ERC-223 doit implémenter la méthode suivante :
1function tokenReceived(address _from, uint _value, bytes calldata _data)Copier
Si des jetons ERC-223 sont envoyés à un contrat qui n'implémente pas la fonction tokenReceived(..)
, alors le transfert doit échouer et les jetons ne doivent pas être retirés du solde de l'expéditeur.
Événements
1event Transfer(address indexed _from, address indexed _to, uint256 _value, bytes calldata _data)Copier
Exemples
L'API du jeton ERC-223 est similaire à celle de l'ERC-20, donc du point de vue du développement de l'interface utilisateur, il n'y a pas de différence. La seule exception ici est que les jetons ERC-223 peuvent ne pas avoir les fonctions approve
et transferFrom
, car elles sont optionnelles pour cette norme.
Exemples sur Solidity
L'exemple suivant illustre le fonctionnement d'un contrat de jeton ERC-223 de base :
1pragma solidity ^0.8.19;2abstract contract IERC223Recipient {3 function tokenReceived(address _from, uint _value, bytes memory _data) public virtual;4}5contract VeryBasicERC223Token {6 event Transfer(address indexed from, address indexed to, uint value, bytes data);7 string private _name;8 string private _symbol;9 uint8 private _decimals;10 uint256 private _totalSupply;11 mapping(address => uint256) private balances;12 function name() public view returns (string memory) { return _name; }13 function symbol() public view returns (string memory) {return _symbol; }14 function decimals() public view returns (uint8) { return _decimals; }15 function totalSupply() public view returns (uint256) { return _totalSupply; }16 function balanceOf(address _owner) public view returns (uint256) { return balances[_owner]; }17 function isContract(address account) internal view returns (bool) {18 uint256 size;19 assembly { size := extcodesize(account) }20 return size > 0;21 }22 function transfer(address _to, uint _value, bytes calldata _data) public returns (bool success){23 balances[msg.sender] = balances[msg.sender] - _value;24 balances[_to] = balances[_to] + _value;25 if(isContract(_to)) {26 IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data);27 }28 emit Transfer(msg.sender, _to, _value, _data);29 return true;30 }31 function transfer(address _to, uint _value) public returns (bool success){32 bytes memory _empty = hex"00000000";33 balances[msg.sender] = balances[msg.sender] - _value;34 balances[_to] = balances[_to] + _value;35 if(isContract(_to)) {36 IERC223Recipient(_to).tokenReceived(msg.sender, _value, _empty);37 }38 emit Transfer(msg.sender, _to, _value, _empty);39 return true;40 }41}Afficher toutCopier
Nous souhaitons maintenant qu'un autre contrat accepte les dépôts de tokenA
, en supposant que tokenA
est un jeton ERC-223. Le contrat doit accepter uniquement tokenA
et rejeter tout autre jeton. Lorsque le contrat reçoit tokenA
, il doit émettre un événement Deposit()
et augmenter la valeur de la variable interne deposits
.
Voici le code :
1contract RecipientContract is IERC223Recipient {2 event Deposit(address whoSentTheTokens);3 uint256 deposits = 0;4 address tokenA; // The only token that we want to accept.5 function tokenReceived(address _from, uint _value, bytes memory _data) public override6 {7 // It is important to understand that within this function8 // msg.sender is the address of a token that is being received,9 // msg.value is always 0 as the token contract does not own or send Ether in most cases,10 // _from is the sender of the token transfer,11 // _value is the amount of tokens that was deposited.12 require(msg.sender == tokenA);13 deposits += _value;14 emit Deposit(_from);15 }16}Afficher toutCopier
Foire aux questions
Que se passera-t-il si nous envoyons des tokenB au contrat ?
La transaction échouera, et le transfert des jetons ne se produira pas. Les jetons seront renvoyés à l'adresse de l'expéditeur.
Comment pouvons-nous effectuer un dépôt sur ce contrat ?
Appelez la fonction transfer(address,uint256)
ou transfer(address,uint256,bytes)
du jeton ERC-223, en spécifiant l'adresse du RecipientContract
.
Que se passera-t-il si nous transférons un jeton ERC-20 à ce contrat ?
Si un jeton ERC-20 est envoyé au RecipientContract
, les jetons seront transférés, mais le transfert ne sera pas reconnu (aucun événement Deposit()
ne sera déclenché, et la valeur des dépôts ne changera pas). Les dépôts indésirables de jetons ERC-20 ne peuvent pas être filtrés ou empêchés.
Que faire si nous voulons exécuter une fonction après que le dépôt de jetons soit complété ?
Il existe plusieurs façons de procéder. Dans cet exemple, nous suivrons la méthode qui rend les transferts ERC-223 identiques aux transferts d'Ether :
1contract RecipientContract is IERC223Recipient {2 event Foo();3 event Bar(uint256 someNumber);4 address tokenA; // The only token that we want to accept.5 function tokenReceived(address _from, uint _value, bytes memory _data) public override6 {7 require(msg.sender == tokenA);8 address(this).call(_data); // Handle incoming transaction and perform a subsequent function call.9 }10 function foo() public11 {12 emit Foo();13 }14 function bar(uint256 _someNumber) public15 {16 emit Bar(_someNumber);17 }18}Afficher toutCopier
Lorsque le RecipientContract
recevra un jeton ERC-223, le contrat exécutera une fonction encodée comme paramètre _data
de la transaction de jeton, de manière identique à la façon dont les transactions Ether codent les appels de fonction en tant que data
de transaction. Consultez le champ de données(opens in a new tab) pour plus d'informations.
Dans l'exemple ci-dessus, un jeton ERC-223 doit être transféré à l'adresse du RecipientContract
avec la fonction transfer(address,uint256,bytes calldata _data)
. Si le paramètre de données est 0xc2985578
(la signature de la fonction faut()
), alors la fonction foo()
sera invoquée après la réception du dépôt de jetons, et l'événement Foo()
sera déclenché.
Les paramètres peuvent également être encodés dans les data
du transfert de jetons. Par exemple, nous pouvons appeler la fonction bar()
avec la valeur 12345
pour _someNumber
. Dans ce cas, les data
doivent être 0x0423a13200000000000000000000000000000000000000000000000000000000000004d2
, où 0x0423a132
est la signature de la fonction bar(uint256)
et 00000000000000000000000000000000000000000000000000000000000004d2
correspond à 12345
en tant que uint256
.
Limitations
Bien que l'ERC-223 résolve plusieurs problèmes présents dans la norme ERC-20, il n'est pas sans ses propres limitations :
- Adoption et compatibilité : l'ERC-223 n'est pas encore largement adopté, ce qui peut limiter sa compatibilité avec les plateformes et outils existants.
- Compatibilité rétroactive : l'ERC-223 n'est pas rétrocompatible avec l'ERC-20, ce qui signifie que les contrats et outils ERC-20 existants ne fonctionneront pas avec les jetons ERC-223 sans modifications.
- Coûts en gaz : les vérifications supplémentaires et les fonctionnalités des transferts ERC-223 peuvent entraîner des coûts en gaz plus élevés par rapport aux transactions ERC-20.