Standard dei token ERC-223
Ultima modifica: @hyperalchemy(opens in a new tab), 11 giugno 2024
Introduzione
Cos'è ERC-223?
ERC-223 è uno standard per token fungibili simile allo standard ERC-20. La differenza principale è che ERC-223 definisce non solo l'API del token ma anche la logica per trasferire i token dal mittente al destinatario. Introduce un modello di comunicazione che permette la gestione dei trasferimenti di token da parte del destinatario.
Differenze rispetto a ERC-20
ERC-223 affronta alcune limitazioni di ERC-20 e introduce un nuovo metodo di interazione tra il contratto del token e un contratto che potrebbe ricevere i token. Ci sono alcune cose che sono possibili con ERC-223 ma non con ERC-20:
- La gestione del trasferimento dei token da parte del destinatario: i destinatari possono rilevare che un token ERC-223 è stato depositato.
- Il rifiuto di token inviati erroneamente: se un utente manda dei token ERC-223 a un contratto che non dovrebbe ricevere token, il contratto può rifiutare la transazione impedendo la perdita di token.
- Metadati nei trasferimenti: i token ERC-223 possono includere metadati, permettendo di allegare informazioni arbitrarie alle transazioni di token.
Prerequisiti
Body
ERC-223 è uno standard di token che implementa un'API per i token con contratti intelligenti. Dichiara anche un'API per i contratti che dovrebbero ricevere token ERC-223. I contratti che non supportano l'API ERC-223 del destinatario non possono ricevere token ERC-223, impedendo un errore utente.
Se un contratto intelligente implementa i seguenti metodi ed eventi può essere definito contratto di toke compatibile con ERC-223. Una volta distribuito, sarà responsabile di tener traccia dei token creati su Ethereum.
Il contratto non è obbligato ad avere solo queste funzioni e uno sviluppatore può aggiungere qualsiasi altra funzione da diversi standard di token a questo contratto. Per esempio, le funzioni approve
e transferFrom
non sono parte dello standard ERC-223 ma queste funzioni potrebbero essere implementate se necessario.
Da EIP-223(opens in a new tab):
Metodi
Il token ERC-223 deve implementare i metodi seguenti:
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)Copia
Un contratto che debba ricevere token ERC-223 deve implementare il metodo seguente:
1function tokenReceived(address _from, uint _value, bytes calldata _data)Copia
Se i token ERC-223 sono inviati a un contratto che non ha implementato la funzione tokenReceived(..)
, il trasferimento deve fallire e i token non possono essere spostati dal saldo del mittente.
Eventi
1event Transfer(address indexed _from, address indexed _to, uint256 _value, bytes calldata _data)Copia
Esempi
L'API del token ERC-223 è simile a quella del ERC-20, quindi dalla prospettiva dello sviluppo della UI non ci sono differenze. L'unica eccezione qui è che i token ERC-223 potrebbero non avere le funzioni approve
+ transferFrom
dato che sono facoltative per questo standard.
Esempi in Solidity
L'esempio seguente illustra come funziona un contratto del token ERC-223 di 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}Mostra tuttoCopia
Adesso vogliamo che un altro contratto accetti i depositi di tokenA
presupponendo che il tokenA sia un token ERC-223. Il contratto deve accettare solo tokenA e rigettare qualsiasi altro token. Quando il contratto riceve tokenA deve emettere un evento Deposit()
e aumentare il valore della variabile interna deposits
.
Ecco il codice:
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}Mostra tuttoCopia
Domande frequenti
Cosa succede se mandiamo dei tokenB al contratto?
La transazione fallirà e il trasferimento dei token non avrà luogo. I token saranno restituiti all'indirizzo del mittente.
Come possiamo fare un deposito su questo contratto?
Chiamare la funzione transfer(address,uint256)
o transfer(address,uint256,bytes)
del token ERC-223 specificando l'indirizzo del RecipientContract
.
Cosa succede se trasferiamo un token ERC-20 a questo contratto?
Se un token ERC-20 viene inviato al RecipientContract
, i token saranno trasferiti ma il trasferimento non verrà riconosciuto (non sarà attivato alcun evento Deposit()
e il valore del deposito non cambierà). Non è possibile filtrare o impedire depositi di ERC-20 indesiderati.
E nel caso volessimo eseguire qualche funzione dopo che il deposito del token è completato?
Ci sono vari modi per farlo. In questo esempio seguiamo il metodo che rende i trasferimenti di ERC-223 identici a trasferimenti di 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}Mostra tuttoCopia
Quando il RecipientContract
riceverà un token ERC-223 il contratto eseguirà una funzione codificata come parametro _data
della transazione del token, in modo identico a come le transazioni di Ether codificano le chiamate di funzioni come transazioni data
. Leggi il campo dati(opens in a new tab) per maggiori informazioni.
Nell'esempio precedente un token ERC-223 deve essere trasferito all'indirizzo del RecipientContract
con la funzione transfer(address,uin256,bytes calldata _data)
. Se il parametro dei dati sarà 0xc2985578
(la firma di una funzione foo()
) allora la funzione foo() sarà invocata dopo che il deposito del token è stato ricevuto e l'evento Foo() è stato attivato.
I parametri possono essere codificati anche come data
del trasferimento del token, per esempio possiamo chiamare la funzione bar() con valore 12345 per _someNumber
. In questo caso data
deve essere 0x0423a13200000000000000000000000000000000000000000000000000000000000004d2
dove 0x0423a132
è la firma della funzione bar(uint256)
e 00000000000000000000000000000000000000000000000000000000000004d2
è 12345 in uint256.
Limitazioni
Nonostante ERC-223 affronti diversi problemi che si trovano nello standard ERC-20, non è privo di limitazioni:
- Adozione e compatibilità: ERC-223 non è ancora adottato ampiamente, il che potrebbe limitarne la compatibilità con strumenti e piattaforme esistenti.
- Compatibilità retroattiva: ERC-223 non è compatibile con le versioni precedenti di ERC-20, il che significa che i contratti ERC-20 e gli strumenti esistenti non funzionano con i token ERC-223 senza modifiche.
- Costi del gas: ulteriori controlli e funzionalità nei trasferimenti di ERC-223 potrebbero risultare in costi del gas maggiori rispetto a transazioni di ERC-20.