Ethereum Tokens Explained.

A Beginner’s Guide to Token Standards: Everything You Need to Know

Veronica Coutts
Linum Labs Blog

--

Glossary

  • Native token: The token of the blockchain that your token exists within. E.g: Ether is the native token of the Ethereum blockchain. DAI is a token created within Ethereum but is not the native token. It can often be referred to as a coin.
  • ERC: Ethereum Request for Comment. This is used for any suggestion to improve Ethereum and not only for tokens. Community members comment on these requests, which then either gets accepted or rejected by the general community.
  • EIP: Ethereum Improvement Proposals. Once an ERC has received enough positive feedback it gets formalised into an EIP. It then gets audited and assessed by the core Ethereum developers.
  • The difference between ERCs and EIPs: There are hundreds of standards that address issues beyond the scope of tokens. A full list of formally proposed standards can be found here for EIPs. ERCs are more informal initial proposals vetted by the community. You can take a look at the Ethereum GitHub to learn more about these standards as the Ethereum community is constantly bursting with new and fresh ideas.
  • Fungible: One is equal to the other. For example, one dollar is always equal to another dollar.
  • Non-fungible: One is not equal to another. For example, artworks. If we traded one for another we are not getting equal value. Each non-fungible item is unique.
  • Spender: An address that gets approved by the token owner. Spenders are approved to spend a specified amount of tokens (with fungible tokens) or approved to spend a specific token (in the case of non-fungible tokens) on behalf of the user. I.E: the user may approve the spender once and the spender may spend on the user’s behalf until their allowance is finished.
  • msg.sender: The address that calls the contract’s function.
  • msg.value: The amount of Ether (in wei) sent with the function call.

What is a token?

A token within Ethereum, that is not Ether itself, is stored in a smart contract. This all began when someone wrote a smart contract to manage balances. It is literally a mapping of addresses to numbers storing the balance of each address. Yes, it really is that simple.

mapping(address => uint256) balances;

Tokens are value counters stored in a contract. This is a simple Solidity contract demonstrating the core principles of a token:

A very simple token example. This does not follow ERC20 standards.

In this SimpleBank contract, we can see that when you buy tokens, you send Ether with the call and that Ether pays for the tokens. In our contract above, 1 Ether is worth 100 tokens (line 26). When you sell (or burn), you send the number of tokens you want to burn as a parameter (line 31). As long as you have enough tokens, you will receive the Ether value of those tokens. The require() in the sell (line 33) checks that you cannot withdraw more tokens than you have. Of course, you don’t have to take Ether in return for tokens. You can use other tokens as payment, such as DAI for example. SimpleBank does not implement a token standard. It shows the core principles of a token, which is that they determine your balance and exist inside of a smart contract. This smart contract allows you to buy, sell and transfer tokens.

ERC token standards (like ERC20) are usually just interfaces that your contract (like SimpleBank.sol) must implement if you want your token to be considered as that specific type of token. There are many different standards such as fungible (where one unit is equal to another, like money) and non-fungible (where one unit is not equal to another, like artwork). These standards tell you what your contract needs to implement in order to be considered compliant with the standard. You can have functions that are not included or go above and beyond the standards, however, you must ensure that if you can only communicate with your contract through the standard’s functions that you can still access the functionality. If you are unable to do so, some of your functionality may go unused by wallet users.

Why do we need standards?

In traditional tech, if two companies wished to have their apps talk to each other, they would have to make a custom API layer to interact between the two apps. This would be a completely custom API and would not be reusable. If the app wanted to talk to a third app, another API layer would have to be created. If there was a standard in place, no additional work would have to be put in before the apps could communicate with one another.

There are thousands of tokens, and without token standards, it would be almost impossible to communicate with each token contract. If a wallet such as Metamask wanted to communicate with multiple tokens, it would need to create an interface for each and every token. This is unscalable. We have standards so that a wallet service can implement one standard that then allows the service to interact with thousands of tokens. It also means that your contract can easily trade with other token contracts. Here is an example of an ERC20 token interface taken from OpenZeppelin. ERC20 is a fungible token.

This is the interface for ERC20 tokens.

Interfaces are powerful because we can cast any ERC20 token contract address to the interface and be able to call any of the above-listed functions on that contract without having to ever import the token’s contract.

Below, we are going to change the buy and sell function of our SimpleBank.sol so that we can buy our tokens with an ERC20 token. To do this, you will need to import the ERC20 interface.

Import the ERC20 interface

Our SimpleBank is still not an ERC20 token but it is talking to one when buying and selling our SimpleBank tokens. The ERC20 token we want to use is specified in the constructor, by sending the ERC20 token’s contract address. Below is the updated constructor that takes in the ERC20 address:

The constructor taking in an ERC20 token address

If we send through the contract address of DAI (an ERC20 stablecoin) in our constructor, 1 DAI would be worth 100 tokens. To make our token price a little more reasonable, one of our tokens will now be worth 2 DAI (line 23). We can now send through any ERC20 token’s contract address. Below is the updated buy function:

The buy function now using the IERC20 interface

The buy allows a user to purchase our SimpleBank tokens with a specified ERC20 token, and sell back to the same ERC20 token. Casting the address of the token (line 40) allows us to call any of the functions of the interface. Here, we are calling the transferFrom() function.

The sell function, now selling tokens for an ERC20 token

