How to Compile Ethereum Smart Contracts

This tutorial walks you through compiling Solidity contracts using the solc javascript library.

Introduction

Learning to use the solc compiler to locally compile your Solidity contracts is an important skill for developing applications on the Ethereum blockchain. Understanding how to compile a Solidity contract locally can develop your understanding of how existing compilation tools work. It also frees you from relying on existing tools, such as truffle or Remix.

Setup

Before you begin you will need to have Node.js installed. To check if you have Node properly installed enter the following command.

node --version
v16.0.0 // Your version does not need to match mine

The next step is to create our project directory and intialize the project.

mkdir ProjectName // Make a new project directory
cd ProjectName // Go into our new project directory
npm init // Initialize our project and skip through all of the prompts

After initializing our project we need to install the solidity compiler .

npm install --save solc // Installs the solidity compiler and saves it to our package.json

Project Structure

You want to remain organized while developing applications so we will use the following directory structure.

ProjectName/
    ethereum/
        contracts/
            ContractName.sol
        compile.js

First create the ethereum and contracts directory. Then create your Solidity contract file in the contracts folder, or move it there if you have a pre-existing contract you want compiled. If you are still learning Solidity and do not have any contracts of your own you can simply copy mine below for use in this tutorial.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

/**
 * @title Greeting
 * @dev Store & retrieve string in a variable
 * @author mattauer@umich.edu
 */
contract Greeting {

    string greeting;

    /**
     * @dev Store string in variable
     * @param _greeting value to store
     */
    function setGreeting(string memory _greeting) public {
        greeting = _greeting;
    }

    /**
     * @dev Return value 
     * @return value of 'greeting'
     */
    function getGreeting() public view returns (string memory){
        return greeting;
    }
}

After adding your contract to the contracts directory, create the compile.js script in the ethereum directory.

Compile Script

Our compile script will compile our Solidity contracts into JSON files so that we can interact with them using javascript. Our first step in writing this script is to require the necessary depenedencies.

const path = require('path'); // Provides utilities for working with file and directory paths
const fs = require('fs-extra'); // Enables interacting with the file system
const solc = require('solc'); // Compiles Solidity code

Next we are going resolve the directory path where we are going to store our compiled JSON contract.

const buildPath = path.resolve(__dirname, 'build');

Here __dirname is a nodejs variable that returns the path to the directory the currently executing script resides in. In our case this is going to be something like /somepath/ProjectName/etheruem/ and build is the name of the directory where we will store our compiled Solidity contracts. Next we are going to remove the build directory if it exists.

fs.removeSync(buildPath);

Our compile script needs to be able to re-compile Solidity contract code that gets updated. Therefore, we remove the existing build directory that contains the outdated compiled contracts. Next we are going to resolve the directory path to our contract.

const contractFolder = 'contracts';
const contractName = 'Greeting.sol';
const contractPath = path.resolve(__dirname, contractFolder, contractName);

After we resolve the path to our Solidity contract file, we read in the file and then construct an object to give as input to the Solidity compiler.


// Read in the Solidity contract file
const sourceCode = fs.readFileSync(contractPath, 'utf-8');
// Construct object to give as input to the Solidity compiler
const input = {
    language: "Solidity",
    sources: {
        [contractName]: {
            content: sourceCode,
        },
    },
    settings: {
        outputSelection: {
            "*": {
                "*": ["*"],
            },
        },
    },
};

The solidity compiler expects the input object to be constructed a certain way. We have to specify the language we are compiling from, in our case Solidity. We also have to specify the .sol files we want to compile and their matching source code. In our case this is simply the filename stored in the variable contractName and the content is the sourceCode variable containing all of the code we read in from our contract file. Our settings here are specified to request all output. If you would like more information about the input object you can find it here. Next we can use the solc library to compile our Solidity code and store the output in a javascript object.

const outputContracts = JSON.parse(solc.compile(JSON.stringify(input))).contracts[contractName];

This code turns our input object into JSON, compiles the JSON, parses the JSON back into an object, and gets the information we need about the compiled contract. All that is left to do is store this output in our build directory.

// Create build folder if it does not already exist
fs.ensureDirSync(buildPath);

for (let contract in output) {
    fs.outputJSONSync(
        path.resolve(buildPath, `${contract}.json`),
        output[contract]
    );
}

The first part of this code creates the build folder if it does not already exist. The second part of our code loops through the contracts stored in our output object and creates a corresponding compiled contract JSON file in our build directory. Notice how we loop through our output object even though it only contains one contract. We do this because there are many cases in which we store multiple contracts in one .sol file and we want to ensure our compile script is robust enough to handle these instances.

Here is the entire code for compile.js

// Compiles Contracts and outputs them to a json abi
const path = require('path'); // Provides utilities for working with file and directory paths
const fs = require('fs-extra'); // Enables interacting with the file system
const solc = require('solc'); // Compiles solidity code

const buildPath = path.resolve(__dirname, 'build');
// Whenever we compile/recompile remove the abis
fs.removeSync(buildPath);


const contractFolder = 'contracts';
const contractName = 'BattleHandler.sol';
const contractPath = path.resolve(__dirname, contractFolder, contractName);

// Read in the solidity contract file
const sourceCode = fs.readFileSync(contractPath, 'utf-8');
// Construct object to give as input to the solidity compiler
const input = {
    language: "Solidity",
    sources: {
        [contractName]: {
            content: sourceCode,
        },
    },
    settings: {
        outputSelection: {
            "*": {
                "*": ["*"],
            },
        },
    },
};

const output = JSON.parse(solc.compile(JSON.stringify(input))).contracts[contractName];

// Create build folder if it does not already exist
fs.ensureDirSync(buildPath);

for (let contract in output) {
    fs.outputJSONSync(
        path.resolve(buildPath, `${contract}.json`),
        output[contract]
    );
}

Now we can compile our contract from our project directory.

node ethereum/compile.js

If all goes well you should see the newly created JSON file in our build directory.

Conclusion

In this tutorial I showed you how to write your own compile script to compile your Solidity smart contracts locally. Hopefully this gave you some insight on how tools like truffle and remix work under the hood.

In my next tutorial I will be going over how to test your smart contracts with mocha.

If you have any suggestions for articles or feedback on this article let me know!

3 thoughts on “How to Compile Ethereum Smart Contracts

  1. Pingback: How to Write Tests for Solidity Smart Contracts Using Web3 and Mocha – Super Code Hack

  2. Pingback: How to Deploy Solidity Smart Contracts to the Ethereum Blockchain – Super Code Hack

Comments are closed.