Using lein-solc with truffle

Intro

Truffle is a part of a larger smart contract development framework. Like a sort of swiss-army-knife for smart-contracts development it bring many benefits, but it really shines when it comes to smart contract deployment and migration.

Truffle lets you write simple deployment scripts and will keep track of deployed contracts and their addresses against all of the configured ethereum networks. For details best consult truffle’s comprehensive documentation.

There are some downsides though.

  • Truffle has no --no-compile flag and will start by compiling the contracts before migrating them at the start of the session.
  • There is no watch mode for truffle compile to autocompile the contracts on source code changes.
  • Current stable version of truffle (4.X at the time of writing) uses solc.js, rather than the native compiler, which is significantly slower. Future versions of Truffle (since 5.X) should support setting custom compiler.

All of these problems can be mitigated by using lein-solc plugin for compiling smart-contract code, and relying on truffle just for deploying them. In this post we will go over setting up a workflow that allows to do just that.

Requirements

We will assume native solc compiler is installed an availiable on your PATH. You can quickly get the version 0.4.24 of the compiler with these commands:

sudo wget -P /bin https://github.com/ethereum/solidity/releases/download/v0.4.24/solc-static-linux
sudo chmod a+x /bin/solc-static-linux
sudo ln -s /bin/solc-static-linux /usr/bin/solc

We will also assume that leiningen is installed. Obviously you will also be needing truffle and the ganache testrpc:

npm install -g truffle
npm install -g ganache-cli

Creating a project

You can create a bare Truffle project by issuing this command in the root directory :

truffle init

Project directory will now i.e. contain these files and directories:

  • contracts/ : Directory for smart contracts
  • migrations/ : scriptable deployment files
  • truffle.js : truffle config file

Lets create a very basic TestContract.sol inside the contracts/ directory:

pragma solidity ^0.4.24;

contract TestContract {

  uint public value;

  constructor(uint _value)
    public {
    value = _value;
  }

}

We now need a migration script, which we can call 2_lein_solc_migration.js, created inside the migrations/ directory:

var TestContract = artifacts.require ('TestContract');

module.exports = function(deployer, network, accounts) {
  const address = accounts [0];
  const opts = {gas: 4e6, from: address};

  deployer.deploy (TestContract, 1, Object.assign(opts, {gas: 500000}));
}

Inside the truffle.js config file we specify the host and port where the ganache testrpc will be availiable on:

module.exports = {
  networks: {
    ganache: {
      host: 'localhost',
      port: 8549,
      network_id: '*'
    }
  }
};

Compiling smart contracts with lein-solc

To bring lein-solc to the project we need to create project.clj file with the plugins config:

(defproject lein-solc-truffle "1.0.0"
  :description "Using lein-solc for compiling truffle artifact"
  :plugins [[lein-solc "1.0.11"]]

  :solc {:src-path "contracts"
         :build-path "build/contracts/"
         :abi? false
         :bin? false
         :truffle-artifacts? true
         :contracts :all})

Truffle artifacts are supported since version 1.0.11 of the plugin, and that’s the version we will usem we also give it the path where contract sources reside, tell it where to output the resulting artifacts and to skip generating separate files with the bytecode and abi. For the other options and the explanation you can consult the official documentation.

With that in place we can compile the contracts:

lein solc

To start the plugin in watch mode and autocompile on changes:

lein solc auto

After it finishes the TestContract.json artifact is created inside the build-path directory. If we examine it, truffle artifacts are just JSON envelopes around the compiled abi and bytecode, with some additional information truffle uses for book-keeping of the deployed contracts.

Deploying the contracts

We will be deploying the contracts to the development testrpc, that we start on the localhost port 8549, same as we specified in the truffle.js config file:

ganache-cli -p 8549

To run the migration script and deploy the contracts we invoke truffle, specifying the network (by its name from the truffle.js config) and using the --reset option to run all the migrations from the beginning (effecively redeploying the contracts if they existed on the network before):

truffle migrate --network ganache --reset

Any changes to the contracts will be recompiled by the lein-solc, provided it is running in the watch mode, and we can redeploy them again with truffle, speeding up the development cycle.

Interacting the contracts

To confirm the dpeloyment went fine, and the contracts are availiable on the network we can open the truffle JS console:

truffle console --network ganache

Truffle injects all the deployed contracts, so we can simply call:

TestContract.at(TestContract.address).value();

and get the value set in the contract’s constructor as a response.

Conclusions

Proposed workflow is obviously just one of many, but hopefully you can find it usefull.

I maintain a repository with an extended version of the example that we just went through. There are several contracts deployed, and the migration script covers some more advanced deployment steps, like linking contracts via their bytecode and using delegatecall in MutableForwarder to create updateable instances of the contracts.

Thanks for reading!

Written on December 7, 2018