Solidity is an object-oriented, high-level programming language for implementing smart contracts on various blockchain platforms, most notably Ethereum. It was influenced by C++, Python, and JavaScript and is designed to target the Ethereum Virtual Machine (EVM).
Solidity Anti-Patterns Overview
Solidity, as the primary language for Ethereum smart contracts, has several common anti-patterns that can lead to security vulnerabilities, excessive gas costs, and maintenance issues. Here are the most important anti-patterns to avoid when writing Solidity code.
Reentrancy Vulnerabilities
Always follow the Checks-Effects-Interactions pattern to prevent reentrancy attacks. Update all internal state before making external calls, and consider using reentrancy guards for complex functions.
Unchecked External Calls
Always check the return values of low-level calls like send()
, call()
, and delegatecall()
. These functions return a boolean indicating success or failure, and ignoring this value can lead to silent failures.
Integer Overflow and Underflow
In Solidity versions before 0.8.0, always use SafeMath or similar libraries to prevent integer overflow and underflow. In Solidity 0.8.0 and later, arithmetic operations revert on overflow/underflow by default, but use unchecked
blocks carefully for gas optimization.
Improper Access Control
Implement proper access control for sensitive functions. Use modifiers to restrict access to authorized users, and consider using role-based access control for more complex applications.
Timestamp Dependence
Be cautious when using block.timestamp
for critical logic, as miners can manipulate it slightly. For applications requiring precise timing, consider using block numbers instead or be aware of the potential for small timestamp manipulations.
Improper Use of tx.origin
Avoid using tx.origin
for authorization, as it makes your contract vulnerable to phishing attacks. Use msg.sender
instead to verify the immediate caller of your contract.
Unbounded Loops
Avoid unbounded loops that iterate over arrays of unknown size. Use bounded loops with pagination to prevent out-of-gas errors, especially when the array size can grow indefinitely.
Incorrect Inheritance Order
Pay attention to the order of inheritance in Solidity. The order is from most derived (right) to most base (left), and super
calls are resolved from right to left. Incorrect inheritance order can lead to unexpected behavior.
Excessive Gas Consumption
Optimize your code for gas efficiency. Avoid unnecessary storage operations, minimize copying data to memory, and use gas-efficient patterns like pre-allocating array space. In Solidity 0.8.0+, use unchecked
blocks for simple arithmetic operations when overflow/underflow is not a concern.
Hardcoded Addresses
Avoid hardcoding addresses in your contracts. Use constructor parameters to initialize addresses and provide functions to update them if needed. This makes your contracts more flexible and easier to deploy to different environments.
Lack of Event Emissions
Emit events for important state changes in your contract. Events provide an audit trail and allow off-chain applications to track what’s happening on the blockchain. They are essential for building user interfaces and monitoring contract activity.
Improper Decimals Handling
Handle decimals properly in your contracts. Solidity doesn’t support floating-point numbers, so use integer math with appropriate scaling factors. Consider expressing rates as “X per Y” to avoid division, which can lead to rounding errors.
Unprotected Self-Destruct
Protect the selfdestruct
function with proper access control. An unprotected selfdestruct
can allow anyone to destroy your contract and steal its funds.
Incorrect Use of Cryptography
Don’t rely on block variables like block.timestamp
or block.difficulty
for randomness, as they can be manipulated by miners. For applications requiring randomness, use a commit-reveal scheme, oracles, or VRF (Verifiable Random Function) services like Chainlink VRF.
Lack of Input Validation
Validate all inputs to your functions. Check for zero addresses, valid ranges for numeric values, and other constraints specific to your application. Proper input validation prevents many common errors and vulnerabilities.
Not Using SafeERC20
Use the SafeERC20 library when interacting with ERC20 tokens. Some tokens don’t follow the standard correctly and might not return a boolean on transfer()
or approve()
calls, which can cause your transactions to revert unexpectedly.