Create an ERC20 without writing Solidity, using OpenZeppelin CLI

In this tutorial we will use Presets in OpenZeppelin Contracts 3 to create an ERC20 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 OpenZeppelin CLI but we could use any other tools such as Truffle

$ npm i @openzeppelin/cli

Getting the contract artifacts

We will setup our Solidity project using oz init to create a contracts directory and configuration to connect to a network.

$ npx oz init

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 build/contracts directory.

$ mkdir -p build/contracts/
$ cp node_modules/@openzeppelin/contracts/build/contracts/* build/contracts/

Deploy the contract

In a separate terminal run a local blockchain (ganache-cli)

$ ganache-cli -d

Now we can deploy our new token to our local blockchain. Choose regular (rather than upgradeable) deployment, ERC20PresetMinterPauser and specify a name and a symbol for our new token.

$ npx oz deploy
No contracts found to compile.
? Choose the kind of deployment regular
? Pick a network development
? Pick a contract to deploy ERC20PresetMinterPauser
? name: string: My Token
? symbol: string: MYT
✓ Deployed instance of ERC20PresetMinterPauser
0x0290FB167208Af455bB137780163b7B7a9a10C16

Interact with our Token

Display the accounts that we can use

$ npx oz accounts
? Pick a network development
Accounts for dev-1587447341873:
Default: 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
All:
- 0: 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
- 1: 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
- 2: 0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b
- 3: 0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d
...

Token metadata

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

$ npx oz call
? Pick a network development
? Pick an instance ERC20PresetMinterPauser at 0x0290FB167208Af455bB137780163b7B7a9a10C16
? Select which function name()
✓ Method 'name()' returned: My Token
My Token
$ npx oz call
? Pick a network development
? Pick an instance ERC20PresetMinterPauser at 0x0290FB167208Af455bB137780163b7B7a9a10C16
? Select which function symbol()
✓ Method 'symbol()' returned: MYT
MYT
$ npx oz call
? Pick a network development
? Pick an instance ERC20PresetMinterPauser at 0x0290FB167208Af455bB137780163b7B7a9a10C16
? Select which function decimals()
✓ Method 'decimals()' returned: 18
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).

$ npx oz send-tx
? Pick a network development
? Pick an instance ERC20PresetMinterPauser at 0x0290FB167208Af455bB137780163b7B7a9a10C16
? Select which function mint(to: address, amount: uint256)
? to: address: 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
? amount: uint256: 1000000000000000000000
✓ Transaction successful. Transaction hash: 0x5aeb49aa62b3376b4b3e6daed9262aa2a70f2e4335066a3ee06ece3b98784d2d
Events emitted:
 - Transfer(0x0000000000000000000000000000000000000000, 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0, 1000000000000000000000)

We can check the balance

$ npx oz call
? Pick a network development
? Pick an instance ERC20PresetMinterPauser at 0x0290FB167208Af455bB137780163b7B7a9a10C16
? Select which function balanceOf(account: address)
? account: address: 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
✓ Method 'balanceOf(address)' returned: 1000000000000000000000
1000000000000000000000

Burn

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

$ npx oz send-tx --from 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
? Pick a network development
? Pick an instance ERC20PresetMinterPauser at 0x0290FB167208Af455bB137780163b7B7a9a10C16
? Select which function burn(amount: uint256)
? amount: uint256: 10000000000000000000
✓ Transaction successful. Transaction hash: 0x3c53d1dc01864126a2644b0c1f22ba8e9badf5d6ed63e24b471d744e3ff61472
Events emitted:
 - Transfer(0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0, 0x0000000000000000000000000000000000000000, 10000000000000000000)

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.

$ npx oz send-tx
? Pick a network development
? Pick an instance ERC20PresetMinterPauser at 0x0290FB167208Af455bB137780163b7B7a9a10C16
? Select which function pause()
✓ Transaction successful. Transaction hash: 0xf7fc4fa52c39825616a1c0f1e4e9ef6f908b070840bee9325ff2153b3dad9ef7
Events emitted:
 - Paused(0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1)

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

$ npx oz send-tx
? Pick a network development
? Pick an instance ERC20PresetMinterPauser at 0x0290FB167208Af455bB137780163b7B7a9a10C16
? Select which function mint(to: address, amount: uint256)
? to: address: 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
? amount: uint256: 1000000000000000000000
✖ Calling: 'mint' with:
- to (address): "0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0"
- amount (uint256): "1000000000000000000000"
Error while trying to send transaction to 0x0290FB167208Af455bB137780163b7B7a9a10C16. Error: Returned error: VM Exception while processing transaction: revert ERC20Pausable: token transfer while paused

Next steps

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

1 Like