Making a Simple ERC20 Token
In this blog post, I’ll walk through creating a simple ERC20 token on Ethereum. This was one of my lab assignments this semester, and I thought I better make a blog post about this, because I want to write consistently over here why not?
A token in general is basically simply a digital representation of potentially anything. For example, a token can represent:
reputation points in an online platform
skills of a character in a game
lottery tickets
financial assets like a share in a company
a fiat currency like USD
an ounce of gold
A token can also represent access rights to a blockchain or blockchain app, and tokens can also be used to automate "friction points" in various industries.
Utility tokens, are tokens that have a specific "use" on the blockchain or an app based on that. Utility tokens are also called "app coins" because they are explicitly designed for a certain app or blockchain.
ERC20 Standard
The ERC-20 (Ethereum Request for Comments 20), proposed by Fabian Vogelsteller in November 2015, is a Token Standard that implements an API for tokens within Smart Contracts. The motivation for this EIP was to make "a standard interface allows any tokens on Ethereum to be re-used by other applications: from wallets to decentralized exchanges."
It provides functionalities like transfering tokens from one account to another, to get the current token balance of an account and also the total supply of the token available on the network. Besides these it can allow an owner of some tokens to approve spending those tokens by a third party account.
Methods and Events Defined in ERC20
|
|
The
name
,symbol
,decimals
are state constants that identify the ERC20 token and the number of decimal places used for user representation.The
totalSupply
function returns the total supply of the ERC20 token.The
balanceOf
function returns the balance of the address passed as a argument.The
transfer
function transfers specified number of tokens from the sender to the specified receiver, provided the sender has sufficient balance.The
approve
function is used by the owner of some tokens (specified by the_from
argument) to approve a delegate account to withdraw and spend some allowed number of tokens.The
allowance
function returns the current approved number of tokens by an owner to a specific delegate, as set in the approve function.The
transferFrom
function allows a delegate approved for withdrawal to transfer owner funds, provided the funds specified are less than or equal to the allowance of the delegate, and less than or equal to the owner’s balance.
For our implementation of the ERC20 token, we add some features apart from the standard:
The
burn
function allows the sender to burn some specified number of tokens, provided they have sufficient balance. Successful burning of tokens emits aTransfer
event to address 0x000...000.The
burnFrom
function allows a delegate to burn some specified number of tokens from an account they have sufficient allowance from. Successful burning of tokens emits aTransfer
event to address 0x000...000.The
isMinter
function allows anyone to check if the specified address has minting privileges (or has the minter role).The
addMinter
function allows the creator of the token to add an address as a minter. Successful addition emits aMinterAdded
event.The
removeMinter
function allows the creator of the token to remove an address from the minter role. Successful removal emits aMinterRemoved
event.The
renounce
function allows a minter to renounce their minting privileges. Successful renunciation emits aMinterRemoved
event.The
mint
function allows a minter to mint an arbitrary amount of tokens and take ownership of those tokens. Successful minting emits aTransfer
event with 0x000...000 as the from address.The
_isMinter
mapping maintains a mapping to identify whether an address has minting privileges or not.
Implementation
We make this contract in Solidity, and a simple way to start writing Ethereum contracts in Solidity is to use the Remix IDE It’s browser-based, no setup required.
Token Contract
|
|
Token Vendor Contract
To allow buying and selling tokens (for ether), we create a Vendor contract, that allows buyers and sellers of the tokens to call functions for the same.
The payable
buyTokens
function allows the sender to send some ether in exchange for tokens.The
sellTokens
function allows the sender to sell the tokens for ether. Successful sale of tokens will require that the sender has delegated a minimum of that amount of tokens to the contract account using theapprove
function defined in the token contract, so that the Vendor contract can transfer the specified number of tokens on the sender’s behalf.
|
|
We define arithmetic operations explicitly to secure our ERC20 Token
from integer overflow attacks. We check for integer overflows while
performing these operations. This logic is implemented in a separate
library called SafeMath
.
|
|
Execution
The following steps show how the contracts can be deployed, following a demonstration of the different functionalities coded in the contracts. We run a local Geth client running a private blockchain, and use the Remix IDE to call the contracts.
First, we initialize a local test ethereum network using the genesis block given below, and start the node, allowing RPC CORS from Remix. Attach a console to the running geth client via IPC. Create three accounts, and start mining with one so that we can deploy a contract using that account.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
{ "config": { "chainId": 2021, "homesteadBlock": 0, "eip150Block": 0, "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "istanbulBlock": 0 }, "nonce": "0x0000000000000042", "timestamp": "0x00", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "extraData": "0x00", "gasLimit": "0x4c4b40", "difficulty": "0x0004", "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "alloc": {} }
1 2 3
$ geth --datadir .ethereum/net init genesis.json $ geth --rpc --rpccorsdomain "https://remix.ethereum.org" --datadir .ethereum/net --allow-insecure-unlock $ geth attach ipc:<DATADIR>/geth.ipc
1 2
> personal.newAccount() // to create an account > miner.start() // to start mining
In Remix IDE, switch to the Deploy & run transactions window. Select Web3 Provider as the Environment. When the Remix IDE connects to the Geth client, the three addresses should be visible in the Account input.
We can now deploy the contract by selecting the contract, providing the
total
argument. We select 1000 tokens as the initial supply. Unlock an account which has some ether using the geth console by runningpersonal. unlockAccount(eth.accounts[0])
. Click on Deploy. After we deploy the contract, the contract will appear under Deployed Contracts, with functions available to use.Try running the different view functions first, and check whether the output is as expected. For example, the
balanceOf
function with the contract creator’s address should return 1000 tokens initially. Thename
,symbol
,decimals
,creator
andisMinter
functions should similarly return expected values.If we try minting from the second account, an error will be returned, since the second account does not have minting privileges.
Now we can try adding the second account as a minter using the
addMinter
method from the creator’s account.If we now call the
isMinter
method from the second account, we’ll get true as the return value.We can try minting some tokens from the second account by (first changing the account in Remix) calling
mint
method.We can transfer tokens from second account to third account by calling the
transfer
method from the second account.Let’s approve the third account for delegation of 50 tokens by calling the
approve
method from the second account. We can then check the allowance by calling theallowance
method.
Let’s transfer 20 tokens belonging to the second address to the first address from by calling
transferFrom
method from the third account.Let’s burn 200 tokens from creator’s account.
Let’s burn the remaining 30 tokens from the allowance delegated from second address to the third address.
Now let’s deploy the Token Vendor contract in the same manner as we deployed the Token contract. We will create the vendor contract from the creator address again, and then send some ether to the contract (using Metamask, any other wallet or even the geth console) for its funds, so that it can buy tokens from some address who sells tokens to it for ether. Select the token vendor contract and deploy it, with the address of the token contract as a parameter to the constructor.
If we now try selling some tokens to the contract, we will get an error, since we first have to delegate that amount of tokens to the Vendor contract to allow it to transfer those tokens to itself. Hence, first approve some amount of tokens to the vendor contract, and then call the
sellTokens
function with some tokens less than or equal to the allowance.After approving the vendor contract, we can successfully sell tokens. The image below shows the second account selling 100 tokens, for which it gets back 1ETH, since the exchange rate (defined as a constant) is 100 tokens per ether.
For buying tokens, we simply need to call the
buyTokens
function and send some ether along with it. According to the token exchange rate, some number of tokens will be bought.We can also use a wallet like Metamask to deal with ERC20 tokens. Click on Add Token in Metamask. Fill in the token address, and Metamask will retrieve the symbol and decimals of the token. Click on Next and then Add Token.
Conclusion
We saw how an Ethereum smart contract can be used to implement a fungible token using the ERC20 standard, along with some extensions to allow burning and minting tokens. Then, we developed a Vendor contract to allow users to buy and sell tokens. Remix IDE was used to develop and test the contracts, Geth client was used to simulate a test blockchain locally, and Remix and Metamask was used to deal with tokens.
ERC20 is somewhat limited by its simplicity, even though we added on the minting and burning functions. A newer standard for fungible tokens, ERC777 (EIP) has a lot more functionality, and is backward compatible with ERC20. For non-fungible tokens, there’s the ERC721 standard, often used for collectibles and games.