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 token standard #12004

Closed
wants to merge 2 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
1 change: 1 addition & 0 deletions public/content/developers/docs/standards/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ More detailed information on these different types and categories can be found i

- [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-223](/developers/docs/standards/tokens/erc-223/) - A fungible tokens standard that makes tokens behave identical to 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.
Expand Down
193 changes: 193 additions & 0 deletions public/content/developers/docs/standards/tokens/erc-223/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
title: ERC-223 Token Standard
description: An overview of the ERC-223 fungible token standard, how it works, and a comparison to ERC-20.
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 an 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 an ERC-223 compatible token contract. Once deployed, it
will be responsible to keep track of the created tokens on Ethereum.

The contract is not obligated to have only these 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 {#examples}

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.

### Solidity 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 an 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);
}
}
```

## Frequently asked questions {#faq}

### What will happen if we send some tokenB to the contract? {#sending-tokens}

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? {#contract-deposits}

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 an ERC-20 token to this contract? {#erc-20-transfers}

The 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 an 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? {#function-execution}

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 an 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)
1 change: 1 addition & 0 deletions public/content/developers/docs/standards/tokens/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Many Ethereum development standards focus on token interfaces. These standards h
Here are some of the most popular token standards on Ethereum:

- [ERC-20](/developers/docs/standards/tokens/erc-20/) - A standard interface for fungible (interchangeable) tokens, like voting tokens, staking tokens or virtual currencies.
- [ERC-223](/developers/docs/standards/tokens/erc-223/) - A fungible token standard with "transaction handling" model that makes token behave identical to ether.
- [ERC-721](/developers/docs/standards/tokens/erc-721/) - A standard interface for non-fungible tokens, like a deed for artwork or a song.
- [ERC-777](/developers/docs/standards/tokens/erc-777/) - ERC-777 allows people to build extra functionality on top of tokens such as a mixer contract for improved transaction privacy or an emergency recover function to bail you out if you lose your private keys.
- [ERC-1155](/developers/docs/standards/tokens/erc-1155/) - ERC-1155 allows for more efficient trades and bundling of transactions – thus saving costs. This token standard allows for creating both utility tokens (such as $BNB or $BAT) and Non-Fungible Tokens like CryptoPunks.
Expand Down
2 changes: 2 additions & 0 deletions src/data/developer-docs-links.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@
items:
- id: docs-nav-erc-20
to: /developers/docs/standards/tokens/erc-20/
- id: docs-nav-erc-223
to: /developers/docs/standards/tokens/erc-223/
- id: docs-nav-erc-721
to: /developers/docs/standards/tokens/erc-721/
- id: docs-nav-erc-777
Expand Down
1 change: 1 addition & 0 deletions src/intl/en/page-developers-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"docs-nav-development-networks-description": "Local blockchain environments used to test dapps before deployment",
"docs-nav-dot-net": ".NET",
"docs-nav-erc-20": "ERC-20: Fungible Tokens",
"docs-nav-erc-223": "ERC-223",
"docs-nav-erc-721": "ERC-721: NFTs",
"docs-nav-erc-777": "ERC-777",
"docs-nav-erc-1155": "ERC-1155",
Expand Down
3 changes: 2 additions & 1 deletion src/lib/utils/md.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const getPostSlugs = (dir: string, files: string[] = []) => {
// Upgrade (2/2) ✅
"/roadmap/beacon-chain",
"/roadmap/merge",
// Developer docs (0/95)
// Developer docs (0/96)
"/developers/docs/",
"/developers/docs/accounts",
"/developers/docs/apis/backend",
Expand Down Expand Up @@ -136,6 +136,7 @@ const getPostSlugs = (dir: string, files: string[] = []) => {
"/developers/docs/standards",
"/developers/docs/standards/tokens",
"/developers/docs/standards/tokens/erc-20",
"/developers/docs/standards/tokens/erc-223",
"/developers/docs/standards/tokens/erc-721",
"/developers/docs/standards/tokens/erc-777",
"/developers/docs/standards/tokens/erc-1155",
Expand Down
Loading