Estimated time: 3 minutes read
What are the best practices in Solidity?
Best practices in Solidity are a set of recommended rules, criteria, and programming patterns for developing secure and reliable smart contracts.
Since smart contracts are inherently immutable and often involve managing some type of crypto asset, it’s essential to ensure a good initial design.
How to comply with best practices in Solidity?
The criteria are very broad and generally depend on the type of smart contract being developed. Moreover, with the evolution of blockchain development, it is expected that they will change and adapt over time.
However, there is a basic set of best practices that could serve as a starting point for a deeper analysis.
1. Follow a style guide when writing source code
This involves coding contracts according to style conventions accepted by the community. For example, the official Solidity style guide can be reviewed.
2. Control of compiler versions
Define the minimum version of the compiler at the beginning of the contract using the “pragma” directive to prevent incompatibilities and avoid attacks from older versions of Solidity.
3. Optimization of gas consumption
This means programming smart contracts following guidelines that reduce gas consumption, both in deployment and execution.
However, it is essential to find a balance between clean, maintainable code that also optimizes gas consumption.
My article on gas optimization in Solidity explores several common techniques.
4. Error handling
- Use require, revert, and assert to validate external conditions or results during calls to other contracts using call, delegatecall, and staticcall.
- Return Custom Errors (available since Solidity 0.8) to notify external parties of errors and facilitate their handling from DApps and calling contracts.
- Manage exceptions in try/catch blocks when it is necessary to interrupt the propagation of errors.
5. Use of events
Register actions of interest during the execution of the contract using events to trace its behavior with minimal gas expenditure.
In my article on Solidity events, I detail how to create, debug, and access their information.
6. Integration of reputable contract libraries
Reuse open-source contracts that have been audited and offer community-backed guarantees, such as those provided by OpenZeppelin.
7. Access Control
Develop access control policies that restrict permissions in contract calls. Numerous alternatives exist, such as the Ownable or AccessControl contracts from OpenZeppelin, to limit certain functions to administrators or authorized users.
8. Security in Ether handling
In my article on how to send Ether from a smart contract, it concludes that the current consensus is to use the call function and prevent potential reentrancy attacks through programming patterns that modify the state before making calls to external contracts (Checks-Effects-Interactions) and using ReentrancyGuard.
9. Protection against common attacks
In a subsequent article, I will address the most common errors, design patterns to avoid them, and their detection through vulnerability analysis tools.
10. Plan for contract upgrades
To correct errors or incorporate new features, design the contract with future upgrades in mind can be useful. Some of the main contract upgrade patterns can be followed: proxy pattern, eternal storage, data separation, dispatcher, diamond…
It is also advisable to use trusted third-party libraries to support the implementation of the chosen pattern. An example would be Proxies from OpenZeppelin.
11. Use a Code Linter
As discussed, there are numerous criteria and patterns that form the core best practices for programming Smart Contracts in Solidity.
While ensuring their implementation is primarily the programmer’s responsibility, there are tools that automate compliance with certain rules, helping to detect potential errors.
This section would include code linters, such as Solhint, which is covered in detail in my article on formatting and linting Solidity code.
12. Intensive testing
There are numerous ways to detect and correct errors before deploying contracts in production: unit testing, static analysis, dynamic analysis, mutation testing, fuzzy testing…
The final step could involve security audits performed by third parties or launching bug bounty programs to attract the community of security-focused developers.
Conclusion: Programming secure and reliable Smart Contracts
Programming Smart Contracts is not just about implementing a specific business logic or protocol on the blockchain. Given their immutable nature and the management of valuable digital assets, it is essential to follow a set of rules and criteria to ensure, as much as possible, the security and efficiency of the deployed code, generating trust among the developer and user community.
The set of best practices encompasses various aspects, from style conventions in source code writing to optimizations in gas consumption, secure Ether transfers, intensive testing…
To support and assist developers in their implementation, there are many auxiliary tools I will show you in next articles.
Do you regularly follow best practices and criteria in your Smart Contract programming? Do you have any guide you can share? Do you use any tools that make the work easier? I invite you to share your experiences.
Contact me if you are looking for a Smart Contract developer accustomed to following best practices in their development. I can collaborate on your project.