Notice how we require (line 37–46 (buy), 64–70 (sell)) for the transfer to succeed? We do this because the transfer returns a bool, and we need the transfer to succeed in order to send tokens. Note that before the transfer function can be called, the user first needs to approve() our contract as a spender of their ERC20 tokens, or we will not be able to transfer tokens on their behalf. In an actual dApp, you can convolve these calls.

Making an ERC20 token

Here, we are going to make SimpleBank.sol ERC20 compliant. There are two ways to do this. Either you get the implemented ERC20 token contract from OpenZeppelin, add a mint and a burn function and be done with it, or you can implement all of the functionality yourself. Personally, I would advise against implementing the functionality yourself. OpenZeppelin has battle-tested their contracts, which means that they are less likely to have any vulnerabilities and bugs as hundreds of developers are constantly testing the code. Whereas if you implement the functionality, a single wrong line could cost your users their tokens. The only exception is if you want custom functionality on a call, in which case I would still recommend using OpenZeppelin’s ERC20 and then either overriding (if you inherited it) or editing the single function you need to change.

In the below example, I have inherited the implemented ERC20 from OpenZeppelin and added the public-facing mint and burn functions. SimpleBank.sol is now ERC20 compliant, meaning an exchange could list it simply by knowing its address. If you look at the OpenZeppelin ERC20 contract, you can get a better idea of what each function does. As OpenZeppelin’s comments are quite extensive, repeating them here would be redundant. Below is the ERC20 compliant SimpleBank:

Simple Bank is now an ERC20 compliant token that can be bought with another ERC20 token.

SimpleBank now inherits ERC20 from OpenZeppelin. This means it can interact with the ERC20 interface.

A quick intro to other fungible token standards

ERC20 is not the only fungible token standard, but it was the first, which means that it does have a few bugs. For example, if you send ERC20 tokens to a contract and that contract does not implement any functionality to interact with ERC20 tokens, there is no way to get those tokens back. The tokens are, therefore, “lost”. There is also no way for a contract to revert when it receives ERC20 tokens because there is no way for it to know when it receives those tokens.

For example, if we send DAI to our SimpleBank contract address, there is no way for our SimpleBank to send them back to the sender, or to revert. The only change is in the DAI’s token contract, which doesn’t signal our SimpleBank contract to know that it is being sent tokens. This exact problem has resulted in thousands of tokens being lost.

There are many more token standards than the two listed below, however, these are to give you an idea of what token standards can do.

ERC223

The ERC223 token standard makes tokens act more like Ether by creating receipts. When you are sent an ERC223 token, it receives this receipt, providing information about the transaction such as who sent it and the block.

The ERC223 token standard does not allow tokens to be sent to a contract that does not implement the “receiving hook”. This hook is called when an ERC223 token is sent to it, giving to contract the ability to handle tokens as it needs to. If there is no receiving hook on the contract, the call will revert and the tokens will not be sent to the contract.

To enable a smart contract to receive ERC223 tokens, the contract needs to inherit the ERC223ReceivingContract. This contract is an interface with a single unimplemented function:

tokenFallback(address _from, uint _value, bytes _data);

The contract then needs to implement this function and decide how it is going to handle receiving the token. The rest of the functionality of ERC223 is backwards compatible with ERC20. This means that you can use the ERC20 interface to interact with an ERC223 token, which in itself, is amazing. For a more in-depth look, check out ERC223s goals.

ERC1155

This token standard is mainly used for games. It allows for fungible and non-fungible tokens to be stored in one contract. This is important because a game’s use of tokens will vary. For example, an NFT could be used to represent a valuable unique weapon and a fungible token would be used for in-game currency. Without this standard, a game would have to publish hundreds of different token contracts to represent all in-game items. With ERC1155, a game can deploy one contract to represent all in-game tokenisable items.

The company behind this standard created it to prevent having hundreds of tokens to represent one game. For more information on this standard, there is this article.

Non-fungible tokens

Non-fungible tokens (NFTs) all have a unique uint256 ID. This ID means that you can trace each token’s history and ownership. It also means you can associate metadata with the token, allowing the token to represent anything with unique properties.

ERC721, which is by far the most widely used standard for NFTs, is quite similar to the ERC20 contract interface. It has almost exactly the same functions except when a token amount would be specified in ERC20, the token ID is now specified instead. Below is the ERC721 interface taken from OpenZeppelin.

ERC721 interface

Some use cases for NFTs range from CryptoKitties, a trading game where your kitty’s appearance is determined by its unique NFT ID, to digital art, to representing land ownership and more.

Conclusion

Tokens are value counters in contracts. Standards allow for uniformity between tokens and for wider adoption and use of tokens. There are thousands of token standards, but very few get major adoption. Standards are proposed as ERCs, and once they have received sufficient positive community feedback, they get formalised as an EIP. EIPs are vetted by Ethereum core developers before being implemented.

There are two main groupings of tokens, fungible and non-fungible. Fungible tokens mean that one token is worth any other token, like money, where $1 is always equal to any other $1. Non-fungible tokens all have a unique ID. Each NFT has an inherently different value, such as how one artwork would not be equal to any other artwork. NFTs have a unique value depending on what they are for or represent.

Token standards achieve better interoperability through interfaces. These interfaces allow for exchanges, other tokens and wallets to interact with thousands of tokens through the same interface. Newer standards built for fungible tokens often comply with the ERC20 token interface, as this makes the standard easier to adopt.

If you have any questions, get in touch with us here at Linum Labs, and we will gladly assist you along your blockchain journey.

--

--

Veronica Coutts
Linum Labs Blog

A blockchain believer & Ethereum developer. Trying to spread knowledge, peace and critical thinking.