Top 10 Common Solidity Issues

Updated at: July 30, 20247 Mins Read

Author: QuillAudits Team

Introduction

Ever wondered what a parrot, platypus, and kangaroo have in common? It’s ‘Homoio-thermy’. They maintain their internal body temperature, just like auditing maintains the safety of smart contracts.

However, recent events have shown that smart contract security is not infallible. Despite improvements in smart contract security, vulnerabilities still lead to significant financial losses.

In this blog, we will explore the top 10 common errors in Solidity programming. These errors are often the root cause of vulnerabilities that can lead to costly exploits. Understanding these pitfalls and knowing how to address them is crucial for any developer working with smart contracts.

Let’s dive into these common errors and learn how we at QuillAudits tackle them to ensure the security & reliability of your smart contracts.


top Solidity Issues

Top 10 Common Solidity Issues


1. Unchecked External Call is like a Silent Killer

Unchecked external calls are a common issue in Solidity. The call() and send() functions are often used to interact with external addresses.

While transfer() is generally preferred for sending Ether due to its simplicity and safety, call() and send() are still widely used for their versatility.

However, both call() and send() return a boolean value indicating whether the call was successful.

If this return value isn’t checked, it can lead to silent failures that might compromise the contract's security.

Unchecked External Call

Here, the pitfall for the contract exists where send is used without checking the response.

A failed send transaction doesn’t revert the payedOut state.

As a result, any attacker can exploit this by calling the withdrawLeftOver function to withdraw the remaining balance.

 

Check before you send!

We’ve seen this error too many times.

Instead of send, we use transfer which reverts on failure.

Here’s how we’d fix the code:

By replacing send with transfer, the contract automatically reverts if the transaction fails, ensuring payedOut is only set to true on success.

unchecked2.webp

 

2. Re-Entrancy attacks are the DAO Hack’s Best Friend

Ethereum smart contracts often interact with external contracts.

This requires them to make external calls. These calls are vulnerable to attacks, as seen in the infamous DAO hack.

Attackers exploit this vulnerability when a contract sends ether to an unknown address. The attacker can create a contract at that address with malicious code in the fallback function.

When the original contract sends ether, the malicious code is triggered, potentially compromising the contract.

Re-entrancy attacks exploit external calls in contracts. When a contract sends ether to an address with malicious code, it can re-enter the vulnerable contract and manipulate state variables.

Here, the transfer of ether is vulnerable. An attacker can create a malicious contract that re-enters the EtherStore contract and drains funds.

3

Lock the Door behind you

Re-entrancy attacks can be tricky, but we’ve got it covered.

First, we use transfer instead of call.value() for transfers.

Second, we move state changes before the external call.

4

By making these changes, we can prevent re-entrancy attacks effectively.

 

3. Default Visibilities are the Stealthy Bug

In Solidity, visibility specifiers determine how functions can be called—whether by external users, other derived contracts, or internally only. Incorrect use of these specifiers can lead to significant vulnerabilities in smart contracts.

By default, functions are public, meaning anyone can call them if no visibility is specified. This becomes a problem when developers forget to specify visibility for functions that should be private or internal. For example:

5

In this example, the _sendWinnings function is public by default, allowing anyone to call it and steal funds.

 

Make sure to Lock Down these functions

We always make visibility explicit. It’s a simple but crucial fix:

6

By setting _sendWinnings to private, we ensure it can only be called within the contract.

The visibility of the functions should be specified explicitly, even if they are to be kept public, it should be mentioned.

 

4. Get Your Constructor Names Right

Constructors are special functions used to perform critical and privileged tasks during the initialization of contracts. Before Solidity v0.4.22, constructors had the same name as the contract itself. However, if the contract name is changed during development but the constructor name remains unchanged, this loophole can give attackers easy access to your smart contract.

This mismatch can lead to severe consequences. For example:

7

In this example, a typo in the constructor name allows anyone to call it and become the owner.

 

Name it right, every time!

Here is the fix:

carbon

Using constructor ensures the initialization function can’t be called after deployment.

 

5. Tx.Origin Authentication Makes Phishing Easy

In Solidity, tx.origin is a global variable that contains the address of the account that originally initiated the call or transaction. Using this variable for authentication is dangerous, as it makes the contract vulnerable to phishing attacks.

Contracts that use tx.origin for user authorization are exposed to external attacks, potentially leading users to perform authenticated actions on malicious contracts. For example:

8

Here, authorizing withdrawAll with tx.origin makes the contract vulnerable to phishing.

 

Don’t Trust Tx.Origin

We avoid tx.origin for authorization and stick to msg.sender. Here’s how:

9

We generally avoid using tx.origin for authorization in smart contracts. Although the use of tx.origin isn’t strictly prohibited, it has some specific use cases. We can use tx.origin to deny external contracts from calling the present contract, it can be executed with require of the form require(tx.origin == msg.sender). It is done to avoid the calling of intermediate contracts to call the current contract which limits the contract to regular codeless addresses.

 

6. Overflow & Underflow: When Numbers Go Rogue

Integer overflow and underflow happen when arithmetic operations exceed the storage capacity of a data type, leading to unexpected results.

Underflow occurs when a value drops below zero, while overflow happens when it surpasses its maximum value. These vulnerabilities can cause unexpected behavior in smart contracts, potentially resulting in financial losses or system failures.

Consider a uint8 variable, which can hold a maximum value of 255. If you subtract 1 from a uint8 set to 0, it wraps around to 255 (underflow). Conversely, adding 1 to a uint8 set to 255 resets it back to 0 (overflow).

 

Example of Integer Overflow/Underflow Vulnerability

