Smart contracts aren't the easiest things to work with. The setup process to start developing involves a bunch of cumbersome, error-prone steps, which by themselves lead to hours of developer time wastage.
You'll need to set up:
And a bunch of other things.
For that reason we'll set up an automated route to make our lives easier, that will get your build, run, and test environment ready in minutes.
For demonstration purposes, we'll be using the metacoin project template from trufflebox.
At the end of this article, you'll have yourself an automated docker setup that will help you:
Make sure you've node, npm, docker & docker-compose installed on your system to follow along
So let's get started.
First, let's create a project directory and initiate some files:
mkdir dockerized-smart-contracts
&& cd dockerized-smart-contracts
npm init
mkdir src && cd src
Now within the src folder, we'll get our primary smart contract repo. We'll use a simple project like metacoin for demonstration purposes.
truffle unbox metacoin
If you wish to get one of the truffle suite box projects, you'll need to have truffle installed on your local system. After truffle downloads the project repo, we'll need to initiate a
package.json
within the src folder.npm init
Now let's fill up the
src/package.json
.The scripts are some standard commands that we regularly use for development in our smart contract ecosystem. To know more in detail what each of those truffle commands does. You can check out truffle docs. For linting, I've added Solhint which is also a popular solidity linter.
{
"name": "truffle-metacoin-smart-contracts",
"version": "0.1.0",
"description": "A smart contract demo project",
"scripts": {
"test":"truffle test",
"compile": "truffle compile",
"networks": "truffle networks",
"lint": "solhint -f table contracts/**/*.sol",
"lint-auto-fix": "prettier --write contracts/**/*.sol",
"coverage": "truffle run coverage",
"eslint-auto-fix": "./node_modules/.bin/eslint --fix .",
"deploy": "sh ./scripts/test-contract.sh",
"deploy-rinkeby":"sh ./scripts/deploy-rinkeby.sh",
"deploy-mainnet":"sh ./scripts/deploy-mainnet.sh"
},
"engines": {
"node": "12.0.0",
"npm": "6.9.0"
},
"devDependencies": {
"chai": "^4.2.0",
"dotenv": "^6.2.0",
"eslint": "^5.16.0",
"ganache-cli": "^6.9.1",
"prettier": "^2.0.5",
"prettier-plugin-solidity": "^1.0.0-alpha.56",
"solhint": "^3.2.0",
"solhint-plugin-prettier": "0.0.4",
"solidity-coverage": "^0.7.7",
"standard": "^12.0.1",
"truffle": "^5.0.30",
"@truffle/hdwallet-provider": "^1.0.0-web3one.5"
}
}
We'll need to create some scripts for network deployment and logging. These scripts can be changed based on deployment & network requirements.
mkdir logs
mkdir scripts && cd scripts
touch test-contract.sh deploy-rinkeby.sh
Now inside
test-contract.sh
we'll write a script for testing contracts.LOG_FILE=./logs/$(date "+%Y-%m-%d")-test-log.txt
echo "[ TIMESTAMP: $(date "+%H:%M-%S")" ]>>$LOG_FILE
truffle test | tee -a $LOG_FILE
echo "----------------------------------------------------------------------------------------------------------" >> $LOG_FILE
So far so good, we can similarly write relevant scripts for deployment to other networks and log their results. Now the fun part begins, we'll begin the docker process now.
Let's head back to our
/dockerized-smart-contracts
directory where we had our first package.json
.We'll create a Dockerfile & docker-compose file for our smart-contract project inside src.
touch Dockerfile.test docker-compose.yml
Now inside the docker file, we'll define the layers for our smart contract repo. This dockerfile will create an image for the contract project and run the "deploy" script by default when we run a container out of that image.
FROM mhart/alpine-node:12
RUN apk add --no-cache make gcc g++ python
RUN apk add --update tzdata
#Change it to your timezone. This'll come handy to monitor logfiles.
ENV TZ=Asia/Kolkata
# Creating Relevant directories.
# We decide to name our project directory as "smart-contracts"
WORKDIR /smart-contracts
RUN mkdir logs logs/test-network logs/main-network logs/test
# Copy the package.json & lock file & install the relevant packages
COPY src/package.json /smart-contracts/package.json
COPY src/package-lock.json /smart-contracts/package-lock.json
RUN npm install
# Add smart-contract relevant code to directory
COPY src/ /smart-contracts/
# Give executable permission to scripts.
RUN chmod +x ./scripts/*.sh
ENTRYPOINT ["npm", "run"]
CMD ["deploy"]
Now let's setup our docker-compose.yml
The compose setup will help us bundle the individual container references and link container dependencies.
version: '3'
services:
# Ganache-cli
ganache:
image: trufflesuite/ganache-cli
# Smart contract source
smart-contracts:
build:
context: .
dockerfile: Dockerfile.test
# The .env file should be set with necessary wallet keys and node information
env_file:
- ./.env
depends_on:
- ganache
# For bind mounts, make sure to add your absolute path location on your system.
volumes:
# Mount build folder
- /home/user/dockerize-smart-contracts/src/build:/smart-contracts/build/
# Mount log files
- /home/user/dockerize-smart-contracts/logs:/smart-contracts/logs/
Awesome, now let's add few scripts to our
./dockerize-smart-contracts/package.json
file.docker-compose
....
"scripts"{
"setup": "docker-compose up --no-start --build",
"start": "docker-compose run smart-contracts test && dokcer-compose up --build -d ganache",
"stop": "docker-compose stop",
}
...
Now let's first get the smart-contract project setup ready.
npm run setup
That should be followed by starting the containers and deploying contracts on the test ganache network with:
npm run start.
The smart-contracts container instance will start along with a ganache container and execute the test cases defined within src/test.
Just like that, we've our dockerized smart-contract pipeline ready.
Check the github repo template here for much more features.
The setup will help ease up your journey in the dApp world.
Until next time, live long & prosper. 🖖