Top 5 Common errors in Solidity programming Language

Top 5 Common errors in Solidity programming Language

Top 5 Common errors in Solidity programming Language

Top 5 Common errors in Solidity programming Language

Top 5 Common errors in Solidity programming Language

Table Of Content
Read Time: 7 minutes

What is common between Parrot, Platypus, and Kangaroo? Well, in case you don’t know, it’s ‘Homoio-thermy’. It’s a process to maintain their internal body temperature through metabolic processes just like auditing is the process to maintain the safety and security of smart contracts. 

But in the near past, we have witnessed various events that put a question mark on the security of smart contracts. We were subjected to vulnerabilities which resulted in huge financial loss as compensation. Though with time, the security of the Smart contracts has improved. But we should be cautious and be prepared for any potential threat. We believe the fact that the majority of these attacks take place due to a few common vulnerabilities in smart contracts. 

In the coming sections, we will introduce you to the 5 most commonly encountered errors in Solidity and the vulnerabilities associated with them. Hence, let’s deep dive and look into these loopholes and how we at QuillAudits approach them.

Errors in solidity programming language

1. Unchecked External Call

We are pulling this issue in the first place because it is one of the most commonly observed Solidity pitfalls. Generally, to send ether to any external account is carried out through the transfer() function. Apart from this, the two most widely used functions to make an external call are; call(), and send(), here mainly the call() function is extensively used to perform versatile external calls by the developers. 

Though the call() and send() functions return a boolean value specifying whether the call was a success or not. Thus in this case, if any of the functions call() or send() fails to perform the task, they will revert with a false. Hence, if the developer doesn’t cross-check the return value, it would become a pitfall. 

The Vulnerability

Consider the example below:

 contract Lotto{
 boolpublic payedOut =false;
    address public winner;
 uintpublic winAmount;
 // ... extra functionality here
 function sendToWinner()public{
 require(!payedOut);
        winner.send(winAmount);
        payedOut =true;
 }
 function withdrawLeftOver()public{
 require(payedOut);
        msg.sender.send(this.balance);
 }
 }

In the Lotto-like contract above, we can observe that a winner receives winAmount of ether leaving a little leftover to be withdrawn from any external agent. 

Here, the pitfall for the contract exists at line [11], where a send is used without cross-validation of the response. In the above example, a winner whose transaction fails (either by deficiency of Gas or if it’s a contract that intentionally throws in the fallback function), authorizes payedOut to be set to true irrespective of whether the transaction of ether was a success or not. In this event, any exploiter can withdraw the winner’s winnings via the withdrawLeftOver function. 

QuillAudit’s Approach

Our in-house team of developers tackles this bug with the use of [transfer] function instead of [send] function, as [transfer] will revert if external transaction reverts. And if you’re using [send], always cross-check the return value. 

One of the robust approaches that we follow is utilizing a [withdrawal pattern]. Here, we logically isolate the external send functionality from the rest of the codebase, and place the strain of potentially failed transactions on the end-user, as he is the one to call the withdraw function. 

2. Re-Entrancy

The Ethereum smart contracts call and utilize codes from other external contracts, and to conduct this, the contracts are required to submit external calls. These external calls are vulnerable and prone to attacks, one such attack took place recently in the case of a DAO hack. 

The Vulnerability

Attackers carry out such attacks when a contract sends ether to an unknown address. In this case, the attacker can create a contract at an external address that possesses malicious code in the fallback function, and this malicious code will be invoked when the contract sends ether to this address.  

Fact: The term ‘Reentrancy’ has been coined from the fact that when an external malicious contract calls a function over the vulnerable contract and then the code execution path ‘re-enters’ it. 

Consider the example below, it’s an Ethereum vault that allows depositors to withdraw only 1 ether per week. 

