Gas Optimization In Solidity: Strategies For Cost-Effective Smart Contracts
Gas is the “fuel” that powers smart contract execution. This article offers practical strategies for Solidity gas optimization.
🇺🇦 Hacken stands with Ukraine!Learn more
Crypto projects should realize the scope of risks associated with smart contracts and know how to address them. The most common threats related to smart contracts are operational, implementation, and design risks.
No changes can be introduced into a smart contract once it is deployed.
Most developers need guidance on how to write secure and high-quality smart contracts.
The value of Hacken audit methodology for developers:
Smart Contract Audit is the most robust security solution for the development phase and contract upgrades.
One of the key competitive advantages of Hacken is our transparency. In this material, we would like to share with you a brief overview of the Hacken smart contract audit methodology. Clients should be fully aware of the score of services they get when applying for a smart contract security audit.
Preparation for an audit is as necessary as the audit itself.
During the preparation, customers review their projects to make sure everything they have planned is correctly implemented. At this stage, clients themselves can detect many issues.
To get maximum value from the audit, clients should make the following:
Answer the following question: “What do we want to achieve via these smart contracts?”
Functional requirements need to be clear, simple, and unambiguous.
User stories can be useful as well.
Information about the used languages and technologies, deployment instructions, instructions on how to run tests, etc. Non-functional requirements can also be included in this section.
Setup development environment of the project
A development environment for the project should be configured by the Customer. Truffle, Hardhat, or any other comprehensive development environment can be used. A customer should provide a proper description of the environment. An example of the project environment configuration can be found here.
The code should be covered with unit tests. Test coverage should be 100%, with both positive and negative cases covered. Test cases that imitate the usage of contracts by multiple users should be provided to ensure that contracts will not be vulnerable to DoS attacks and that operations of one user do not affect the other unless it is a part of the requirements. The test should be executed on the emulated VM without the need to run local VM’s or connect to testnets.
We recommend using TDD during the development process.
The Customer should ensure that the code is compliant with standards provided by the Ethereum Enterprise Alliance.
Code review and analysis are performed by 2 separate auditors who don’t share results with each other during the initial phase of an audit. Additional review is performed by a lead auditor. All steps of this process:
2 auditors read the code to understand its structure and purpose and examine functional and technical requirements as well as other documents provided by clients. The main outcome of this step is a high-level description of the code provided by auditors that is used internally during the next steps of the audit.
Scan by automated tools
Automated tools are used to search for simple issues and find more information about smart contracts under review. The following tools are used:
Funds and data flow diagrams
Auditors visualize all possible states of the contract and all its interactions with other contracts. All changes in data and funds flow should be displayed on those diagrams.
During this step, some issues can be found and recorded by auditors and they can better understand smart contracts before line-to-line review.
BPMN and UML diagrams are used to visualize funds and data flows
Auditors thoroughly read each line of the code and record issues such as the ones described in SWC. We also look for possible data manipulations, access violations, flash loans, and other manipulations that can be performed through the interaction with other contracts. This step also includes validation of the code according to code-style guides and best practices.
Auditors provide verification of the following items:
|1||Default Visibility||SWC-100SWC-108||Functions and state variables visibility should be set explicitly. Visibility levels should be specified consciously.|
|2||Integer Overflow and Underflow||SWC-101||If unchecked math is used, all math operations should be safe from overflows and underflows.|
|3||Outdated Compiler Version||SWC-102||It is recommended to use a recent version of the Solidity compiler.|
|4||Floating Pragma||SWC-103||Contracts should be deployed with the same compiler version and flags that they have been tested thoroughly.|
|5||Unchecked Call Return Value||SWC-104||The return value of a message call should be checked.|
|6||Access Control & Authorization||CWE-284||Ownership takeover should not be possible. All crucial functions should be protected. Users could not affect data that belongs to other users.|
|7||SELFDESTRUCT Instruction||SWC-106||The contract should not be self-destructible while it has funds belonging to users.|
|8||Check-Effect- Interaction||SWC-107||Check-Effect-Interaction pattern should be followed if the code performs ANY external call.|
|9||Assert Violation||SWC-110||Properly functioning code should never reach a failing assert statement.|
|10||Deprecated Solidity Functions||SWC-111||Deprecated built-in functions should never be used.|
|11||Delegatecall to Untrusted Callee||SWC-112||Delegatecalls should only be allowed to trusted addresses.|
|12||DoS (Denial of Service)||SWC-113SWC-128||Execution of the code should never be blocked by a specific contract state unless it is required.|
|13||Race Conditions||SWC-114||Race Conditions and Transactions Order Dependency should not be possible.|
|14||Authorization through tx.origin||SWC-115||tx.origin should not be used for authorization.|
|15||Block values as a proxy for time||SWC-116||Block numbers should not be used for time calculations.|
|16||Signature Unique Id||SWC-117SWC-121SWC-122EIP-155||Signed messages should always have a unique id. A transaction hash should not be used as a unique id. Chain identifier should always be used.|
|17||Shadowing State Variable||SWC-119||State variables should not be shadowed.|
|18||Weak Sources of Randomness||SWC-120||Random values should never be generated from Chain Attributes or be predictable.|
|19||Incorrect Inheritance Order||SWC-125||When inheriting multiple contracts, especially if they have identical functions, a developer should carefully specify inheritance in the correct order.|
|20||Calls Only to Trusted Addresses||EEA-Level-2 SWC-126||All external calls should be performed only to trusted addresses.|
|21||Presence of unused variables||SWC-131||The code should not contain unused variables if this is not justified by design.|
|22||EIP standards violation||EIP||EIP standards should not be violated.|
|23||Assets integrity||Custom||Funds are protected and cannot be withdrawn without proper permission.|
|24||User Balances manipulation||Custom||Contract owners or any other third party should not be able to access funds belonging to users.|
|25||Data Consistency||Custom||Smart contract data should be consistent all over the data flow.|
|26||Flashloan Attack||Custom||When working with exchange rates, they should be received from a trusted source and not be vulnerable to short-term rate changes that can be achieved by using flash loans. Oracles should be used.|
|27||Token Supply manipulation||Custom||Tokens can be minted only according to rules specified in a whitepaper or any other documentation provided by the customer.|
|28||Gas Limit and Loops||Custom||Transaction execution costs should not depend dramatically on the amount of data stored on the contract. There should not be any cases when execution fails due to the block gas limit.|
|29||Style guide violation||Custom||Style guides and best practices should be followed.|
|30||Requirements Compliance||Custom||The code should be compliant with the requirements provided by the Customer.|
|31||Environment Consistency||Custom||The project should contain a configured development environment with a comprehensive description of how to compile, build and deploy the code.|
|32||Secure Oracles Usage||Custom||The code should have the ability to pause specific data feeds that it relies on. This should be done to protect a contract from compromised oracles.|
|33||Tests Coverage||Custom||The code should be covered with unit tests. Test coverage should be 100%, with both negative and positive cases covered. Usage of contracts by multiple users should be tested.|
|34||Stable Imports||Custom||The code should not reference draft contracts, that may be changed in the future.|
Each item can have one of the following statuses: Passed, Failed, Not Related.
If unit tests are configured for the project, auditors check coverage and write their own test cases if required. Ideally, code coverage should encompass all positive and negative cases. Some issues can be reproduced with the invalid config of dependency contracts. Auditors create such configs and run corresponding tests.
If unit tests are not configured, auditors deploy smart contracts on the local network and check all required cases.
Some sophisticated issues require complex exploiting contracts. Usually, if it is unclear how the issue can be exploited, auditors provide the sample attacking contracts.
Both auditors review their findings and audit artifacts and share results with each other. All disputable points in the code are discussed internally with the team. Auditors prepare materials for the report. Lead auditor reviews all materials prepared by the two other auditors and performs his own code review. Most possible issues are already documented at this step.
After all code review, analysis, and tests, auditors prepare a report with the following structure:
All issues are reported to the Customer in a draft report. If some points are unclear, we can provide more descriptions of issues or explain everything on a call.
The customer can receive one free remediation check when:
After all fixes are validated, the final report is provided to the customer.
The score contains several metrics:
Each metric has its own weight in the total score. The score has a decimal value with 1 decimal point. The score may vary between 0.0 and 10.0.
Maximum weights of each metric:
The project code should be provided with corresponding documentation. Functional and technical requirements are required.
Functional and technical requirements’ total weight is 5.0 each. The weight in the overall score is 1.0.
The code should follow official language style guides and be covered with unit tests. If most of the code follows those guides, the score is 3. Partially follows – 2.5. Not follows – 0.0.
Test coverage has a maximum weight of 7.
The weight in the overall score is 1.0.
Smart contracts of the project should follow the best practices.
Clean and clear architecture and well-configured development environment – 10.
The weight in the overall score is 1.0.
The most important metric of the score.
Issues have 4 levels of severity: critical, high, medium, and low. Only actual issues are considered in the score calculation. Actual issues have the following statuses: “new” and “reported” (previously “acknowledged”).
Each critical, high, and medium issue decreases the score:
The minimum score is 0, maximum is 10.
The weight in the total score is 7.0.
The total score calculation formula is D+C+A10+S710
Overall, a smart contract audit is an essential form of security testing for Web 3.0 projects allowing them to win users’ trust and confidence that their assets are in safety. Hacken smart contract audit methodology has been developed with regard to the deep expertise of our security engineers. We keep on improving this methodology to deliver a better quality of security services to clients. Only when cybersecurity grows faster than malicious hacking, then we can say “Web 3.0 is becoming a safer place”.