Developing Smart Contracts: ERC20 Token

Seungwon Go
Dec 17, 2018 · 13 min read

By Seungwon Go, CEO & Founder at ReturnValues (seungwon.go@returnvalues.com)

Truffle Framework(https://truffleframework.com/) is a framework, which enables you to develop, test and build Smart Contract in Solidity fast and easily.

Image for post
Image for post

One of the best things about using Truffle is that you can write and run test cases for various scenarios in your local environment without deploying to Testnet or Mainnet. Had it not for a framework like Truffle, in order to test the smart contract you developed, you would have to develop a DApp and run every test case through Metamask. It would take a lot of effort and you would need to do it continuously and repeatedly, until you finish with the development.

Now, let’s configure the development environment.

Configuring the Environment for Developing Smart Contracts

Now, open up the terminal and enter the command below to install Truffle.

npm install -g truffle

Then, create a project folder for developing a new smart contract. You can create one where you want. I’ve created one with the name truffle-demo.

Move to the folder you created and run the command below in the terminal:

truffle init
Image for post
Image for post

After running the command above, new folders and files will be automatically generated inside the project folder.

Image for post
Image for post

Here are short explanations on the folders and files generated by the command truffle init.

  • contracts(folder) : here, smart contract files developed in solidity are stored in this folder.
  • Migrations.sol(file) : this file is solidity file, which is for deploying smart contract program. You should not delete this file.
  • migrations(folder) : here will be script files that are for deploying. Everytime you develop a smart contract program, you will make a corresponding migration file in this folder and the script files are ordered.
  • 1_initial_migration.js(file) : it is a script file for deploying Migrations.sol.
  • test(folder) : it is a folder to test smart contract programs developed. In this folder, you write test cases and run them with the command ‘truffle test’.
  • truffle.js(file) : it is a configuration file for Truffle. Here You can set information such as host, port, networkID etc. for development.
  • truffle-config.js(file) : it is also a configuration file for Truffle. This is used as config file in case truffle.js file doesn’t exist.

Before developing ERC20 token, let me briefly explain what ERC20 is.

In Ethereum, when a programmer wants to propose a standard, he/she submits EIP (Ethereum Improvement Proposal). When it gets approved, it becomes ERC(Ethereum Request for Comments). And the number 20 is a number to identify the proposal document.

There are also other ERCs for creating tokens, other than ERC20. But most of the tokens you’re familiar with such as EOS, Filecoin, Bancor, Qash, Bankes are based on ERC20. In case of EOS, though, is no more on Ethererum but on its own blockchain platform.

Most of the tokens that are being used for recent ICOs are based on ERC20. So we can say ERC20 functions as a standard for creating a token.

Now, let’s get started with developing a ERC20-based token smart contract. Fortunately, openzeppelin, an opensource project, provides a basic smart contract for creating tokens and crowdsale. And you can develope a token with the features you want very easily.

First, type the command below to install openzeppelin:

npm install openzeppelin-solidity --save

You can see the openzeppelin-solidity folder was generated under the node_modules folder.

Image for post
Image for post

Developing Smart Contracts: ERC20 Token

And copy the code below and past it into that file.

pragma solidity ^0.4.24;contract SampleToken {}

pragam solidity ^0.4.24; in the first line indicates the version of solidity compiler. Since solidity is being continuously upgraded and each version provides different codes, so you need to specify which version you use for developing so that your program will be compiled with the solidity of the version you specified when it’ll be run.

We’ve already installed openzeppelin. We’ll inherit from one of the solidity programs that are already developed in the openzeppelin-solidity / contracts / token / ERC20 folder.

Now, let’s have a look at the solidity program we’ll inherit from inside the ERC20 folder.

Open DetailedERC20.sol file in your editor.

Image for post
Image for post
ERC20Detailed

ERC20Detailed.sol inherits from IERC20 and you can declare token name, token symbol, token decimals for this smart contract. This means that you can make a new token with the name, symbol and decimals you want.

Here, you need to clearly understand what the variable ‘decimals’ means. decimals refers to how divisible a token can be, from 0 to 18 and higher. The decimals value is the number of digits that come after the decimal place when displaying token values on-screen. Ethereum does not deal with decimal numbers, so you need to represent all numeric values as integers. In order to understand the total supply, you need to have a clear understanding about the decimals.

For example, if you’ve decleared total supply of the token as 100,000 and set the decimals as 3, the real token amount will be 100.000 which is 100, not 100,000.

Now let’s inherit from ERC20Detailed contract, and define a new token you want to generate.

pragma solidity ^0.4.24;import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";contract SampleToken is ERC20Detailed {constructor(string name, string symbol, uint8 decimals)ERC20Detailed(name, symbol, decimals)public {}}

When our new token contract is generated, you’ll deploy it to Ethereum testnet or mainnet and you’ll declare variables as parameters for generating a contract.

Image for post
Image for post
You declare variables that are need for generating tokens when creating contracts.

In case of ERC20.sol that you inherited from, only interface was declared. In order to complete, you need to inherit from another contract.

Open ERC20.sol in the openzeppelin-solidity / contracts / token / ERC20 folder, you’ll find the code that implements the interface declared in ERC20.

Now let’s change our code so that it inherits from ERC20.sol.

Image for post
Image for post
컨트랙트 생성시 토큰 생성을 위해 필요한 변수를 선업합니다

In case of ERC20.sol that you inherited from, only interface was declared. In order to complete, you need to inherit from another contract.

Open ERC20.sol in the openzeppelin-solidity / contracts / token / ERC20 folder, you’ll find the code that implements the interface declared in ERC20.

Now let’s change our code so that it inherits from ERC20.sol.

pragma solidity ^0.4.24;import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";import "openzeppelin-solidity/contracts/ownership/Ownable.sol";contract SampleToken is ERC20Detailed, ERC20, Ownable {constructor(string name, string symbol, uint8 decimals, uint256 totalSupply)ERC20Detailed(name, symbol, decimals)public {_mint(owner(), totalSupply * 10**uint(decimals));}}

Now you have created a token with basic features by inheriting from ERC20Detailed and ERC20. You’ll add several features from now on, but before that, let’s test the developed program and deploy it.

Compiling Smart Contracts

Run the command below in the terminal:

truffle compile

If there’s no error in the program, you’ll get the messages as follows:

Image for post
Image for post

In the compiling process, all the files that are imported in the program, get found and compiled. When it’s done with compiling, the build folder will be generated in the project folder and json files that are correspondent with the compiled Solidity files will be generated in the build folder as shown below:

Image for post
Image for post

Generating Migration Files

var SampleToken = artifacts.require("./SampleToken.sol");const _name = "TestToken";
const _symbol = "TTK";
const _decimals = 18;
const _total_supply = 1000000;
module.exports = function(deployer) {deployer.deploy(SampleToken, _name, _symbol, _decimals, _total_supply);};

Writing Test Cases

Fortunately open-zeppelin provides various features that are needed when writing test cases and we’re going to use them. In the link below, down the helpers folder and save it in your project folder.

Now, create a new file named SampleToken.test.js in the test folder, and copy the code below and past it into that file.

const { expectThrow } = require("../helpers/expectThrow");const { EVMRevert } = require("../helpers/EVMRevert");const SampleToken = artifacts.require("SampleToken");const BigNumber = web3.BigNumber;require("chai").use(require("chai-bignumber")(BigNumber)).should();contract("SampleToken", function([_, owner, investor]) {let token;const _name = "SampleToken";const _symbol = "STK";const _decimals = 18;const _total_supply = new BigNumber(1000000);const _over_total_supply = new BigNumber(1100000000000000000000000);beforeEach(async function() {token = await SampleToken.new(_name, _symbol, _decimals, _total_supply, {from: owner});});it("has a name", async function() {(await token.name()).should.eq(_name);});it("has a symbol", async function() {(await token.symbol()).should.eq(_symbol);});it("has 18 decimals", async function() {(await token.decimals()).should.be.bignumber.equal(_decimals);});it("has " + String(1000000000000000000000000) + " total supply",async function() {(await token.totalSupply()).should.be.bignumber.equal(1000000000000000000000000);});it("assigns the initial total supply to the creator", async function() {const totalSupply = await token.totalSupply();const ownerBalance = await token.balanceOf(owner);ownerBalance.should.be.bignumber.equal(totalSupply);});it("transfer token to the investor", async function() {await token.transfer(investor, 1000, { from: owner });const investorBalance = await token.balanceOf(investor);investorBalance.should.be.bignumber.equal(1000);});it("transfer token to the investor", async function() {await token.transfer(investor, 1000, { from: owner });const investorBalance = await token.balanceOf(investor);investorBalance.should.be.bignumber.equal(1000);});it("should reject transfer token(more than has) to the investor", async function() {await expectThrow(token.transfer(investor, _over_total_supply, { from: owner }),EVMRevert);});});

The test code above is meant to test 8 cases:

1. to check if the token name is the same as the name set in the variable.

2. to check if the token symbol is the same as the symbol set in the variable.

3. to check if the decimals is the same as the decimal set in the variable.

4. to check if the total supply is the same as the total amount set in the variable.

5. to check if all the tokens issued were transferred to the address of the token creator.

6. to check if the token owner can transfer as much as he/she wants to a specific address.

7. to check if the revert feature works properly when the token owner tries to transfer more tokens than he/she has left.

Configuring the Environment for Testing and Deploying

module.exports = {networks: {development: {host: "localhost",port: 7545,network_id: "*"}}};

Then, in order to use an account for testing in your local environment, install Ganache. You can download it from https://truffleframework.com/ganache.

After running Ganache,click the settings icon on the top right, which will move you to the settings. Set the port number 7545, as set in the truffle.js.

Now, we’re ready for testing. In the terminal, write the command below, which will run the smart contract you wrote previously.

truffle test ./test/SampleToken.test.js

You’ll get an error message as below in the terminal:

Image for post
Image for post

In order to test, you need web3 module. Other than that, you need several other modules in order to use several functions in the helpers folder that you copied and pasted to write test cases.

You can install all the needed modules by running the commands below:

npm i web3 --save
npm i chai --save
npm i chai-bignumber --save

Now, let’s run the test again.

truffle test ./test/SampleToken.test.js

The test goes through the written test cases, and the test results will show in the terminal as below:

Image for post
Image for post

Now, you can see all the 8 test cases were run successfully.

Deploying to Testnet

require("dotenv").config();const HDWalletProvider = require("truffle-hdwallet-provider-privkey");const privateKeys = [process.env.PRIVATEKEY];module.exports = {networks: {development: {host: "localhost",port: 7545,network_id: "*" // eslint-disable-line camelcase},ropsten: {provider: () =>new HDWalletProvider(privateKeys,"https://ropsten.infura.io/" + process.env.INFURA_API_KEY),network_id: 3,gas: 4700000}}};

In order to use dotenv and truffle-hdwallet-provider-privkey, you need to install the modules below in the given order. The commands are:

npm i dotenv --save
npm i truffle-hdwallet-provider-privkey --save

dotenv is a module with which enables you to save key values and load them while running the program.

Before deploying smart contracts to testnet, you need to create an account for the contract owner at https://www.myetherwallet.com. Also you need to create INFURA API KEY at https://infura.io with the account you’ve created.

On this topic, you’ll find many articles in the Internet.

Now, create an .env file in your project root folder. Open it and add the two lines below and write down the PRIVATEKEY and INFURA_API_KEY of the account you’ve created

PRIVATEKEY=INFURA_API_KEY=

Then, let’s deploy the contract to testnet.

I chose ropsten as the testnet to deploy to, and the command to deploy is as below:

truffle migrate --reset --network ropsten

If the contract has been successfully deployed, you’ll get a message as below:

Image for post
Image for post

Now, go to the website https://ropsten.etherscan.io/ and search with token contract address. You’ll see that the token was created successfully.

Image for post
Image for post

Verifying and Publishing Smart Contract

In order to verify the deployed smart contract, you need all the code of the modules that were imported in your program.

The truffle-flattener module takes all the files and their dependencies and puts them into one new file.

Use the command below to install truffle-flattener.

npm i truffle-flattener --save

When it’s done with the installation, let’s make a file that includes all the code that are being referenced using truffle-flattener.

First, create a folder named flat in your project root.

Run the command below in the terminal:

truffle-flattener ./contracts/SampleToken.sol > ./flat/SampleToken_flat.sol

When you run the command, it takes all the contract code, from not only the contracts that are imported but also what the imported contract files are referencing, and concatenate them and put them into the SampleToken_flat.sol file in flat folder.

Image for post
Image for post

Now, open SampleToken_flat.sol file, copy all the code in it, and paste them into the editor in the Code/Verify and Publish tap of the deployed SampleToken contract in https://ropsten.etherscan.io.

Image for post
Image for post

Choose SampleToken for Contract Name, and choose Compiler of your chosen version. Choose No for Optimization and click Verify and Publish button.

If the contract gets verified successfully, you’ll see the screen as follows:

Image for post
Image for post

If you click ‘Successfully generated ByteCode and ABI for Contract Address’ button, you’ll see the SampleToken that you deployed. Unlike before you verified the code, in the Code tap, you’ll see all the contract code that you generated using truffle-flattener. Also the tabs such as Read Contract and Write Contract will be activated.

Image for post
Image for post

If you click Read Contract tap, you’ll see the token generation information as follows:

Image for post
Image for post

If you click Write Contract tap, you can directly execute functions that are provided by the token contract that you developed.

Image for post
Image for post

Hoorey! you have successfully implemented ERC20 based token and deployed it to testnet. The steps to deploy to mainnet is the same.

Open the truffle.js file and set the information as below and choose mainnet when you type command migrate. Then the contract will be deployed to mainnet.

require("dotenv").config();const HDWalletProvider = require("truffle-hdwallet-provider-privkey");const privateKeys = [process.env.PRIVATEKEY];module.exports = {networks: {development: {host: "localhost",port: 7545,network_id: "*"},ropsten: {provider: () =>new HDWalletProvider(privateKeys,"https://ropsten.infura.io/" + process.env.INFURA_API_KEY),network_id: 3,gas: 4700000},main: {provider: () =>new HDWalletProvider(privateKeys,"https://mainnet.infura.io/" + process.env.INFURA_API_KEY),network_id: 1,gas: 4700000}}};

Extending ERC20 Token Features

•MintableToken: this token type means that you can mint token after your initial token generation.

•PausableToken: If your token is PausableToken, you can choose to Pause in a certain circumstance in order to pause features such as transfer and approval.

•BurnableToken: If your token is BurnableToken, you can irreversibly destroy some amount of tokens.

pragma solidity ^0.4.24;import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol";import "openzeppelin-solidity/contracts/token/ERC20/ERC20Pausable.sol";import "openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol";import "openzeppelin-solidity/contracts/ownership/Ownable.sol";contract SampleToken is ERC20Detailed, ERC20, ERC20Mintable, ERC20Pausable, ERC20Burnable, Ownable {constructor(string name, string symbol, uint8 decimals, uint256 totalSupply)ERC20Detailed(name, symbol, decimals)public {_mint(owner(), totalSupply * 10**uint(decimals));}}

Now, try to deploy smart contract that include additional features, by going through the steps to test and deploy.

So far, we have easily generated our own token based on ERC20 using open source project openzeppelin. The token we generated is a contract with the most basic features. In order to develop more advanced token contract, I recommend you to analyze the contract code in openzeppelin.

ReturnValues

ReturnValues Blogs

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store