Created: Mar 06, 2024

[Write-Up: Preparing for smart contract audits]

An audit is a phase in a blockchain project's development intended to verify its reliability prior to its use by the target audience. An audit involves a thorough analysis of the code, a verification of its resilience to several attack vectors, an assessment of its operational effectiveness, and a suggestion of potential fixes for issues that are found. De facto, audits are an industry standard.

Publishing audit results for transparency purposes among stakeholders and intended users is an excellent move (which introduces a sense of security when launching and using the product).

The audit is typically an expensive and not a fast activity since it covers a wide range of topics, that require the performers to possess the necessary skills. Nonetheless, the project's initial preparation and planning may have an impact on this stage. This will assist in reducing the amount of communication costs and project analysis time. Let's now consider several ways to facilitate the project's audit passability.

Overview of an audit process

To prepare for an audit, you first need to understand how this process works as a whole. The general audit process can be described as follows:

  • The team's overall review and pre-audit of the project.
  • Interaction with auditors, involving the definition of objectives and audit scope - what are the project goals, and what aspects of it need auditing (smart contracts, off-chain components, and infrastructure, or all of them). Other important it to determine how much cost (budget) and how long the audit will take to complete (time frame).
  • A code freeze is a precautionary measure to address possible issues in a consistent way.
  • Providing auditors with code, design documentation, and other prerequisites.
  • Initial audit, which often includes code review, performance, and efficiency analysis. It also pushes auditors to examine how well code performs in unique situations and in edge cases, test code resilience using different attack methods.
  • Gathering results of the initial audit for internal release of the preliminary report.
  • The team's evaluation of the report and communication with the auditors: clarification of issues, that cause no real threat, then fixing remaining issues identified as valid threats.
  • Submitting updated documentation and code for the final analysis.
  • Receiving the auditors' final report, which includes found issues, a summary of their discussion, and resolution status.

The following links will allow you to browse examples of final audit reports from well-known industry representatives:

The multiphase nature of the process is evident. Both the project team and the auditors will need to put in an effort in order to achieve the most beneficial outcome. The team should go through the code, describe the necessary context, and be prepared to start working on fixes. Auditors will also need to dive into the project from scratch, carry out multiple checks, and document the findings. The parties will need to actively communicate with each other at certain stages of the process.

Getting ready for an audit

Let's figure out what preparation actions would help pass an audit.

Planning and arrangements

First of all, define the audit's budget and time frame and decide on an auditing company. There are many possible candidates, so it would be good to figure out the best option for your constraints.

Aside from that, get the project ready to be put on hold while the audit is ongoing. Enhance the project's functionality to the point where major adjustments shouldn't be made anymore. Modifications made during an audit will lead to more issues, therefore the audit results could become devalued. A re-audit will probably be necessary if there is a major change in functionality.

Thorough review (pre-audit)

The team should carry out a thorough review. As a first step, examine and evaluate the code:

  • Generally speaking, it's critical that the code is legible, clear, and effective. If there are any areas where this is "wrong," it's important to fix them or provide an explanation in the NatSpec comments. As a result, there will be less communication going forward between the team and the auditors.
  • Verifying compliance with standard security and efficiency approaches as well as recognized attack vectors is a worthwhile endeavor. As a result, auditors may find fewer flaws overall, allowing them space to concentrate on more difficult individual cases.

Next, verify auto-tests to ensure that the project addresses exceptional circumstances, important functionality, and the primary user path. Tests need to be error-free. It is preferable that they are simple to comprehend. As a result, they ought to be examined for readability in addition to the project code. Additionally, the coverage may need to be brought to a specific level.

Another good action is to examine the supporting documents:

  • The project repository should include instructions on how to run auto-tests, publish the project, and install and configure it.
  • It is advisable to provide the basic configuration and parameters since they can be needed for testing. When using sophisticated tools (like simulators) for auditing, auditors may need to fully understand the configuration of the project and each of its component pieces. Certain vulnerabilities stem from the operating settings of the code rather than the code itself. One should not undervalue their influence.
  • Architectural diagrams for call graphs, management control roles, and the relationships between smart contracts are useful to have. They have to be important if they exist. From an analytical standpoint, the team should also pay attention to them separately since irregularities or discrepancies can be found at any time.
  • A general description of the project, including the problem it answers, the techniques it uses, the functionality it should have, and how it should operate, is a good idea.

Initial communication and paperwork

Shortly before an audit, you would need to specify the audit's scope:

  • Explain which major risks and issues need to be the audit's top priorities.
  • List the commit, branch, and files in the repository that should be ignored and those that are up for review.

It is important to put development on hold and to identify a repository commit that you will send to the auditors. It is not recommended to continue active development in parallel with an audit, as audit fixes may clash with side changes.

Another step is to prepare the contact information of team members who will communicate with the auditors during the process. As the audit moves along and you receive a preliminary report, the team should be ready to respond to inquiries, requests, and remarks from the auditors.

Practical Resources and Techniques