contract EtherStore {
uint256 public withdrawalLimit = 1 ether;
   mapping(address => uint256) public lastWithdrawTime;
   mapping(address => uint256) public balances;
   function depositFunds() external payable {
balances[msg.sender] += msg.value;
   }
   function withdrawFunds (uint256 _weiToWithdraw) public {
       require(balances[msg.sender] >= _weiToWithdraw);
       // limit the withdrawal
       require(_weiToWithdraw <= withdrawalLimit);
       // limit the time allowed to withdraw
       require(now >= lastWithdrawTime[msg.sender] + 1 weeks);
       require(msg.sender.call.value(_weiToWithdraw)());
       balances[msg.sender] -= _weiToWithdraw;
       lastWithdrawTime[msg.sender] = now;
   }
}

In the above contract, we have two public functions, [depositFunds] and [withdrawFunds]. The [depositFunds] is used to increment the sender’s balance, whereas [withdrawFunds] specifies the amount to be withdrawn. In this case, it will be a success if the amount to be withdrawn is less than 1 ether. 

The pitfall here lies in line [17] where the transfer of ether takes place. The attacker could create a malicious contract with [EtherStores]’s contract address as the only constructor parameter. This would make [etherStore] a public variable, hence more prone to be attacked. 

QuilllAudit’s Approach

We follow various techniques to avoid potential reentrancy vulnerabilities in smart contracts. The very first and the best possible way is the use of the built-in [transfer] function when transferring ether to any external contract. 

Secondly, it is important to ensure that all the logic changes in the state variables should be done before sending ether out of the contract. In the [EtherStore] example, lines [18] and [19] should be put before line [17]. 

A third technique can also be used to prevent reentrant calls; through the introduction of a mutex. It is an addition of a state variable that will lock the contract during code execution. 

3. Default Visibilities

There are visibility specifiers for the functions we use in Solidity, and they prescribe the way they can be called. It is the visibility that determines the calling of the functions; externally by users, by other derived contracts, only internally or only externally. Let us look at how erroneous use of visibility specifiers can cause huge vulnerability in smart contracts

The Vulnerability

By default, the visibility of the function is [public], hence the external users can call the functions with no specific visibility. The bug arises when developers forget to specify visibility on functions that should be private (or can be called within the contract itself). For example;

contract HashForEther {
   function withdrawWinnings() {
       // Winner if the last 8 hex characters of the address are 0
       require(uint32(msg.sender) == 0);
       _sendWinnings();
    }
    function _sendWinnings() {
        msg.sender.transfer(this.balance);
    }
}

The above contract is a simple address-guessing bounty game. In this, we can see that visibility of the functions is not specified, particularly the          [ _sendWinnings] function is [public] (by default), hence this can be called through any address to steal the bounty. 

QuillAudit’s Approach

Our in-house team consists of seasoned developers who always follow the best audit practices, here the visibility of the functions should be specified explicitly, even if they are to be kept public, it should be mentioned. 

4. Safeguarding the Use of Constructors

Generally, Constructors are called special functions that are used to perform critical and privileged tasks while initializing the contracts. Before Solidity [v0.4.22], constructors were holding the same name used by the contract that contained them. Now, consider a case where the contract name is changed during the development phase but the constructor name remains the same, this loophole can also provide attackers an easy entry to your smart contract.  

The Vulnerability

It can lead to severe consequences if the contract name is modified but the constructor’s name is unchanged. For example:

contract OwnerWallet {
   address public owner;
   // constructor
   function ownerWallet(address _owner) public {
       owner = _owner;
   }
   // Fallback. Collect ether.
   function () payable {}
   function withdraw() public {
       require(msg.sender == owner);
       msg.sender.transfer(this.balance);
   }
}

In the above contract, we can see that only the owner can withdraw ether via calling the [withdraw] function. Here, the vulnerability occurs as the constructor is named different from the contract (the first letter is different!). Thus exploiter can call [ownerWallet] function and authorize themselves as owner, and then withdraw all the ether in contract by calling [withdraw]. 

QuillAudit’s Approach

We comply with version [0.4.22] of the Solidity compiler. This version has introduced a keyword; [constructor] which requires the name of the function to match the contract name. 

