There are many methods to build Solidity Smart Contract eg Truffle Framework, Embark JS & etc. I’m trying to do it without any DApp framework by just using Solcjs.
Below is the full sample code from my Gist
// Tutorial 1
// Command Line: node tutorial1.js --build example.sol
// The require packages
const path = require('path');
const fs = require('fs');
const solc = require('solc');
const md5File = require('md5-file');
// Retrieve the command line arguments
var argv = require('minimist')(process.argv.slice(2));
// Input parameters for solc
// Refer to https://solidity.readthedocs.io/en/develop/using-the-compiler.html#compiler-input-and-output-json-description
var solcInput = {
language: "Solidity",
sources: { },
settings: {
optimizer: {
enabled: true
},
evmVersion: "byzantium",
outputSelection: {
"*": {
"": [
"legacyAST",
"ast"
],
"*": [
"abi",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap",
"evm.gasEstimates"
]
},
}
}
};
// Try to lookup imported sol files in "contracts" folder or "node_modules" folder
function findImports(importFile) {
console.log("Import File:" + importFile);
try {
// Find in contracts folder first
result = fs.readFileSync("contracts/" + importFile, 'utf8');
return { contents: result };
} catch (error) {
// Try to look into node_modules
try {
result = fs.readFileSync("node_modules/" + importFile, 'utf8');
return { contents: result };
} catch (error) {
console.log(error.message);
return { error: 'File not found' };
}
}
}
// Compile the sol file in "contracts" folder and output the built json file to "build/contracts"
function buildContract(contract) {
let contractFile = 'contracts/' + contract;
let jsonOutputName = path.parse(contract).name + '.json';
let jsonOutputFile = './build/contracts/' + jsonOutputName;
let result = false;
try {
result = fs.statSync(contractFile);
} catch (error) {
console.log(error.message);
return false;
}
let contractFileChecksum = md5File.sync(contractFile);
try {
fs.statSync(jsonOutputFile);
let jsonContent = fs.readFileSync(jsonOutputFile, 'utf8');
let jsonObject = JSON.parse(jsonContent);
let buildChecksum = '';
if (typeof jsonObject['contracts'][contract]['checksum'] != 'undefined') {
buildChecksum = jsonObject['contracts'][contract]['checksum'];
console.log('File Checksum: ' + contractFileChecksum);
console.log('Build Checksum: ' + buildChecksum);
if (contractFileChecksum === buildChecksum) {
console.log('No build is required due no change in file.');
console.log('==============================');
return true;
}
}
} catch (error) {
// Any file not found, will continue build
}
let contractContent = fs.readFileSync(contractFile, 'utf8');
console.log('Contract File: ' + contract);
solcInput.sources[contract] = {
"content": contractContent
};
let solcInputString = JSON.stringify(solcInput);
let output = solc.compileStandardWrapper(solcInputString, findImports);
let jsonOutput = JSON.parse(output);
let isError = false;
if (jsonOutput.errors) {
jsonOutput.errors.forEach(error => {
console.log(error.severity + ': ' + error.component + ': ' + error.formattedMessage);
if (error.severity == 'error') {
isError = true;
}
});
}
if (isError) {
// Compilation errors
console.log('Compile error!');
return false;
}
// Update the sol file checksum
jsonOutput['contracts'][contract]['checksum'] = contractFileChecksum;
let formattedJson = JSON.stringify(jsonOutput, null, 4);
// Write the output JSON
fs.writeFileSync('./build/contracts/' + jsonOutputName, formattedJson);
console.log('==============================');
return true;
}
if (typeof argv.build !== 'undefined') {
// Build contract
var contract = argv.build;
let result = buildContract(contract);
return;
}
console.log('End here.');
...
Let me explain a bit from here, first, I need to declare all the required packages
const path = require('path');
const fs = require('fs');
const solc = require('solc');
const md5File = require('md5-file');
// Retrieve the command line arguments
var argv = require('minimist')(process.argv.slice(2));
“solc” is the Solidity compiler, which will compile the sol source code and output ABI & byte codes in JSON format.
var solcInput = {
language: "Solidity",
sources: { },
settings: {
...
}
};
solcInput variable provides the settings for the solc compiler. Please refer to this link for the input parameters description.
function findImports(importFile) {
...
}
findImports() will be called to lookup the external sol files which declare with import() statement inside the sol file. First this function will try to lookup the sol file inside the “./contracts” folder then “./node_modules” folders after that return the contents of the sol file back to the compiler.
function buildContract(contract) {
...
}
buildContract() will check the previous compiled file in JSON format exist. If the compiled JSON file is exist, it will check the source sol file md5 checksum against the checksum recorded in the compiled JSON file, if both checksums are matched, the function will return without compile the source.
If the checksums are different or JSON file is not found, the function will continue execute the compilation process which will output the JSON format content. The “abi” and “bytecode” fields are required to deploy the smart contract to the Ethereum Blockchain.
The next tutorial will talk about how to use web3js to deploy the smart contract to the Ethereum Blockchain.
My source code repository is here. 😄