Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ERC-223 to token standards. #9646

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/content/developers/docs/standards/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ More detailed information on these different types and categories can be found i
### Token standards {#token-standards}

- [ERC-20](/developers/docs/standards/tokens/erc-20/) - A standard interface for fungible (interchangeable) tokens, like voting tokens, staking tokens or virtual currencies.
- [ERC-1363](https://eips.ethereum.org/EIPS/eip-1363) - Defines a token interface for ERC-20 tokens that supports executing recipient code after transfer or transferFrom, or spender code after approve
- [ERC-1363](https://eips.ethereum.org/EIPS/eip-1363) - Defines a token interface for ERC-20 tokens that supports executing recipient code after transfer or transferFrom, or spender code after approve.
- [ERC-223](/developers/docs/standards/tokens/erc-223/) - A fungible tokens standard that makes tokens behave identical to plain Ether and supports token transfers handling on the recipients side.
- [ERC-721](/developers/docs/standards/tokens/erc-721/) - A standard interface for non-fungible tokens, like a deed for artwork or a song.
- [ERC-2309](https://eips.ethereum.org/EIPS/eip-2309) - A standardized event emitted when creating/transferring one, or many non-fungible tokens using consecutive token identifiers.
- [ERC-4400](https://eips.ethereum.org/EIPS/eip-4400) - Interface extension for EIP-721 consumer role
- [ERC-4400](https://eips.ethereum.org/EIPS/eip-4400) - Interface extension for EIP-721 consumer role.
- [ERC-4907](https://eips.ethereum.org/EIPS/eip-4907) - Add a time-limited role with restricted permissions to ERC-721 tokens.
- [ERC-777](/developers/docs/standards/tokens/erc-777/) - **(NOT RECOMMENDED)** A token standard improving over ERC-20.
- [ERC-1155](/developers/docs/standards/tokens/erc-1155/) - A token standard which can contain both fungible and non-fungible assets.
Expand Down
187 changes: 187 additions & 0 deletions src/content/developers/docs/standards/tokens/erc-223/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
---
title: ERC-223 Token Standard
description:
lang: en
---

## Introduction {#introduction}

**What is ERC-223?**

The ERC-223 is another standard for Fungible Tokens, like the ERC-20. The key difference is that ERC-223 defines not only the token API, but also the logic of how tokens should be transferred from sender to recipient and introduces a communication model that allows token transfers to be handled on the recipients side.

**How is it different from ERC-20 and why we need another token standard?**

ERC-223 addresses some limitations of ERC-20 and introduces a new method of interactions between token contract and a contract that may receive the tokens. There are few things that are possible with ERC-223 but not with ERC-20:

- Token transfer handling on the recipient's side. Recipient can detect that a ERC-223 token is being deposited.
- Rejection of improperly sent tokens. If a user sent ERC-223 tokens to a contract that is not supposed to receive tokens then the contract can reject the transaction and the tokens will not be lost.
- The transfer of ERC-223 tokens may contain metadata, which allows arbitrary information to be attached to the token transactions.

## Prerequisites {#prerequisites}

- [Accounts](/developers/docs/accounts)
- [Smart Contracts](/developers/docs/smart-contracts/)
- [Token standards](/developers/docs/standards/tokens/)
- [ERC-20](/developers/docs/standards/tokens/erc-20/)

## Body {#body}

The ERC-223 (Ethereum Request for Comments 223), proposed by Dexaran in March 2017, is a Token Standard that
implements an API for tokens within Smart Contracts and declares API for a contract that is supposed to receive ERC-223 tokens. Any contract that does not support ERC-223 Receiver API can not receive ERC-223 tokens which prevents the most common user mistakes.

If a Smart Contract implements the following methods and events it can be called a ERC-223 compatible token contract and, once deployed, it
will be responsible to keep track of the created tokens on Ethereum.

The contract is not obligated to have only this functions and a developer can add any other feature from different token standards to this contract. For example, `approve` and `transferFrom` functions are not part of ERC-223 standard but these functions could be implemented should it be necessary.

From [EIP-223](https://eips.ethereum.org/EIPS/eip-223):

#### Methods {#methods}

ERC-223 token must implement the following methods:

```solidity
function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transfer(address _to, uint256 _value, bytes calldata _data) public returns (bool success)
```

A contract that is supposed to receive ERC-223 tokens must implement the following method:

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

If ERC-223 tokens are sent to a contract that doesn't implement the `tokenReceived(..)` function then the transfer must fail and the tokens must not be moved from the sender's balance.

#### Events {#events}

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

### Examples {#web3py-example}

The API of ERC-223 token is similar to that of ERC-20, so from UI development point of view there is no difference. The only exception here is that ERC-223 tokens may not have `approve` + `transferFrom` functions as these are optional for this standard.

### Examples {#solidity-example}

The purpose of this example is to illustrate how a contract must work with ERC-223 tokens.

Assume that we have a very basic ERC-223 token:

```solidity
pragma solidity ^0.8.19;

abstract contract IERC223Recipient {
function tokenReceived(address _from, uint _value, bytes memory _data) public virtual;
}

contract VeryBasicERC223Token {
event Transfer(address indexed from, address indexed to, uint value, bytes data);

string private _name;
string private _symbol;
uint8 private _decimals;
uint256 private _totalSupply;

mapping(address => uint256) private balances;
function name() public view returns (string memory) { return _name; }
function symbol() public view returns (string memory) {return _symbol; }
function decimals() public view returns (uint8) { return _decimals; }
function totalSupply() public view returns (uint256) { return _totalSupply; }
function balanceOf(address _owner) public view returns (uint256) { return balances[_owner]; }

function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function transfer(address _to, uint _value, bytes calldata _data) public returns (bool success){
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
if(isContract(_to)) {
IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data);
}
emit Transfer(msg.sender, _to, _value, _data);
return true;
}
function transfer(address _to, uint _value) public returns (bool success){
bytes memory _empty = hex"00000000";
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
if(isContract(_to)) {
IERC223Recipient(_to).tokenReceived(msg.sender, _value, _empty);
}
emit Transfer(msg.sender, _to, _value, _empty);
return true;
}
}
```

Now we want another contract to accept deposits of `tokenA` assuming that tokenA is a ERC-223 token. The contract must accept only tokenA and reject any other tokens. When the contract receives tokenA it must emit a `Deposit()` event and increase the value of the internal `deposits` variable.

Here is the code:

```solidity
contract RecipientContract is IERC223Recipient {
event Deposit(address whoSentTheTokens);
uint256 deposits = 0;
address tokenA; // The only token that we want to accept.
function tokenReceived(address _from, uint _value, bytes memory _data) public override
{
// It is important to understand that within this function
// msg.sender is the address of a token that is being received,
// msg.value is always 0 as the token contract does not own or send Ether in most cases,
// _from is the sender of the token transfer,
// _value is the amount of tokens that was deposited.
require(msg.sender == tokenA);
deposits += _value;
emit Deposit(_from);
}
}
```

- **What will happen if we send some tokenB to the contract?** - The transaction will fail and the transfer of tokens will simply not happen. The tokens will be returned to the sender's address.
- **How can we make a deposit to this contract?** - We can simply call the `transfer(address,uint256)` or `transfer(address,uint256,bytes)` function of the ERC-223 token and tell it to transfer some tokens to the address of the `RecipientContract`. That's it.
- **What will happen if we transfer a ERC-20 token to this contract?** - Well, ERC-20 standard supports two methods of transferring tokens: `transfer` function and `approve + transferFrom` pattern. This is not possible to make a deposit with `transferFrom` function as the `RecipientContract` does not have any functions that subsequently call `transferFrom`. If a ERC-20 token is sent with `transfer` function to the address of the `RecipientContract` then unfortunately the tokens will be transferred from the sender's address to the address of the `RecipientContract` but the transfer will not be recognized i.e. `Deposit()` event will not be fired and `deposits` value will not change. There is also no way to filter or prevent unwanted ERC-20 deposits that are made with the `transfer` function.
- **What if we want to execute some function after the token deposit is completed?**

There are multiple ways of doing so. In this example we will follow the method which makes ERC-223 transfers identical to Ether transfers:

```solidity
contract RecipientContract is IERC223Recipient {
event Foo();
event Bar(uint256 someNumber);
address tokenA; // The only token that we want to accept.
function tokenReceived(address _from, uint _value, bytes memory _data) public override
{
require(msg.sender == tokenA);
address(this).call(_data); // Handle incoming transaction and perform a subsequent function call.
}
function foo() public
{
emit Foo();
}
function bar(uint256 _someNumber) public
{
emit Bar(_someNumber);
}
}
```

When the `RecipientContract` will receive a ERC-223 token the contract will execute a function encoded as `_data` parameter of the token transaction, identical to how Ether transactions encode function calls as transaction `data`. Read [the data field](https://ethereum.org/en/developers/docs/transactions/#the-data-field) for more information.

In the above example a ERC-223 token must be transferred to the address of the `RecipientContract` with the `transfer(address,uin256,bytes calldata _data)` function. If the data parameter will be `0xc2985578` (the signature of a `foo()` function) then the function foo() will be invoked after the token deposit is received and the event Foo() will be fired.

Parameters can be encoded in the `data` of the token transfer as well, for example we can call the bar() function with 12345 value for `_someNumber`. In this case the `data` must be `0x0423a13200000000000000000000000000000000000000000000000000000000000004d2` where `0x0423a132` is the signature of the `bar(uint256)` function and `00000000000000000000000000000000000000000000000000000000000004d2` is 12345 as uint256.

## Further reading {#further-reading}

- [EIP-223: ERC-223 Token Standard](https://eips.ethereum.org/EIPS/eip-223)
- [Initial ERC-223 proposal](https://github.com/ethereum/eips/issues/223)