DEV Community

foxgem
foxgem

Posted on • Edited on

Using Remix Analyzer For Solidity Syntax Analysis.

Remix Analyzer is the underlying library for the Remix IDE Solidity Static Analysis plugin. This means that it can be also used for other projects, not just for Remix IDE.

Since Hardhat is a popular contract development tool nowadays, it would be nice to make Remix Analyzer work with it. In the Remix code repository, this test file shows its basic usage, the key code:

const res: CompilationResult = compile('functionParameters.sol')
const Module: any = checksEffectsInteraction
const statRunner: StatRunner = new StatRunner()
...
const reports = statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }])
Enter fullscreen mode Exit fullscreen mode

The logic is simple: compile code > apply rules > output report.

Note that.

The Module in the code above is Remix Analyzer Rule class, all rules currently supported can be found in: this file.

Multiple Module objects can be passed to runWithModuleList, if you want to apply multiple rules what you need to do is to initialize multiple Module instances.

Making a Hardhat Task

Using Remix Analyzer in Hardhat is a piece of cake: calling the script above from a Hardhat task. However, there are some tricky things if you just copy it as it is. If you look closely at the code above, you can see it first goes to download a Solidity compiler and then uses it to complete the contracts. In test setup, there is a script like the following:

test('setup', function (t) {
  solc.loadRemoteVersion('v0.5.0+commit.1d4f565a', (error, compiler) => {
    ...
Enter fullscreen mode Exit fullscreen mode

Of course, there is no way to stop you from copying all code lines to your task script. But it is not a Hardhat style:

  1. Hardhat comes with a compiler, you should use it directly when you can, rather than downloading a new one.
  2. Following the previous step, it would be nice to get CompilationResult data from the compiled files directly. Then, we don't have to do any data transformation jobs.

As for 1, Hardhat supports calling tasks within a task script, the code example:

await hre.run(TASK_COMPILE, { quiet: true });
Enter fullscreen mode Exit fullscreen mode

As For 2, it's simple too. Hardhat exposes fields to allow developers to access the artifacts in a project, including the compiled files. One thing to note here is that syntax analysis does not use the final artifacts but a middle one. That is, it needs artifacts/build-info rather than artifacts/contracts.

Also, a json file in artifacts/build-info is not expected by Remix Analyzer which only uses part of it. By comparing the CompilationResult type with the contents of the json, it is easy to see the connection: the output field.

So, the code snippet for the task is like:

const runner = new staticAnalysisRunner();

await hre.run(TASK_COMPILE, { quiet: true });
console.log("√ compiled contracts.\n");

const compileResults = await hre.artifacts.getBuildInfoPaths();
compileResults.forEach((result) => {
  const compiled = JSON.parse(fs.readFileSync(result).toString());
  const source = Object.keys(compiled.input.sources)[0];
  const modules = calculateRules(source, rulesConfig).map((Module: any) => {
    return { name: new Module().name, mod: new Module() }
  });
  const reports = runner.runWithModuleList(compiled.output, modules);
  ...
})
Enter fullscreen mode Exit fullscreen mode

The main logic here is:

  1. compile
  2. generate a report for each compiled file: parse json > compute rules > apply rules

Making a Hardhat Plugin

Syntax analysis is a highly reusable task and there is no reason why it cannot be a plugin.

As a dapp developer, you should really read its documentation if you still don't know what a Hardhat plugin is. With a plugin you can easily share configurations between projects, including custom tasks of course.

The steps recommended in the documentation: first testing the logic of a task in a Hardhat project; then moving it into a plugin project and wrapping it as a plugin. Now, we have completed the first step.

The documentation is very clear on how to build the plug-in, so I won't go into it again here: fork the plug-in template project and make some changes.

Here, I only highlight two things.

Dependencies

Following the rules in the documentation.

Extending Configuration

The documentation does not show too many details on this topic, rather gives a link to an example file. But I found that I have to read this file together to understand the logic.

After reading the code, I summarized the following rules:

  1. Extending both XxxUserConfiguration and XxxConfig, where:
    • The fields added to the former are optional and the latter are required.
    • Generating the latter based on the former during initialization. Or giving a default if the former is missing.
  2. When adding a new field to Hardhat existing configuration items, follow what the example did: use the corresponding Types. In the example, they are ProjectPathsUserConfig and ProjectPathsConfig.
  3. To add a new top-level configuration item to Hardhat, use HardhatUserConfig and HardhatConfig like below:
declare module "hardhat/types/config" {
  interface HardhatUserConfig {
    analyzerRules?: AnalyzerConfiguration;
  }

  interface HardhatConfig {
    analyzerRules: AnalyzerConfiguration;
  }
}
Enter fullscreen mode Exit fullscreen mode

As for the rest, there's not much more to say. If you're interested, you can visit hardhat-remix-analyzer for more details.

Final Words

There are two other similar tools: slither and solhint.

After comparing among them, I personally recommend focusing on the latter two, as they offer more rules and are updated more frequently. Meanwhile, Hardhat has a plugin for solhint.

Top comments (0)