10

This is how you mitigate Integer Overflow/Underflow

With Solidity 0.8 and later, this concern has been addressed. The compiler automatically checks each arithmetic operation for overflow and underflow, throwing an error if detected.

For Solidity versions below 0.8, use the SafeMath library to prevent these issues. SafeMath provides functions to safeguard against overflow and underflow, ensuring the integrity of arithmetic operations in smart contracts.

By adopting these practices, you can safeguard your smart contracts from the pitfalls of integer overflow and underflow.

11

 

7. Keep Out! Access Control Matters

An access control vulnerability lets unauthorized users access or modify a contract’s data or functions. This happens when the code doesn't restrict access based on permissions. Such issues can affect governance and critical functions like minting tokens, voting, withdrawing funds, pausing/upgrading contracts, and changing ownership.

Common Access Control Vulnerabilities

  • Missed Modifier Validations: Missing validations in critical functions can compromise the contract or lead to fund loss.
  • Incorrect Modifier Names: Errors in modifier names can bypass controls, risking fund loss or unauthorized changes.
  • Overpowered Roles: Excessive privileges create vulnerabilities; follow the least privilege principle.

Example of Improper Access Control

12

Keep an eye on who you're giving access to

Grant only the minimum access levels needed for entities to perform their tasks – Principle of Least Privilege (PoLP). This principle prevents unauthorized parties from performing destructive actions, such as draining funds or altering critical variables.

Implement PoLP by using access control modifiers like onlyOwner from OpenZeppelin’s Ownable contract or role-based access control (RBAC) to define specific roles with distinct permissions.

Here is how you solve it:

13

In the code above, RBAC assigns user permissions based on predefined roles, providing granular control. It supports multiple roles, like onlyAdminRole and onlyModeratorRole, enhancing security by limiting access to necessary functions. OpenZeppelin’s AccessControl contract simplifies RBAC implementation in smart contracts.

 

8. Dodging the Front-Running Bullet

Front-running occurs when a malicious actor jumps ahead of a transaction by submitting a similar one with a higher gas fee. Since June 2020, Maximum Extractable Value (MEV) traders, employing bots, have racked up over $1 billion on Ethereum, BSC, and Solana, often at the expense of retail investors.

Three Types of Front-Running Attacks

  • Displacement: Using higher gas fees to prioritize their transaction.
  • Suppression: Flooding the network with high-fee transactions to delay others.
  • Insertion: Squeezing a victim’s transaction between two of their own to profit from price changes.

 

Dont get front-ran

Employ commit-reveal schemes to conceal bid details until after the bidding period.

14

To safeguard swapping applications, enforce a slippage restriction between 0.1% and 5%, depending on network fees and swap size. This minimizes slippage, protecting trades against front-runners who exploit higher rates.

 

9. Denial of Service (DoS) Attack

DoS attacks disrupt contract functionality by exploiting reverts, external call failures, and gas limit issues, rendering it inaccessible to legitimate users.

When a contract operation fails, it reverses changes. The EVM uses REVERT (0xFD) and INVALID (0xFE) opcodes to handle errors. REVERT returns remaining gas to the caller, while INVALID doesn’t return any gas.

DoS with Unexpected Revert Example

14

Gotta adopt pull over payment patterns

Adopt pull over push payment patterns to prevent DoS. The pull pattern shifts fund withdrawal responsibility to the recipient, preventing contract lockup due to failed transfers. A fully mitigated code should store pending withdrawals, enabling users to claim them.

16

These revisions aim to maintain clarity and conciseness while highlighting the key aspects of each vulnerability and its mitigation strategies.

 

10. Weak Randomness: Don't Gamble with Security

Generating random numbers on Ethereum is tricky due to its deterministic nature. Solidity relies on pseudorandom factors, and insecure methods can lead to exploitation.

Common Weak Random Generation Methods

  • Block-related Methods: Using block timestamps, difficulty, number, miner address, or block hash. These are insecure because miners can manipulate them.

17

Weak Randomness Mitigation

  1. Use Oracles: Employ external sources of randomness like Chainlink VRF, which provides verifiable random numbers.
  2. Commitment Schemes: Use commit-reveal approaches for applications like coin flipping, zero-knowledge proofs, and secure computation.
  3. Signidice Algorithm: Utilize cryptographic signatures suitable for PRNG in applications involving two parties.

By implementing these strategies, you can ensure secure and unpredictable random number generation in your smart contracts.

 

Wrap-Up

As we conclude our discussion on common pitfalls in Solidity, it’s crucial to remember that smart contracts are immutable once deployed.

This underscores the importance of thorough security testing and auditing before deployment.

At QuillAudits, we specialize in identifying and mitigating risks to ensure your contracts remain secure.

Not sure if your code is good to deploy or not?

Consider leveraging our QuillShield AI or our manual audit service to safeguard your project from vulnerabilities.

Protect your smart contracts with our expertise and stay confident in their safety and reliability.

Quillaudits-ai agents

Frequently Asked Questions

What resources are there to troubleshoot solidity issues?
Here are a few recommendations: - QuillShield AI - The official Solidity documentation - Ethereum Stack Exchange - Solidity Github - Remix IDE - Community forums & educational platforms - Dev tools & frameworks like Hardhat, Openzepplin & Truffle
How can I avoid Solidity Programming mistakes?
How do you handle errors in a smart contract?
Can a smart contract fail?
What are custom errors in Solidity require?
logo

Subscribe to our Newsletter

Get Pure Alpha Straight to Your Inbox. Miss this, and you’re missing out. Insider Secrets - Delivered Right to You. Subscribe now.

Telegram