- Summary
- Project Structure
- Local Node Setup
- ERC20 Contract
- Programmatic Deployment
- Client Interactions
- Running Tests
- Future Improvements
This project aims to demonstrate how to deploy an ERC20 contract to a local node using only go-ethereum by generating Go bindings using abigen. Additionally, it provides simple client functionality that can query ERC20 balances and transfer ERC20 tokens between accounts. Unit tests are provided to verify individual function correctness while a Behaviour Driven Development (BDD) approach to tests are provided using ginko and gomega to verify the behaviour of the implementation and establish an easy to maintain and extend test suite. To demonstrate the functionality of the project, we will assume a scenario where two friends Vlad and Nick are interacting with a token called Vladcoin.
├── contracts // ERC20 contract, Golang bindings and compiled contract artifacts
│ ├── VladToken.go // You will generate this file using abigen
│ ├── VladToken.sol
│ └── build // You will generate this folder after compiling the contract
├── deployer // Deployer script to deploy the contract on a local node and BDD tests
│ ├── deployer.go
│ └── deployer_bdd_test.go
├── erc20-client // Erc20 client scripts to query and transfer ERC20 tokens and BDD and unit tests
│ ├── erc20_client.go
│ ├── erc20_client_bdd_test.go
│ └── erc20_client_test.go
├── utils // Utility scripts for ethereum client connectin, tx signer generation and unit tests
│ ├── utils.go
│ └── utils_test.go
├── node // Files generated after running local node - genesis file, private key and node configs
│ ├── config
│ ├── data
│ └── keyring-test
├── init.sh // Script used to start a local node and create user keys
├── keys_util.sh // Script used to easily view user Hex addresses and Private Keys
├── main.go // Main entry point for the application where all the commands are
├── go.mod
├── package.json
├── package-lock.json
├── README.md
Make sure you follow the instructions in the official Evmos documentation to install Go and install the latest version of Evmos
A convenience script has been prepared called init.sh
which will initialize a local node and generate user keys.
In our case these keys are vlad_validator
who is the default validator at genesis and nick_validator
who is our guest user.
Feel free to modify these keys if you want to use different names for the keys.
Additionally, you should replace the HOME_DIR
variable
in the script with your own local directory where you want your node
folder to be created. In that folder you will
find all the necessary files for your local node and genesis state.
Once you have successfully started your local node you will be able to see blocks being produced.
Make sure to keep the terminal window open.
For the ERC20 contract we will use an already established and trusted implementation from OpenZeppelin.
The token contract can be found under the contracts
folder named VladToken.sol
.
You can install OpenZepelin contracts directly with NPM using npm install @openzeppelin/contracts
.
The contract defines a simple ERC20 contract named Vladcoin with symbol VLAD
and initial supply of 100,000,000.00
which will be minted to the contract initiator or in our case vlad_validator
The first step is to install the Solidity compiler (solc).
We also need to install a tool called abigen
for generating the ABI from a Solidity smart contract.
Assuming you have Go all set up on your computer, simply run the following to install the abigen tool
go get -u github.com/ethereum/go-ethereum
cd $GOPATH/src/github.com/ethereum/go-ethereum/
make
make devtools
After this you can compile your contract and generate Go bindings using the following commands
solc --abi --bin VladToken.sol -o build --base-path 'path_to_node_modules'
This will create a build
folder with the generated ABI
and bin
files. Make sure to replace
path_to_node_modules
with the path to your node_modules
folder so that the compiler knows where to look for
OpenZeppelin contracts that are being imported in our ERC20 contract.
Next we run the following command:
abigen --bin=contracts/build/VladToken.bin --abi=contracts/build/VladToken.abi --out=VladToken.go
This will compile the necessary Go file which will include the deployment methods which we will
later user to in our implementation. Now you should have a VladToken.go
file in the contracts
folder.
In order to deploy an ERC20 contract a deployer account is needed. We can get our default vlad_validator
account's
Private Key using a convenient command in the Evmos daemon.
evmosd keys unsafe-export-eth-key vlad_validator --keyring-backend test
The response might look something like this:
5CE50C38D30F7C95EE50ACC842ACE8D1D7C1397B991259F0AF7B0780B079F958
This is the private key of the vlad_validator
account which we will use to sign transactions as the default
contract deployer. Make sure to note down
your private key and keep it safe as we will need it later.
If you ever lose or forget your private key there is a utility script keys_utils.sh
which will show you
both private keys and hex addresses
A small CLI utility has been provided to deploy the ERC20 contract using the private key.
In a new terminal window run the following command making sure you replace deployer-private-key
with your own.
go run main.go deployContract deployer-private-key
Or in our case:
go run main.go deployContract 5CE50C38D30F7C95EE50ACC842ACE8D1D7C1397B991259F0AF7B0780B079F958
You should now see the output in your terminal if there were no errors. Make sure you record the contract address as we will be using it in the next section Client Interactions to interact with the contract.
The ERC20 contract address: 0x22B17eAB16deEC032c41f31e8Aadfd62BE9F2863
Transaction hash: 0x33ac13ecc05a3922ebaf5d560e141a5b1e196954e9002790512f8410c50e601a
Now that we have successfully deployed our ERC20 smart contract we can start interacting with it.
For now the only 2 functions that we will be using are balanceOf
and transfer
. We will start by checking
the balance of the vlad_validator
account which should contain the entire initial supply of Vladcoin
In order to transfer tokens to nick_validator
we will need to transform the address from bech32
format tohex
format.
Luckily for us the Evmos daemon has a utility called evmosd keys parse
which can be used to do this.
Before we can interact with the contract we need to get the 2 accounts we generated earlier with ourinit.sh
script.
There is a utility script called keys_util.sh
which will do the necessary conversions and give you the Hex addresses for both users
and their private keys which you can use to check the balance of the account or send a transfer transaction.
In your terminal window run the utility script
./keys_util.sh
That will result in an output in the following format:
Vlad Hex key - 161E373958716DC16EFB4659CE80F926EC0399A2
Nick Hex key - 58BA2585E068720D989FC2DC2DF991C5DA3DA7C0
Vlad Private Key - D55B4BD2106C8691298B8A110F734CE7353A2CFBCECFD698028DEF752451AF6E
Nick Private Key - DBFA8274FCBEA07BD3EA649464E15082F4D53681D2D5C11C8297B25BF9C139A
Now that we have the addresses in hex format we can use the client functions to transfer some VLAD tokens to nick_valdiator
.
But before that let's make sure that vlad_validator
indeed has a balance larger than 0.
In your terminal execute the following command making sure to replace erc2-contract-address
and user-hex-address
with your own:
go run main.go balanceOf erc20-contract-address user-hex-address
Or in our case:
go run main.go balanceOf 0x4022411E2bd4b19E7EFc474Bb620db2209C7F5Fa 0x161e373958716Dc16Efb4659CE80f926EC0399a2
The result will look something like this
Balance: 10000000000000000000000000000
Now that we know vlad_validator
has some VLAD tokens we can transfer some to nick_validator
.
In your terminal type the following command making sure to replace
erc20-contract-adress
,sender-private-key
,receiver-hex-address
and amount
with your own:
go run main.go transfer er20-contract-address sender-private-key receiver-hex-address amount
Or in our case:
go run main.go transfer 0x22B17eAB16deEC032c41f31e8Aadfd62BE9F2863 5CE50C38D30F7C95EE50ACC842ACE8D1D7C1397B991259F0AF7B0780B079F958 161E373958716DC16EFB4659CE80F926EC0399A2 50000000000
The result will look something like this:
Transaction status: 1
Transaction hash: 0xeae34e82ba3b4c39359a86da694fcae8030ffb72f43e552f66c31ecfcee51ddd
Now we can check to see if nick_validator
has the tokens we just transferred.
In your terminal type the following command making sure to replace erc20-contract-address
and
user-address
with your own:
go run main.go balanceOf erc20-contract-address user-address
Or in our case:
go run main.go balanceOf 0x423DE28D5d2d223B2990cc15Dc591A9E49cC30a3 0x58BA2585E068720D989FC2DC2DF991C5DA3DA7C0
The result will look something like this
Balance: 50000000000
Just to be sure lets also check vlad_validator
s balance as well.
go run main.go balanceOf 0x423DE28D5d2d223B2990cc15Dc591A9E49cC30a3 0x161E373958716DC16EFB4659CE80F926EC0399A2
The result is:
Balance: 9999999999999999950000000000
There are 2 types of tests implemented in this project.
- Table Driven Tests that check for the correct input and the expected return values for the individual functions
Transfer
,QueryBalance
,ObtainClient
andObtainClientAndTxSigner
- Behaviour Driven Devlopment (BDD) inspired tests written with ginko and gomega
Before running the Behaviour Driven Tests make sure to replace the address and private key constant values
with your own. If you are unsure what the values are you can always use the keys_utils.sh
utility script to view them
Navigate to the deployer
module and run go test -v
to begin the test suite
You should get the following output if you replaced the constant values and followed all the steps up to here.
Random Seed: 1661193377
Will run 2 of 2 specs
••
Ran 2 of 2 Specs in 3.011 seconds
SUCCESS! -- 2 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS
ok github.com/Vvaradinov/evmos-erc20/deployer 3.025s
Navigate to the erc20_client
module and run go test -v
to begin the test suite which will run both
the BDD tests and the Unit tests.
You should get the following output if you replaced the constant values.
Random Seed: 1661253033
Will run 4 of 4 specs
Balance: 10000000000000000000000000000
•Balance: 0
•Transaction status: 1
Transaction hash: 0xb2812ad77d0b65c382935a310c4e0b8d0561ad1207e2525341a479c4a511077f
Balance: 50000000
Balance: 9999999999999999999950000000
•Balance: 9999999999999999999950000000
Balance: 50000000
•
Ran 4 of 4 Specs in 7.027 seconds
SUCCESS! -- 4 Passed | 0 Failed | 0 Pending | 0 Skipped
--- PASS: TestERC20 (7.03s)
=== RUN TestQueryBalance
=== RUN TestQueryBalance/Test_with_empty_strings
=== RUN TestQueryBalance/Test_with_missing_contract_address
=== RUN TestQueryBalance/Test_with_missing_wallet_address
=== RUN TestQueryBalance/Test_with_badly_formatted_contract_address
=== RUN TestQueryBalance/Test_with_badly_formatted_wallet_address
--- PASS: TestQueryBalance (0.00s)
--- PASS: TestQueryBalance/Test_with_empty_strings (0.00s)
--- PASS: TestQueryBalance/Test_with_missing_contract_address (0.00s)
--- PASS: TestQueryBalance/Test_with_missing_wallet_address (0.00s)
--- PASS: TestQueryBalance/Test_with_badly_formatted_contract_address (0.00s)
--- PASS: TestQueryBalance/Test_with_badly_formatted_wallet_address (0.00s)
=== RUN TestTransfer
=== RUN TestTransfer/Test_with_empty_strings
=== RUN TestTransfer/Test_with_missing_addresses
=== RUN TestTransfer/Test_with_missing_fromPK_and_toAddr
--- PASS: TestTransfer (0.00s)
--- PASS: TestTransfer/Test_with_empty_strings (0.00s)
--- PASS: TestTransfer/Test_with_missing_addresses (0.00s)
--- PASS: TestTransfer/Test_with_missing_fromPK_and_toAddr (0.00s)
PASS
ok github.com/Vvaradinov/evmos-erc20/erc20_client 7.041s
Navigate to the utils
module and run go test -v
to being the test suite.
You should get the following output.
=== RUN TestObtainClient
=== RUN TestObtainClient/Test_with_missing_url
=== RUN TestObtainClient/Test_with_a_non_existing_IPC_endpoint_file_path
--- PASS: TestObtainClient (0.00s)
--- PASS: TestObtainClient/Test_with_missing_url (0.00s)
--- PASS: TestObtainClient/Test_with_a_non_existing_IPC_endpoint_file_path (0.00s)
=== RUN TestObtainClientAndTxSigner
=== RUN TestObtainClientAndTxSigner/Test_with_missing_private_key_and_missing_rpc_url
=== RUN TestObtainClientAndTxSigner/Test_with_missing_private_key_only
=== RUN TestObtainClientAndTxSigner/Test_with_missing_client_only
--- PASS: TestObtainClientAndTxSigner (0.00s)
--- PASS: TestObtainClientAndTxSigner/Test_with_missing_private_key_and_missing_rpc_url (0.00s)
--- PASS: TestObtainClientAndTxSigner/Test_with_missing_private_key_only (0.00s)
--- PASS: TestObtainClientAndTxSigner/Test_with_missing_client_only (0.00s)
PASS
ok github.com/Vvaradinov/evmos-erc20/utils 0.011s
- Add more test cases in the
Table Driven Tests
section - Build a CLI utility with Cobra to interact with the contract
- Automatically inject the Private keys and Hex addresses into the BDD tests
- Subscribe to contract deploy and transfer events order to avoid using
time.Sleep
function to ensure the transfer or deploy event is included in the block