We have been experimenting on how to introduce web3 at work. After using hardhat for all of the Buildspace classes, I thought exploring Truffle and Ganache would be interesting for comparison. Unfortunately, not everything worked out-of-the-box for me. And it lead me to try to get a working Truffle/Ganache setup using Docker.
Understanding the Technology
That introduction used a lot of terms that you might not be familiar with. You also might not know what in the world web3 is. If so, this section is for you. If the introduction did not sound like Greek to you, feel free to skip this section and go directly to the problem statement.
web3 & EthereumWeb3 is an iteration of web development based on blockchain technology. The hopes of this iteration is to decentralize data throughout the web and establish a more universal, accessible economy. 1 The two largest blockchain technologies today is Bitcoin and Ethereum. Ethereum is the blockchain which the libraries mentioned above use. Ethereum is a decentralized, open-source blockchain technology that has built-in smart contract capability. 2 The ability for smart contracts is what makes Ethereum so attractive to developers. This attractiveness is why there is so much tooling around these contracts.
Solidity is a programming language that one uses to write smart contracts. This is the language that is used when you initialize a project with either Hardhat or Truffle. Even though Solidity was first developed to work with the Ethereum blockchain, it has since expanded to work with different blockchains. This fact makes it useful to learn if you want to dive into smart contract development.
HardhatHardhat is an NPM package that creates a development environment to compile, deploy, test, and debug your Ethereum software. 3 This is the most popular tool to develop Ethereum smart contracts.
TruffleTruffle is another NPM package. It is a development environment, testing framework and asset pipeline for blockchains using the Ethereum Virtual Machine. 4 Very much like Hardhat, Truffle makes contract development smoother.
GanacheGanache is the third NPM package listed here. It is a personal blockchain for Ethereum distributed application development. 5 This allows you to deploy your smart contracts locally and saves you from testing on the real blockchains. It’s the first time that I have explored running my own blockchain. I usually just use the testnets, but this was a nice alternative. And I can see using this going forward.
I had issues installing Truffle globally on my computer. (Which is the preferred method per their documentation.) There seems to be an issue with Truffle and the latest version of NPM; which seems to have been around for a while. I could not get Truffle to install locally without messing with my global Node.js install. Below is the error I am getting when I tried the global installation:
npm ERR! fatal error: too many errors emitted, stopping now [-ferror-limit=] npm ERR! 20 errors generated. npm ERR! make: *** [Release/obj.target/leveldb/deps/leveldb/leveldb-1.20/db/builder.o] Error 1 npm ERR! gyp ERR! build error npm ERR! gyp ERR! stack Error: `make` failed with exit code: 2 npm ERR! gyp ERR! stack at ChildProcess.onExit (/nodejs/16.3.0/.npm/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:194:23) npm ERR! gyp ERR! stack at ChildProcess.emit (node:events:394:28) npm ERR! gyp ERR! stack at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12) npm ERR! gyp ERR! System Darwin 18.7.0 npm ERR! gyp ERR! command "/nodejs/16.3.0/bin/node" "/nodejs/16.3.0/.npm/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild" npm ERR! gyp ERR! cwd /nodejs/16.3.0/.npm/lib/node_modules/truffle/node_modules/ganache/node_modules/leveldown npm ERR! gyp ERR! node -v v16.3.0 npm ERR! gyp ERR! node-gyp -v v8.4.1 npm ERR! gyp ERR! not ok
I am not a huge fan of installing things globally, especially when it does not work with the latest version of Node.js or NPM. Luckily, we have tools to help us in these situations.
DockerDocker has been around for a while. But in case you are unfamiliar, Docker is a set of tools that facilitates OS-level virtualization. 6 These tools will allow us to containerize the Truffle and Ganache setup removing the need of global installation. It’s a win-win. The Docker container we build can be used anywhere, and we no longer need to worry about the version of Node.js we have installed on our computer.
If you do not have Docker installed on your machine yet, go here. It’s beyond the scope of this post, but the linked directions should be pretty straight forward. But once installed, let’s clone my init example where we will keep the code we need for Docker. Run the following commands to set up this directory:
$ git clone https://github.com/joshfinnie/truffle-init-example.git <PATH> $ cd <PATH> $ touch Dockerfile $ touch docker-compose.yml
This will give you the default files we will need to get Truffle and Ganache installed in a Docker container.
It’s a bit odd, but I had to figure out how to get the default Truffle folder structure without installing it globally.
This repo was generated for this post and gives you the default folders and files a
truffle init will do.
Give it a ⭐️ if you find it helpful!
Below is the code for the Dockerfile. We’ll go into some depth asto what’s going on here.
FROM node:16-bullseye-slim as base RUN apt-get update && \ apt-get install --no-install-recommends -y \ build-essential \ python3 && \ rm -fr /var/lib/apt/lists/* && \ rm -rf /etc/apt/sources.list.d/* RUN npm install --global --quiet npm truffle ganache FROM base as truffle RUN mkdir -p /home/app WORKDIR /home/app COPY package.json /home/app COPY package-lock.json /home/app RUN npm install --quiet COPY truffle-config.js /home/app COPY contracts /home/app/contracts COPY migrations /home/app/migrations/ COPY test /home/app/test/ CMD ["truffle", "version"] FROM base as ganache RUN mkdir -p /home WORKDIR /home EXPOSE 8545 ENTRYPOINT ["ganache", "--host 0.0.0.0"]
The first block of code I will breakdown is the code that builds our “base” container.
I have long ago adopted multi-stage builds as a way to optimize my Dockerfiles, and feel like this is the ideal reason why.
Having a “base” container allows us to make sure the default
node:16-bullseye-slim Docker image is up-to-date and has all our required packages installed.
FROM node:16-bullseye-slim as base RUN apt-get update && \ apt-get install --no-install-recommends -y \ build-essential \ python3 && \ rm -fr /var/lib/apt/lists/* && \ rm -rf /etc/apt/sources.list.d/* RUN npm install --global --quiet npm truffle ganache
python3 from the Debian package manager.
These are dependencies for Truffle.
And we install
ganache using NPM.
Taking from our created “base” image, we then buildout our truffle container.
FROM base as truffle RUN mkdir -p /home/app WORKDIR /home/app COPY package.json /home/app COPY package-lock.json /home/app RUN npm install --quiet COPY truffle-config.js /home/app COPY contracts /home/app/contracts COPY migrations /home/app/migrations/ COPY test /home/app/test/ CMD ["truffle", "version"]
We create a working directory, copy our
package.json file, and install the required packages.
We then copy over the rest of the files needed to run the Truffle project.
truffle version to spit out our version of Truffle installed.
Note This command would be doing something more exciting here, but for this blog post, that is all we’re doing.
Lastly, we build out our Ganache container. This one is a tad less complex due to Ganache being a tool that runs in the background.
FROM base as ganache RUN mkdir -p /home WORKDIR /home EXPOSE 8545 ENTRYPOINT ["ganache-cli", "--host 0.0.0.0"]
In this container, we again take from the “base” container, expose the default Ganache port of
8545 and run it.
Note we use the
--host flag here to change the default host from 127.0.0.1 to 0.0.0.0.
Below is the Docker Compose file.
Even though you don’t need a Compose file, I find them easier to deal with and love to include them.
There’s something satisfying about running
docker compose up and having your entire system running within Docker.
version: "3.4" services: truffle: build: context: . target: truffle depends_on: - ganache networks: - backend volumes: - .:/home/app - /home/app/node_modules ganache: build: context: . target: ganache ports: - 8545:8545 networks: - backend networks: backend: driver: "bridge"
I hope this post helps anyone struggling to install Truffle locally.
I never understood the reasoning NPM libraries needing global installation.
Hardhat fixes this by using
npx and I would love to see Truffle move to this paradigm.
It would make using it much easier in my mind.
But it would also require them to fix this issue with the latest version of Node.js.
If anyone knows of another way to fix this, feel free to chat with me on Twitter.