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 (since5.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!