Demo Compiling A Solidity Smart Contract in React with WebWorker

In this post I gonna compile a Solidity Smart Contract with Solidity Compiler JS in browsers supported WebWorker.

Tools and plugins:

nx@12.7.1
worker-plugin@5.0.1
solc@0.8.7-fixed
webpack@4 (included in NX)

Compiling a smart contract with Solc is a heavy task for browsers, and for such heavy task we need to use WebWorker.

The tool for setting up the project is NX. NX is a great tool to create monos-repository.

Configuring WebWorker for NX

NX is using Webpack underhood, In this post we use worker-plugin to make WebWorker work with Webpack. In order to config worker-plugin we need to extend the default configuration of Webpack inside NX.

  1. Create webpack.json in root folder of frontend module.

    const WorkerPlugin = require("worker-plugin");
    const nrwlConfig = require("@nrwl/react/plugins/webpack.js");
    
    module.exports = (config, context) => {
    	nrwlConfig(config);
    	return {
    		...config,
    		node: {
    			Buffer: true,
    			module: "empty",
    		},
    		plugins: [new WorkerPlugin(), ...config.plugins],
    	};
    };
    

    As you can see we have some polyfill configurations for NodeJS in Webpack

  2. Add webpack.json to workspace.json file.

    ```js
    "webpackConfig": "apps/frontend/webpack.config.js"
    ```
    

    Here is the detail of webpackConfig option.

Compile Solidity Smart Contract with WebWorker

  1. Solc@0.8.7-fixed doesn’t have typescript declaration yet, we have to add a type declaration manually. Simply add declare module 'solc/wrapper'; to *.d.ts file in your project.

  2. Creating a worker SolcJs.worker.ts file.

    /* eslint-disable no-restricted-globals */
    import * as wrapper from "solc/wrapper";
    const ctx: Worker = self as any;
    
    importScripts(
    	"https://solc-bin.ethereum.org/bin/soljson-v0.8.6+commit.11564f7e.js"
    );
    
    ctx.addEventListener("message", ({ data }) => {
    	const solc = wrapper((ctx as any).Module);
    	const compileResult = solc.compile(
    		createCompileInput(data.contractFileName, data.content)
    	);
    	ctx.postMessage(compileResult);
    });
    
    function createCompileInput(
    	fileName = "storage.sol",
    	fileContent: string
    ): string {
    	const CompileInput = {
    		language: "Solidity",
    		sources: {
    			[fileName]: {
    				content: fileContent,
    			},
    		},
    		settings: {
    			outputSelection: {
    				"*": {
    					"*": ["*"],
    				},
    			},
    		},
    	};
    	return JSON.stringify(CompileInput);
    }
    
  3. Create a Promisify function to call SolcJs.worker.ts and wait until the compilation finished.

    const compileWithWorker = async (data: any) => {
    	return new Promise((resolve, reject) => {
    		const worker = new Worker("../../SolcJs.worker.ts", {
    			type: "module",
    		});
    		worker.postMessage(data);
    		worker.onmessage = function (event: any) {
    			resolve(event.data);
    		};
    		worker.onerror = reject;
    	});
    };
    

    Thank to this great answer from T.J. Crower

  4. Now we are ready to use WebWorker to compile a Simple Solidity Smart Contract.

    const handleCompile = async () => {
    	setCompiling(true);
    	const result = await compileWithWorker({
    		content: SimpleStorageContact,
    	});
    	setCompileResult(result as string);
    	setCompiling(false);
    };
    

SourceCode in Github - nx-webworker-sample