Audit Readiness Guide

Ready for an audit? Check out our guide for actionable tips to help you prepare for a web3 security audit.

Part I – Security Audit Overview

What is a Security Audit?

A smart contract audit provides developers with an in-depth review of their code base, in order to identify potential vulnerabilities, inefficient code, or poor coding practices. Typically an audit will check the code against security best practices and also identify discrepancies between the specifications  and the code. A thorough security audit should also provide potential fixes and mitigations for such findings. Experienced auditors have deep and varied knowledge of different types of issues and business logic errors that they draw upon during an audit.  They perform depth reviews of the codebase and architecture, and identify potential issues as well as put forward recommendations to fix or mitigate the issues found.

In addition to manual code review, today’s professional security engineers often utilize special tools to help them rule out certain vulnerabilities or cover attack vectors that are easier to cover via automation. Therefore the best firms typically carry out using a mixture of automated and manual processes in order to find security issues that could be exploited by malicious actors.

Furthermore, a good smart contract auditor will look at the business logic laid out by the project whitepaper and other (end-user-facing) documentation and make sure that the logic is mapped correctly in the smart contract code.

The findings of the audit are then laid out in a comprehensive report, highlighting findings and outlining potential solutions. The process is often iterative with two-way communication between the auditors and project developers, and an active working relationship will often yield the best results for a security audit.

Audits from reputable security companies are extremely beneficial in building trust in a community of users as well as reassuring investors that the project prioritizes good security.

When should you get an audit?

Once a smart contract goes live on a blockchain, they can be accessed by everyone, and unfortunately this includes malicious/bad actors that will try to take advantage of any vulnerabilities for financial gain.On a public blockchain such exploits or hacks cannot be reversed, leading to financial and reputational loss not only for the project but also for users of the protocol.

Decentralized and permissionless systems come at a cost, and hacks are a constant thorn in the side of projects, with over $1.5B in exploits in the first half of 2024 alone. It is therefore best practice and optimal to audit BEFORE these smart contracts are deployed on mainnet, and instead projects will typically deploy to a testnet to do final testing and security audits before going live.

Smart contract audits are always done on a frozen code base which is a snapshot in time and has a specific commit hash. Any changes made to the code during or after a code audit may introduce new attack vectors that may not be addressed by the audit.

Projects often upgrade their protocols or add new features over time, with the smart contracts or indeed the entire codebase upgraded to a new version. Every time such an implementation takes place it is best practice to get a follow-up audit to make sure that the code is safe for users.

How should you prepare for a security audit?

Preparing for an audit requires following common engineering best practices for high-quality project development. This involves clean and modularized code, standard libraries, and thorough testing with 100% test case coverage. Documentation is also a crucial component of the audit process, providing evidence to support findings and recommendations. Being well-prepared for an audit allows the auditors to focus on identifying difficult-to-catch issues.

Thorough testing, both automated and manual, detects system issues and bugs. One of the first things an auditor will do is run the test cases provided by the project to get an understanding of the specific contract properties and mechanics. Well-written tests decrease the time and cost of audits. Ideally test cases should  provide coverage for 100% of the code-base.

Documentation (and ideally detailed specifications) is required and provides the blueprints and evidence required to support audit findings and recommendations. They lay out the business logic, features, and both desired and unacceptable functionality, so that an auditor can thoroughly examine and verify that the contracts behave only as expected.

Ultimately, while an audit can enhance project quality and security, the development team bears the ultimate responsibility for ensuring high quality. Following best practices in code development, testing, and documentation can make the audit process efficient and effective, improve project quality, and ensure a successful audit.

A good resource for writing clean code  →

What does a typical Quantstamp audit look like?

Any audit will begin with a project contacting and engaging an audit company. Quantstamp uses  an intake form on our website to gather information about the project, github repo, desired timeline, specifications, test cases, contacts etc. The form kicks off the scoping process which involves Quantstamp engineers scoping the code base to assess the complexity and amount of code and reviewing the provided documentations and test cases. It is critical at this juncture to make sure that the scopers have as much information as possible in order to accurately scope the code base.

At this point a business development representative is assigned to each project to manage communications, assessments, cost and timeline proposals, and more. Upon completion of a scope, we will then communicate a quote for the audit, with a tentative schedule and price and answer any questions a client may have.

We strongly recommend you start engaging with audit companies as early as possible, with a reasonable lead time before the audit is scheduled to start. Scoping can be done with code developed to a reasonable level of completion (~85-90% complete), so it’s better to establish a communications channel ahead of time!

The audit process

At Quantstamp, we believe in positive redundancy and allocate a minimum of three audit engineers on every project.  Many firms only allocate one or two auditors so we are quite unique in that sense.

Each auditor will conduct their own code review, using a mixture of manual auditing as well as using proprietary tooling.  During the audit, this team frequently compares notes dn strategies as well as notifying the client immediately of any serious issues.

Once each auditor team has conducted the full review, they collate their findings into a preliminary report. The findings are categorized based on the severity of the issue, with our audit team also documenting potential ways to fix or mitigate them.

The auditors create a Slack channel with the project team and there is ongoing communication from engineers with client engineers for questions and clarifications, which give Quantstamp audits a true sense of collaboration.  When the preliminary findings are shared confidentially with the project, and the team does a code walk through with the client to talk through the issues that were found.

There is then a period time (usually two weeks) given for the project to fix vulnerabilities and address the findings contained within the initial report. On completion of fixes, the audit team will conduct a ‘Fix Review’ in order to check the changes that have been made. The original issues will be marked ‘Fixed,’ ‘Mitigated,’ ‘Acknowledged’ or left as ‘Unresolved.’