Project preparation for an audit can be supported by a variety of techniques and tools. Some of them are applicable not just when a project is getting ready for anything significant, but also while quality control is being maintained in the initial phases of development - after all, "it's not clear where they sweep, but where they don't litter". Utilizing these instruments with knowledge and consistency can help maintain the project's cleanliness and prevent the need for heavy labor before any crucial phases.

Readability of the code

Linters like Solhint or Prettier with the Solidity plugin (or their equivalents) are helpful for keeping the code readable. They will only need to be set up once, which will ultimately save a great deal of time and work when formatting the code. To make the code readable for the typical developer, you should concentrate on following the Solidity style guide. To enforce the same readability of code in various development environments, it's a good idea to invoke linters in pre-commit hooks or continuous integration.

These tools are useful, but they don't highlight issues with duplication, improper structure, or inefficiency of code. To handle these effects you should concentrate on general programming methods, principles, and patterns like DRY, KISS, Composability, Single responsibility principle, Separation of concerns, and particular Solidity development patterns to strive for clean code.

Code resilience analysis

The smart contracts development practice has been around for a while, and throughout that period, the ecosystem has seen the emergence of several tools that make resilience analysis possible. In addition, several of them are free to utilize.

Slither is the first tool you should consider using. As a static analysis framework, this tool will let you examine your project for typical issues and commonly known vulnerabilities. The tool is simple to install and operate: all it needs is access to the smart contract code and the relevant version of Python. Slither will display the work's results based on its findings; it's important to note that these results may not always be accurate and can be considered broad. The process of static analysis itself suffers from false negative and false positive results. Nevertheless, the search for common issues, flaws, and independent insights about the current state of the code base (at the cost of time to examine all the output results) is where its value resides.

Further, I recommend that you focus on Echidna. This instrument is a fuzzer, which attempts to pick a certain set of input parameters in order to verify the stability of conditions that are critical to the contract. You'll learn how to utilize it with a tutorial where you have to create extra code to make the fuzzer decide what is important to pay attention to. Testing important contract properties will benefit from this.

By the way, both Slither and Echidna are quite easily integrated into a CI of a project via GitHub Actions:

These tools may therefore regularly contribute to enhancing and preserving reliability.

Subsequently, more advanced instruments such as Manticore and Mythril employ a symbolic execution approach to discover means of cracking contract conditions. Their instructions outline their capabilities and use cases:

Note that this independent examination of smart contracts for common issues and weaknesses
is merely a setup for a more thorough audit. Analysis could provide "some sense of security," but that assurance could be misplaced. Automated tools cannot take the place of professional auditors, who possess “out of the box” thinking and deep knowledge.

At the same time, keep in mind that auditors have time and financial constraints, therefore
should not be delegated an exclusive responsibility over the security of your project. Thus, when the development team and the auditors approach their task with the appropriate level of responsibility on both sides, it is the best scenario.

Getting the documentation ready

Documentation is helpful for any software project, including blockchain projects, since it facilitates understanding and decision-making throughout the entire process. The project will eventually grow to the point where it can not be fully covered solely by the code. When a project is created iteratively, this is not perceived. However, those who must comprehend the connections between various parts and elements in the short term experience all of the intricacies.

The following are things to be very mindful of while preparing paperwork for an audit

  • The actualized content of README provides easy-to-follow instructions for setting up the project, executing auto-tests, deploying and configuring it.
  • Functional requirements for the product include what problems the product solves, what advantages it offers, and what features and behaviors are essential.
  • Architectural documents covering access control grid to critical functionality, smart contract inheritance, and method call diagrams, as well as external interfaces.

Architectural documentation is frequently subject to time limitations, while functional requirements and README are necessary for development and design specifications. Fortunately, you may generate the documentation on demand with the following tools since code is available as the primary source of truth:

With the exception of verifying access control methods, which have various implementation techniques and are not yet covered by tools, the mentioned tools will suffice to meet the majority of needs. It is therefore preferable to document access control and carefully maintain such documentation.

An overall checklist to utilize

- [ ] Define the audit's budget and time frame.
- [ ] Choose an auditing company.
- [ ] Finish development of product critical functionality.
- [ ] Perform an internal review (a pre-audit).
   - [ ] Code review: readability, formatting, and efficiency (gas optimization).
   - [ ] Examine the code reliability by considering known issues, vulnerabilities and attack vectors.
      - [ ] Apply a list of automated tools for code analysis: <list of tools>.
   - [ ] Check the auto-tests for sufficiency, error-free running, coverage, and critical user path.
- [ ] Check the documentation:
      - [ ] Inline comments in the code at crucial but obscure sections.
      - [ ] The actualized README covers installation, auto-testing, deployment and configuring.
      - [ ] Project description and functional requirements.
      - [ ] The inheritance hierarchy, smart contract interconnections and access control actors are covered in the current architectural documentation.
- [ ] Temporarily freeze the code changes (at least while the audit is ongoing).
- [ ] Prepare a scope document that includes the required links, documentation, and a specific commit from the repository.
- [ ] Send the project in for an audit.
- [ ] Communication with the auditors following the receipt of the preliminary report.
- [ ] Address identified issues (if any).
- [ ] Send in a revised commit that has the fixes.
- [ ] Recap the main points.

Additional helpful articles as a reference