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