Sign in
Log inSign up

Creating a simple project management tool for ethereum in less than 150 lines of code .

Alvin T Zachariah's photo
Alvin T Zachariah
·Jun 29, 2019

What we're building

Sol-cli is a command line tool which understands two commands :

1 . init<filename>[options..] -  This command creates a smartcontracts directory and config.json file . The smartcontracts directory will contain an empty <filename>.sol file . The init command also accepts two options :

-h  - to specify hostname of the ethereum node from where we're planning to deploy the contract .

-p  - to specify the rpc port of the node .

2 . compile<filename> - This command will create an artifacts directory with a <filename>.json . This file contains bytecode and abi of the deployed smart contract .

3 . deploy<filename> - This command will deploy our contract to the specified network . It has following options -a  - to specify the address to be used as default account -p  - to specify the password of the given account

If youre using ganache , you need not specify address and password .

Installing Sol-Cli

Clone the sol-cli github repository

git clone https:github.com/alvinzach/solcli.git

Navigate to the cloned directory . Install the dependencies and create a symbolic link

npm install
sudo npm link

Thats it ! . To test your installation just run

solcli --help

The code

We are using commander for creating our command line application in js.

#!/usr/bin/env node
const program=require('commander')
const solc=require('solc')
const fs=require('fs')
var Web3=require('web3');
program
    .version('1.0.0')
    .description('solidity compiler')

program
    .command('init <filename>')
    .description('initiate')
    .alias('i')
    .option('-p,--port <n>','rpc port')
    .option('-h,--host <n>','rpc host')
    .action((filename)=>{
        var config={
            "smartcontracts":[]
        }
        if(program.port){
            config.port=program.port
        }else{
            config.port="8545"
        }
        if(program.host){
            config.host=program.host
        }else{
            config.host="localhost"
        }
        config.smartcontracts.push(filename)
        var sampleSol=`pragma solidity ^0.4.23;\ncontract ${filename} {\n}`
        fs.writeFile('./config.json',JSON.stringify(config,null,2),(err)=>{
            if(err){
                console.log('Cannot init solcli')
                return
            }else{
                if (!fs.existsSync('smartcontracts')){
                    fs.mkdirSync('smartcontracts');
                }
                fs.writeFile(`./smartcontracts/${filename}.sol`,sampleSol,(err)=>{
                    if(err){
                        console.log(err)
                        return
                    }else{
                        console.log(`Edit smartcontracts/${filename}.sol file`)
                    }
                })

            }
        })


    })
program
    .command('compile [filename]')
    .description('compile solidity file')
    .alias('c')
    .action((filename)=>{

        if(filename){
            if (!fs.existsSync('artifacts')){
                fs.mkdirSync('artifacts');
            }
            let input = fs.readFileSync(`./smartcontracts/${filename}.sol`);
            let output = solc.compile(input.toString(), 1);
            if(output.errors){
                output.errors.forEach(element => {
                    console.log(element)
                });
            }
            if(output.contracts[`:${filename}`]){
                var artifacts={
                    bytecode:output.contracts[`:${filename}`].bytecode,
                    abi:output.contracts[`:${filename}`].interface,
                }
                fs.writeFile(`./artifacts/${filename}.json`,JSON.stringify(artifacts,null,2),(err)=>{
                    if(err){
                        console.log(err)
                        return
                    }else{
                        console.log('wrote artifacts')
                    }
                })

            }
        }else{
            console.log('no contract specified')
        }
    })
program
    .command('deploy [filename]')
    .description('deploy smart contract')
    .alias('d')
    .option('-a,--address <n>','Address of sender')
    .option('-p,--password <n>','password')
    .action((filename)=>{
        var config=JSON.parse(fs.readFileSync('config.json'))
        var web3=new Web3(new Web3.providers.HttpProvider(`${config.host}:${config.port}`));
        if(program.address){
            web3.eth.defaultAccount=address;
        }else{
            web3.eth.defaultAccount=web3.eth.accounts[0];
        }
        if(program.password){
            web3.personal.unlockAccount(web3.eth.defaultAccount,program.password,100000)
        }
        var contract_details=JSON.parse(fs.readFileSync(`artifacts/${filename}.json`));
        var contract=web3.eth.contract(JSON.parse(contract_details.abi))
        const contractInstance = contract.new({
            data: '0x' + contract_details.bytecode,
            gas: 90000*5
        }, (err, res) => {
            if (err) {
                console.log(err);
                return;
            }

            console.log("transaction hash :"+res.transactionHash);

            var intId=setInterval(()=>{
                if(contractInstance.address!==undefined){
                    contract_details.address=contractInstance.address;
                    fs.writeFileSync(`artifacts/${filename}.json`,JSON.stringify(contract_details,null,2))
                    console.log(contractInstance.address)
                    clearInterval(intId)
                    return
                }
            },3000)
        });
        console.log(config)
    })
program.parse(process.argv)

Sol-cli has 2 dependencies - commander to write command-line tools in nodejs and solc ,the solidity compiler .

On receiving the command init<filename> I am creating a new json object called config to contain details of smart contract . config contains a smartcontracts array , host and port as properties . If hosname and port are not specifies in the init command , localhost and 8545 will be taken as default values . The config object is then written into config.json file . A smartcontracts/<filename>.sol file is also created .

The compile<filename> command reads content of smartcontracts/<filename>.sol and passes it to solc.compile() function . The abi and bytecode generated will be stored in the artifacts folder . The deploy<filename> command will create a new instance of the contract . The address of the deployed contract will be present in artifacts/<filename.json>