5. Tx.Origin Authentication

Here, [Tx.Origin] is Solidity’s global variable, it contains the address of the account that originally executed the call or transaction. This variable can’t be used for authentication, as doing so makes the contract vulnerable to phishing attacks. 

The Vulnerability

Contracts authorizing users through [tx.origin] variable are exposed to external attacks leading users to perform authenticated actions on the erroneous contract. Consider the below example:

contract Phishable {
   address public owner;
   constructor (address _owner) {
       owner = _owner;
   }
   function () external payable {} // collect ether

   function withdrawAll(address _recipient) public {
       require(tx.origin == owner);
       _recipient.transfer(this.balance);
   }
}

Here at line [11], the contract authorizes [withdrawAll] function with the help of [tx.origin]. 

QuillAudit’s Approach

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.

Final Wrap-Up

We have comprehensively covered the five common pitfalls in the Solidity language. While developing smart contracts, we must not forget that they are immutable by design, which means that once we create them, there is no way to patch the source code. 

This poses a great challenge to developers to take the advantage of available security testing and auditing tools before deployment. 

Discovering potential malicious threats to the smart contracts, and the risks some of which we mentioned above are performed in a very unique and robust way by our in-house team of auditing experts. We at QuillAudits put our best efforts into security research to keep your contract updated with all the software security practices to keep your contract safe and secure.

5,749 Views

Blockchain for dog nose wrinkles' Ponzi makes off ~$127M🐶

Project promised up to 150% returns on investment in 100 days, raising about 166.4 billion South Korean won — or about $127 million — from 22,000 people.

Latest blogs for this week

Understanding Fuzzing and Fuzz Testing: A Vital Tool in Web3 Security

Read Time: 5 minutes When it comes to smart contracts, ensuring the robustness and security of code is paramount. Many techniques are employed to safeguard these contracts against vulnerabilities
Read More

How EigenLayer’s Restaking Enhances Security and Rewards in DeFi

Read Time: 7 minutes Decentralized finance (DeFi) relies on Ethereum staking to secure the blockchain and maintain consensus. Restaking allows liquid staking tokens to be staked with validators in
Read More

ERC 404 Standard: Everything You Need to Know

Read Time: 7 minutes Introduction Ethereum has significantly shaped the crypto world with its introduction of smart contracts and decentralized applications (DApps). This has led to innovative developments in
Read More

DNS Attacks:  Cascading Effects and Mitigation Strategies

Read Time: 8 minutes Introduction DNS security is vital for a safe online space. DNS translates domain names to IP addresses, crucial for internet functionality. DNS ensures unique name-value
Read More

EIP-4844 Explained: The Key to Ethereum’s Scalability with Protodanksharding

Read Time: 7 minutes Introduction  Ethereum, the driving force behind dApps, has struggled with scalability. High fees and slow processing have limited its potential. They have kept it from
Read More

QuillAudits Powers Supermoon at ETH Denver!

Read Time: 4 minutes Calling all the brightest minds and leaders in the crypto world! Are you ready to build, connect, and innovate at the hottest event during ETH
Read More

Decoding the Role of Artificial Intelligence in Metaverse and Web3

Read Time: 7 minutes Introduction  Experts predict a transformative shift in global software, driven by AI and ML, marking the dawn of a new era. PwC predicts AI will
Read More

Transforming Assets: Unlocking Real-World Asset Tokenization

Read Time: 7 minutes In the blockchain, real-world assets (RWAs) are digital tokens that stand for tangible and conventional financial assets, including money, raw materials, stocks, and bonds. As
Read More
Scroll to Top

Become a Quiffiliate!
Join our mission to safeguard web3

Sounds Interesting, Right? All you have to do is:

1

Refer QuillAudits to Web3 projects for audits.

2

Earn rewards as we conclude the audits.

3

Thereby help us Secure web3 ecosystem.

Total Rewards Shared Out: $200K+