Pular para o conteúdo principal
Change page

Padrão de token ERC-223

Última atualização da página: 6 de setembro de 2025

Introdução

O que é ERC-223?

O ERC-223 é um padrão para tokens fungíveis, semelhante ao padrão ERC-20. A principal diferença é que o ERC-223 define não apenas a API do token, mas também a lógica para transferir tokens do remetente para o destinatário. Ele introduz um modelo de comunicação que permite que transferências de tokens sejam manipuladas pelo lado do destinatário.

Diferenças do ERC-20

O ERC-223 aborda algumas limitações do ERC-20 e introduz um novo método de interação entre o contrato de token e um contrato que pode receber os tokens. Há poucas coisas que são possíveis com ERC-223, mas não com ERC-20:

  • Tratamento de transferência de tokens no lado do destinatário: os destinatários podem detectar que um token ERC-223 está sendo depositado.
  • Rejeição de tokens enviados incorretamente: se um usuário enviar tokens ERC-223 para um contrato que não deveria receber tokens, o contrato poderá rejeitar a transação, evitando a perda de tokens.
  • Metadados em transferências: tokens ERC-223 podem incluir metadados, permitindo que informações arbitrárias sejam anexadas às transações de tokens.

Pré-requisitos

Body

ERC-223 é um padrão de token que implementa uma API para tokens dentro de contratos inteligentes. Ele também declara uma API para contratos que devem receber tokens ERC-223. Contratos que não suportam a API do Receptor ERC-223 não podem receber tokens ERC-223, evitando erros do usuário.

Se um contrato inteligente implementar os seguintes métodos e eventos, ele poderá ser chamado de contrato de token compatível com ERC-223. Uma vez implantado, ele será responsável por manter o controle dos tokens criados no Ethereum.

O contrato não é obrigado a ter apenas essas funções e um desenvolvedor pode adicionar qualquer outro recurso de diferentes padrões de token a este contrato. Por exemplo, as funções approve e transferFrom não fazem parte do padrão ERC-223, mas essas funções podem ser implementadas caso seja necessário.

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

Métodos

O token ERC-223 deve implementar os seguintes métodos:

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)

Um contrato que deve receber tokens ERC-223 deve implementar o seguinte método:

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

Se os tokens ERC-223 forem enviados para um contrato que não implementa a função tokenReceived(..), a transferência deverá falhar e os tokens não deverão ser movidos do saldo do remetente.

Eventos

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

Exemplos

A API do token ERC-223 é semelhante à do ERC-20, portanto, do ponto de vista do desenvolvimento da interface do usuário, não há diferença. A única exceção aqui é que os tokens ERC-223 não podem ter as funções approve + transferFrom, pois elas são opcionais para este padrão.

Exemplos de Solidity

O exemplo a seguir ilustra como um contrato básico de token ERC-223 opera:

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}
Exibir tudo

Agora queremos que outro contrato aceite depósitos de tokenA, assumindo que o tokenA é um token ERC-223. O contrato deve aceitar apenas o tokenA e rejeitar quaisquer outros tokens. Quando o contrato recebe o tokenA, ele deve emitir um evento Deposit() e aumentar o valor da variável interna deposits.

Aqui está o código:

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 override
6 {
7 // It is important to understand that within this function
8 // 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}
Exibir tudo

Perguntas mais frequentes

O que acontecerá se enviarmos algum tokenB para o contrato?

A transação falhará e a transferência de tokens não acontecerá. Os tokens serão devolvidos ao endereço do remetente.

Como podemos fazer um depósito neste contrato?

Chame a função transfer(address,uint256) ou transfer(address,uint256,bytes) do token ERC-223, especificando o endereço do RecipientContract.

O que acontecerá se transferirmos um token ERC-20 para este contrato?

Se um token ERC-20 for enviado para o RecipientContract, os tokens serão transferidos, mas a transferência não será reconhecida (nenhum evento Deposit() será disparado e o valor dos depósitos não será alterado). Depósitos indesejados de ERC-20 não podem ser filtrados ou evitados.

E se quisermos executar alguma função após a conclusão do depósito do token?

Há várias maneiras de fazer isso. Neste exemplo, seguiremos o método que torna as transferências ERC-223 idênticas às transferências de ether:

1contract RecipientContract is IERC223Recipient {
2 event Foo();
3 event Bar(uint256 someNumber);
4 address tokenA; // O único token que queremos aceitar.
5 function tokenReceived(address _from, uint _value, bytes memory _data) public override
6 {
7 require(msg.sender == tokenA);
8 address(this).call(_data); // Manipular a transação recebida e executar uma chamada de função subsequente.
9 }
10 function foo() public
11 {
12 emit Foo();
13 }
14 function bar(uint256 _someNumber) public
15 {
16 emit Bar(_someNumber);
17 }
18}
Exibir tudo

Quando o RecipientContract receber token de acordo à ERC-223 o contrato executará uma função codificada como parâmetro _data da transação do token, idêntico a como transações ether codificam chamadas de função como data da transação. Leia o campo de dados para obter mais informações.

No exemplo acima, um token ERC-223 deve ser transferido para o endereço do RecipientContract com a função transfer(address,uin256,bytes calldata _data). Se o parâmetro de dados for 0xc2985578 (a assinatura de uma função foo()), então a função foo() será invocada após o depósito do token ser recebido e o evento Foo() será disparado.

Os parâmetros também podem ser codificados nos data da transferência do token, por exemplo, podemos chamar a função bar() com o valor 12345 para _someNumber. Neste caso, o data deve ser 0x0423a13200000000000000000000000000000000000000000000000000000000000000000000004d2 onde 0x0423a132 é a assinatura da função bar(uint256) e 000000000000000000000000000000000000000000000000000000000000000000000004d2 é 12345 como uint256.

Limitações

Embora o ERC-223 aborde vários problemas encontrados no padrão ERC-20, ele não está isento de limitações:

  • Adoção e compatibilidade: o ERC-223 ainda não foi amplamente adotado, o que pode limitar sua compatibilidade com ferramentas e plataformas existentes.
  • Compatibilidade com versões anteriores: O ERC-223 não é compatível com versões anteriores do ERC-20, o que significa que os contratos e ferramentas ERC-20 existentes não funcionarão com tokens ERC-223 sem modificações.
  • Custos de gás: as verificações e funcionalidades adicionais nas transferências ERC-223 podem resultar em custos de gás mais altos em comparação às transações ERC-20.

Leitura adicional

Este artigo foi útil?