Przejdź do głównej zawartości
Change page

Standard tokenów ERC-223

Strona ostatnio zaktualizowana: 6 września 2025

Wprowadzenie

Czym jest ERC-223?

ERC-223 jest standardem dla tokenów wymienialnych, podobnie jak standard ERC-20. Kluczową różnicą jest to, że ERC-223 określa nie tylko API tokena, ale również logikę odpowiedzialną za transfer tokenów od nadawcy do odbiorcy. Wprowadza model komunikacji, który zezwala na obsługę transferów tokenów po stronie odbiorcy.

Różnice w stosunku do ERC-20

ERC-223 rozwiązuje pewne ograniczenia ERC-20 oraz wprowadza nową metodę interakcji między kontraktem tokena, a kontraktem, które może otrzymać tokeny. Istnieje kilka rzeczy, które są możliwe z ERC-223, ale nie z ERC-20:

  • Obsługa transferu tokenów po stronie odbiorcy: odbiorcy mogą wykryć, że token ERC-223 zostaje wpłacony.
  • Odrzucenie nieprawidłowo wysłanych tokenów: jeśli użytkownik wyśle tokeny ERC-223 do kontraktu, który nie powinien otrzymać tokenów, to kontrakt może odrzucić transakcję, zapobiegając utracie tokenów.
  • Metadane w transferach: tokeny ERC-223 mogą zawierać metadane zezwalające na dołączenie dowolnych informacji do transakcji tokenowych.

Wymagania wstępne

Treść

ERC-223 to standard tokenów, który implementuje API dla tokenów w inteligentnych kontraktach. Deklaruje również API dla kontraktów, które miałyby otrzymywać tokeny ERC-223. Kontrakty, które nie wspierają API odbiorcy ERC-223, nie mogą otrzymać tokenów ERC-223, co zapobiega przed błędami użytkownika.

Jeśli inteligentny kontrakt implementuje następujące metody i zdarzenia to może zostać nazwanym kontraktem tokena zgodnym z ERC-223. Po wdrożeniu będzie on odpowiedzialny za monitorowanie stworzonym tokenów na Ethereum.

Kontrakt nie jest zobowiązany, aby mieć tylko te funkcje, a deweloper może dodać każdą inną funkcję od innego standardu tokenów to tego kontraktu. Dla przykładu, funkcje approve oraz transferFrom nie są częścią standardu ERC-223, ale mogą zostać zaimplementowane, jeśli tylko zajdzie taka potrzeba.

Na podstawie EIP-223 (opens in a new tab):

Metody

Token ERC-223 musi zaimplementować następujące metody:

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)

A kontrakt, który miałby otrzymać tokeny ERC-223 musi zaimplementować następującą metodę:

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

Jeśli tokeny ERC-223 zostaną wysłane do kontraktu, który nie zaimplementował funkcji tokenReceived(..), to transfer musi się zakończyć niepowodzeniem, a tokeny nie mogą zostać przeniesione z konta nadawcy.

Zdarzenia

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

Przykłady

API tokena ERC-223 jest podobne do tego z ERC-20, więc z punktu widzenia rozwoju UI, nie ma różnicy. Jedynym wyjątkiem tutaj jest to, że tokeny ERC-223 mogą nie mieć funkcji approve i transferFrom, ponieważ te są opcjonalne dla tego standardu.

Przykłady w Solidity

Poniższy przykład pokazuje, w jaki sposób działa podstawowy kontrakt tokena ERC-223:

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}
Pokaż wszystko

Teraz chcemy, aby inny kontrakt zaakceptował wpłaty tokena o nazwie tokenA zakładając, że tokenA jest tokenem ERC-223. Kontrakt musi akceptować tylko tokenA oraz odrzucać wszystkie inne tokeny. Kiedy kontrakt otrzyma tokenA, music wyemitować zdarzenie Deposit() oraz zwiększyć wartość wewnętrznej zmiennej deposits.

Oto kod:

