Create an ERC20 using buidler, without writing Solidity

In this tutorial we will use a Preset ERC20 contract in OpenZeppelin Contracts v3.x to create an ERC20 using buidler, without having to write any Solidity code .

Setting up the Environment

We begin by creating a new project.

$ mkdir mytoken && cd mytoken
$ npm init -y

Then we install OpenZeppelin Contracts

$ npm i --save-dev @openzeppelin/contracts

Next we install a development tool for deployment, for this tutorial we will use buidler but we could use any other tools such as Truffle or OpenZeppelin CLI.

$ npm install --save-dev @nomiclabs/buidler

We will use ethers to interact with our contract so we need to install that and the buidler-ethers plugin too.

$ npm install --save-dev @nomiclabs/buidler-waffle ethereum-waffle chai @nomiclabs/buidler-ethers ethers

We will setup our Solidity project using buidler to create our project by running npx buidler.
We will use the sample project and add a .gitignore to ensure we only commit what is required and .gitattributes to highlight Solidity code in GitHub. Select create a sample project, and yes to add a .gitignore and .gitattributes.

$ npx buidler
888               d8b      888 888
888               Y8P      888 888
888                        888 888
88888b.  888  888 888  .d88888 888  .d88b.  888d888
888 "88b 888  888 888 d88" 888 888 d8P  Y8b 888P"
888  888 888  888 888 888  888 888 88888888 888
888 d88P Y88b 888 888 Y88b 888 888 Y8b.     888
88888P"   "Y88888 888  "Y88888 888  "Y8888  888

Welcome to Buidler v1.3.8‍

✔ What do you want to do? · Create a sample project
✔ Buidler project root: · /home/abcoathup/projects/forum/mytoken
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to add a .gitattributes to enable Soldity highlighting on GitHub? (Y/n) · y

Project created

Try running some of the following tasks:
  npx buidler accounts
  npx buidler compile
  npx buidler test
  npx buidler node
  node scripts/sample-script.js
  npx buidler help

Getting the contract artifacts

We are going to use Preset ERC20PresetMinterPauser which is an ERC20 that is preset so it can be minted, paused and burned.

The Preset contracts have already been compiled, so we only need to copy the artifacts to the artifacts directory.

$ mkdir artifacts
$ cp node_modules/@openzeppelin/contracts/build/contracts/* artifacts

Get accounts

$ npx buidler accounts
0xc783df8a850f42e7F7e57013759C285caa701eB6
0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4
0xE5904695748fe4A84b40b3fc79De2277660BD1D3
0x92561F28Ec438Ee9831D00D1D59fbDC981b762b2
...

Deploy the contract

We will use npx buidler console to open a buidler console with a development blockchain

$ npx buidler console
Compiling...
Compiled 2 contracts successfully
>

We can deploy our new token to our development blockchain. Providing a name and a symbol as parameters to the constructor to create a new ERC20PresetMinterPauser.

> const Token = await ethers.getContractFactory("ERC20PresetMinterPauser");
undefined
> const token = await Token.deploy("My Token","MYT")
undefined

Interact with our Token

Token metadata

We can call the contract to read token metadata such as name, symbol and decimals

> await token.name()
'My Token'
> await token.symbol()
'MYT'
> await token.decimals()
18

Mint

We can send a transaction to mint tokens to a given account, from an account with the minter role.
In our case we are minting from the account which deployed the token, which is given the minter role.

We will mint 1000 tokens (the token has 18 decimals).

> await token.decimals()
18
> await token.mint("0xc783df8a850f42e7F7e57013759C285caa701eB6","1000000000000000000000")
{ hash:
   '0x7bc0ebd4387e34206f84bdce3281fa5d058266fa9f6b53c5ba50ecf14f4e3257',
  blockHash:
   '0xe39655aefed33d596d38ccfadcc7e1bd0eff10f1bf19bafc75ba2f60dbc2f574',
  blockNumber: 2,
  transactionIndex: 0,
  confirmations: 1,
  from: '0xc783df8a850f42e7F7e57013759C285caa701eB6',
  gasPrice: BigNumber { _hex: '0x01dcd65000', _isBigNumber: true },
  gasLimit: BigNumber { _hex: '0x90f560', _isBigNumber: true },
  to: '0x7c2C195CD6D34B8F845992d380aADB2730bB9C6F',
...

We can check the balance

> (await token.balanceOf("0xc783df8a850f42e7F7e57013759C285caa701eB6")).toString()
'1000000000000000000000'

Burn

We can send a transaction to burn tokens (from the account holder)

> await token.burn("10000000000000000000")
{ hash:
   '0x1dd320cbc1752aa655cb96e4c05823ada4c4023b575081a7c0e80a5157440f94',
  blockHash:
   '0x2ce0c3a2421dc0bb47ead6c0853321d2148ae1c73b3badf48a9e41af789dde39',
  blockNumber: 3,
  transactionIndex: 0,
  confirmations: 1,
  from: '0xc783df8a850f42e7F7e57013759C285caa701eB6',
  gasPrice: BigNumber { _hex: '0x01dcd65000', _isBigNumber: true },
  gasLimit: BigNumber { _hex: '0x90f560', _isBigNumber: true },
  to: '0x7c2C195CD6D34B8F845992d380aADB2730bB9C6F',
...

We can check the balance after burning

> (await token.balanceOf("0xc783df8a850f42e7F7e57013759C285caa701eB6")).toString()
'990000000000000000000'

Pause

We can pause our Token by sending a transaction calling the pause function, from an account with the pauser role.
In our case we are pausing from the account which deployed the token, which is given the pauser role.

> await token.pause()
{ hash:
   '0x353318980049dbdf3fcc7e572a823f992d643efb0f9f2bb33cd047ca6b8f79cc',
  blockHash:
   '0xb14ae32ea0559b1de09efea3964797375382fbfaaa411e3d21b80482f2b68f07',
  blockNumber: 4,
  transactionIndex: 0,
  confirmations: 1,
  from: '0xc783df8a850f42e7F7e57013759C285caa701eB6',
  gasPrice: BigNumber { _hex: '0x01dcd65000', _isBigNumber: true },
  gasLimit: BigNumber { _hex: '0x90f560', _isBigNumber: true },
  to: '0x7c2C195CD6D34B8F845992d380aADB2730bB9C6F',
...

Transactions which attempt to transfer (such as mint) will revert whilst paused.

> await token.mint("0xc783df8a850f42e7F7e57013759C285caa701eB6","1000000000000000000000")
Thrown:
Error: VM Exception while processing transaction: revert ERC20Pausable: token transfer while paused
    at <UnrecognizedContract>.<unknown> (0x7c2c195cd6d34b8f845992d380aadb2730bb9c6f)
    at process._tickCallback (internal/process/next_tick.js:68:7)

Next steps

  • We could write a script to deploy our contract
  • We could deploy to a public test network
  • We could extend ERC20PresetMinterPauser
  • If we write contract code we should write automated tests