Automating Smart Contract Development with Docker
Tags: blockchain, devops, docker, tutorial
Published: 2021-09-12 05:52:42


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:
- A local testRPC network for testing.
- Truffle + web3 environment for node communication and deployment.
- Gathering transaction receipts, logs, etc.
- Contract linting and staging preparation.
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:
- Run smart contract tests with testRPC.
- Perform deployments on your chosen network.
- Easily get updated migration files on your local system.
- Help collect data logs for future evaluation

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. This will create a logfile with today's date and the entire deployment transcript.

    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. These are the primary scripts that we'll execute to set up our running containers via 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. 🖖

Originally posted: https://hackernoon.com/automating-smart-contract-development-with-docker-5w5735ou