1contract RecipientContract is IERC223Recipient {
2 event Deposit(address whoSentTheTokens);
3 uint256 deposits = 0;
4 address tokenA; // Jedyny token, który chcemy akceptować.
5 function tokenReceived(address _from, uint _value, bytes memory _data) public override
6 {
7 // Ważne jest, aby zrozumieć, że w ramach tej funkcji
8 // msg.sender to adres otrzymywanego tokenu,
9 // msg.value ma zawsze wartość 0, ponieważ kontrakt tokenu w większości przypadków nie posiada ani nie wysyła etheru,
10 // _from to nadawca transferu tokenu,
11 // _value to kwota tokenów, które zostały zdeponowane.
12 require(msg.sender == tokenA);
13 deposits += _value;
14 emit Deposit(_from);
15 }
16}
Pokaż wszystko

Często zadawane pytania

Co się stanie, jeśli wyślę tokenB do kontraktu?

Transakcja się nie powiedzie, a transfer tokenów się nie wydarzy. Tokeny zostają zwrócone na adres nadawcy.

Jak mogę dokonać wpłaty na ten kontrakt?

Wywołaj funkcję transfer(address,uint256) lub transfer(address,uint256,bytes) tokena ERC-223, określając adres RecipientContract.

Co się stanie, jeśli wyślę token ERC-20 do tego kontraktu?

Jeśli token ERC-20 zostanie wysłany do RecipientContract, to tokeny zostaną przesłane, ale transfer nie zostanie rozpoznany (nie zostanie uruchomione żadnego zdarzenie Deposit(), a wartość depozytów nie zostanie zmieniona). Niechcianych wpłat ERC-20 nie można filtrować ani im zapobiegać.

Co, jeśli chcę wykonać jakąś funkcję po zakończeniu wpłaty tokena?

Istnieje kilka sposobów na zrobienie tego. W tym przykładzie zastosujemy metodę, która sprawia, że transfery ERC-223 są identyczne, jak transfery etheru:

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 override
6 {
7 require(msg.sender == tokenA);
8 address(this).call(_data); // Handle incoming transaction and perform a subsequent function call.
9 }
10 function foo() public
11 {
12 emit Foo();
13 }
14 function bar(uint256 _someNumber) public
15 {
16 emit Bar(_someNumber);
17 }
18}
Pokaż wszystko

Kiedy RecipientContract otrzyma token ERC-223, kontrakt wykona funkcję zakodowaną jako parametr _data transakcji tokena, identycznie do tego, jak transakcje etheru kodują wywołania funkcji jako data transakcji. Przeczytaj o polu danych po więcej informacji.

W powyższym przykładzie token ERC-223 musi zostać przeniesiony na adres RecipientContract przy użyciu funkcji transfer(address,uin256,bytes calldata _data). Jeśli parametr danych będzie wynosił 0xc2985578 (podpis funkcji foo()), to funkcja foo() zostanie wywołana po otrzymaniu wpłaty tokena oraz zostanie uruchomione zdarzenie Foo().

Parametry mogą również zostać zakodowane w data transferu tokena, dla przykładu możemy wywołać funkcję bar() z wartością 12345 dla _someNumber. W tym przypadku data musi wynosić 0x0423a13200000000000000000000000000000000000000000000000000000000000004d2, gdzie 0x0423a132 to podpis funkcji bar(uint256), a 00000000000000000000000000000000000000000000000000000000000004d2 to 12345 w formie uint256.

Ograniczenia

Chociaż ERC-223 rozwiązuje parę problemów znalezionych w standardzie ERC-20, to nie jest pozbawiony on własnych ograniczeń:

  • Przyjęcie i kompatybilność: ERC-223 nie jest jeszcze powszechnie przyjęty, co może ograniczać jego kompatybilność z istniejącymi narzędziami i platformami.
  • Kompatybilność wsteczna: ERC-223 nie jest wstecznie kompatybilny z ERC-20, co oznacza, że istniejące kontrakty ERC-20 i narzędzia nie będą działać z tokenami ERC-223 bez modyfikacji.
  • Koszty gazu: dodatkowe kontrole i funkcje w transferach ERC-223 mogą powodować większe koszta gazu w porównaniu z transakcjami ERC-20.

Dalsza lektura

Czy ten artykuł był pomocny?