Vai al contenuto principale
Change page

Standard dei Token ERC-223

Ultimo aggiornamento della pagina: 6 settembre 2025

Introduzione

Cos'è l'ERC-223?

L'ERC-223 è uno standard per i token fungibili, simile allo standard ERC-20. La differenza chiave è che l'ERC-223 definisce non solo l'API del token, ma anche la logica per il trasferimento dei token dal mittente al destinatario. Introduce un modello di comunicazione che consente di gestire i trasferimenti di token dal lato del destinatario.

Differenze rispetto all'ERC-20

L'ERC-223 affronta alcune limitazioni dell'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 l'ERC-223 ma non con l'ERC-20:

  • Gestione del trasferimento di token dal lato del destinatario: i destinatari possono rilevare che un token ERC-223 viene depositato.
  • Rifiuto di token inviati in modo improprio: se un utente invia token ERC-223 a un contratto che non dovrebbe ricevere token, il contratto può rifiutare la transazione, prevenendo la perdita di token.
  • Metadati nei trasferimenti: i token ERC-223 possono includere metadati, consentendo di allegare informazioni arbitrarie alle transazioni di token.

Prerequisiti

Corpo

L'ERC-223 è uno standard di token che implementa un'API per i token all'interno dei contratti intelligenti. Dichiara inoltre un'API per i contratti che dovrebbero ricevere token ERC-223. I contratti che non supportano l'API del Ricevitore ERC-223 non possono ricevere token ERC-223, prevenendo errori da parte dell'utente.

Se un contratto intelligente implementa i seguenti metodi ed eventi, può essere definito un contratto di token compatibile con l'ERC-223. Una volta distribuito, sarà responsabile di tenere traccia dei token creati su Ethereum.

Il contratto non è obbligato ad avere solo queste funzioni e uno sviluppatore può aggiungere a questo contratto qualsiasi altra funzionalità da diversi standard di token. Ad esempio, le funzioni approve e transferFrom non fanno parte dello standard ERC-223, ma queste funzioni potrebbero essere implementate qualora fosse necessario.

Da EIP-223 (opens in a new tab):

Metodi

Il token ERC-223 deve implementare i seguenti metodi:

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)

Un contratto che dovrebbe ricevere token ERC-223 deve implementare il seguente metodo:

1function tokenReceived(address _from, uint _value, bytes calldata _data)

Se i token ERC-223 vengono inviati a un contratto che non implementa la funzione tokenReceived(..), il trasferimento deve fallire e i token non devono essere spostati dal saldo del mittente.

Eventi

1event Transfer(address indexed _from, address indexed _to, uint256 _value, bytes calldata _data)

Esempi

L'API del token ERC-223 è simile a quella dell'ERC-20, quindi dal punto di vista dello sviluppo dell'interfaccia utente non c'è alcuna differenza. L'unica eccezione qui è che i token ERC-223 potrebbero non avere le funzioni approve + transferFrom, poiché queste sono opzionali per questo standard.

Esempi in Solidity

Il seguente esempio illustra come opera un contratto di 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 tutto

Ora vogliamo che un altro contratto accetti depositi di tokenA supponendo che tokenA sia un token ERC-223. Il contratto deve accettare solo tokenA e rifiutare 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; // L'unico token che vogliamo accettare.
5 function tokenReceived(address _from, uint _value, bytes memory _data) public override
6 {
7 // È importante capire che all'interno di questa funzione
8 // msg.sender è l'indirizzo di un token che viene ricevuto,
9 // msg.value è sempre 0 poiché il contratto del token non possiede né invia ether nella maggior parte dei casi,
10 // _from è il mittente del trasferimento del token,
11 // _value è la quantità di token che è stata depositata.
12 require(msg.sender == tokenA);
13 deposits += _value;
14 emit Deposit(_from);
15 }
16}
Mostra tutto

Domande frequenti

Cosa succederà se inviamo del tokenB al contratto?

La transazione fallirà e il trasferimento dei token non avverrà. I token verranno restituiti all'indirizzo del mittente.

Come possiamo effettuare un deposito su questo contratto?

Chiama la funzione transfer(address,uint256) o transfer(address,uint256,bytes) del token ERC-223, specificando l'indirizzo del RecipientContract.

Cosa succederà se trasferiamo un token ERC-20 a questo contratto?

Se un token ERC-20 viene inviato al RecipientContract, i token verranno trasferiti, ma il trasferimento non sarà riconosciuto (nessun evento Deposit() verrà attivato e il valore dei depositi non cambierà). I depositi ERC-20 indesiderati non possono essere filtrati o prevenuti.

E se volessimo eseguire una funzione dopo il completamento del deposito del token?

Ci sono diversi modi per farlo. In questo esempio seguiremo il metodo che rende i trasferimenti ERC-223 identici ai trasferimenti di ether:

1contract RecipientContract is IERC223Recipient {
2 event Foo();
3 event Bar(uint256 someNumber);
4 address tokenA; // L'unico token che vogliamo accettare.
5 function tokenReceived(address _from, uint _value, bytes memory _data) public override
6 {
7 require(msg.sender == tokenA);
8 address(this).call(_data); // Gestire la transazione in entrata ed eseguire una successiva chiamata di funzione.
9 }
10 function foo() public
11 {
12 emit Foo();
13 }
14 function bar(uint256 _someNumber) public
15 {
16 emit Bar(_someNumber);
17 }
18}
Mostra tutto

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 funzione come data della transazione. Leggi il campo dei dati per maggiori informazioni.

Nell'esempio sopra, 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() verrà invocata dopo aver ricevuto il deposito del token e verrà attivato l'evento Foo().

I parametri possono essere codificati anche nei data del trasferimento del token, ad esempio possiamo chiamare la funzione bar() con il valore 12345 per _someNumber. In questo caso i data devono essere 0x0423a13200000000000000000000000000000000000000000000000000000000000004d2 dove 0x0423a132 è la firma della funzione bar(uint256) e 00000000000000000000000000000000000000000000000000000000000004d2 è 12345 come uint256.

Limitazioni

Sebbene l'ERC-223 affronti diversi problemi riscontrati nello standard ERC-20, non è privo di limitazioni:

  • Adozione e compatibilità: l'ERC-223 non è ancora ampiamente adottato, il che potrebbe limitare la sua compatibilità con gli strumenti e le piattaforme esistenti.
  • Retrocompatibilità: l'ERC-223 non è retrocompatibile con l'ERC-20, il che significa che i contratti e gli strumenti ERC-20 esistenti non funzioneranno con i token ERC-223 senza modifiche.
  • Costi del gas: i controlli e le funzionalità aggiuntive nei trasferimenti ERC-223 potrebbero comportare costi del gas più elevati rispetto alle transazioni ERC-20.

Letture consigliate

Questo articolo è stato utile?