The audit report is updated and a final version delivered to the project. Often our reports are made public by the project themselves, but public reports can also be viewed (and also verified) on our website. A quality audit report can help a project get listed on a centralized exchange or bring in investments.

Security consultation, community-driven security efforts

Quantstamp works with many projects on many different levels. Security audits can also evolve into longer-term security consultation engagements where we provide resources on a periodic basis.

Other ways to add extra sets of security-minded eyes to a project is to establish bug bounty programs. These also aid community building/engagement efforts while stressing a project’s commitment to security.

Lastly, once a project is live it is important to enable a real-time monitoring system (e.g. Hypernative) to monitor and detect malicious attacks that can happen well after initial deployment. Attacks against a certain project can take advantage of anomalies in governance, liquidity, property thresholds and more, which are often outside of the smart contract functionality, but can be detected by such a monitoring solution.

Part II – Audit Readiness

1. Project Specification Checklist

Good documentation is essential for a successful audit as it enables auditors to quickly onboard the project and have a reliable reference when needed. Additionally, clear and concise documentation not only helps with the audit process but also allows the wider community to understand and engage with the project. Therefore, investing time and effort into creating comprehensive documentation can benefit the project both during and beyond the audit.
Summarize what your project is intended to do and what features it has (e.g., token, staking, governance). Include appropriate references, files, and documents (e.g., unit tests, requirements, specifications, white paper).
State any assumptions, design decisions, and non-standard practices. For example, indicate that there is a trusted owner or whether you trust external oracles.
If the system is NOT intended to be completely trustless, document who the trusted actors are and what they should be trusted with. What are the expectations of parties? What are the actions that each party can perform?
Document who the main users and stakeholders are. What kind of applications and users will be interacting with the project?
List all outside libraries or resources that your project relies on. State any assumptions for the external contracts or configuration, e.g., “only integrating with ERC20 tokens without the callback functionality (no ERC777)”, or “only integrating with ERC20 tokens with 18 decimals”.
Document the functional and non-functional requirements of the smart contracts.

2. General Code Checklist

Code hygiene and wherever possible implementing best practices is another key feature of great projects. Follow common conventions of the specific language - for example the solidity style guide is outlined here.
Use the latest major compiler version.
Use established and well-tested libraries whenever possible (e.g., OpenZeppelin for Solidity smart contracts).
Ensure that the code compiles without any warnings and errors. Provide the exact instructions for building the project and include any implicit dependencies and their versions (e.g., the npm version).
Document all functions inline. Use NatSpec documentation for public and external functions.
Ensure that both code and documentation are in English.
Run the code through a spellchecker.
Run a static analyzer (e.g., Slither for Solidity) and review its output. Although these tools often raise flags for non-issues, they can sometimes catch a low-hanging fruit (e.g., ignored return value).
Ask an experienced code reviewer, (preferably external to your project) to sanity check your code and to get an early feedback.

3. Solidity Specific Checklist

There are several Solidity best practices that should be checked such as limiting the amount of Ether that can be stored in a contract, and adhering warnings from the compiler, and keeping the code base as small as possible to reduce complexity.
Any public function that can be made external should be made external. This is both to save the gas and to reduce the possibility of bugs since external functions cannot be accessed internally.
Avoid using assembly code unless absolutely necessary. The use of assembly increases audit times as it removes Solidity's guardrails and must be checked much more carefully.
Document the use of unchecked. Describe why it is safe to skip arithmetic checks on each code block. Preferably for each operation.
Follow the Checks-Effects-Interactions pattern (possibly with a combination of reentrancy guards) to avoid reentrancies. Treat all asset transfers as “interactions”.

4. Test Checklist

Testing is crucial for developing high-quality projects and reducing bugs. Both happy path and edge cases should be tested to ensure the code can handle different scenarios. While high coverage does not guarantee quality, low coverage suggests low quality. We suggest aiming for at least 80% coverage, with good quality projects aiming for 90+% branch coverage. Prioritizing testing and quality practices can improve code reliability and quality, save time and costs during audits, and allow auditors to focus on complex issues. It's recommended to prioritize testing to ensure the project's success and a successful audit.
Ensure that the code comes with an extensive test suite. Tests help to express your intent and to assure code quality.
Provide step-by-step instructions for running the test suite. Include any necessary information related to the setup and environment.
Have tests for all "happy path" user stories. All tests should be passing.
Test access controls and paths for all the roles, such as owners and non-owners.
Write negative tests. E.g., if users should NOT be able to withdraw within 100 blocks of depositing, then write a test where a user tries to withdraw early and make sure the user's attempt fails.
Measure test coverage (e.g., with solidity-coverage for Solidity) with 100% coverage as a goal. Try to  ensure at least 90% coverage in each category (statements, functions, branches). Note, however, that optimizing for 100% coverage does not automatically imply a high quality test suite.
If you rely on external protocols, implement tests with a mainnet fork so that the integration points are tested.

5. Audit Scoping Checklist

Identify the target date of audit completion and any reasons for such timing (e.g., commitment to investors).
Provide the location of your source code (e.g., GitHub) with the commit hash to be audited and make sure they are accessible to the auditors.
Please be sure to specify files or folders to be audited if it is not the entire repository.
Indicate if the code is currently deemed production-ready by the company. Have all previous reviews been considered and fixes implemented?
Indicate if you need the finalized audit report to be released under your company name, subject to confidentiality terms, or any other special requirements.