How to Secure a Smart Contract through Solidity Visibility Modifiers?

By Said Habou Adi on August 8, 2022

In programming, modifiers are keywords that enable you to restrict access to a piece of code, be it a class, function or other data construct. 

Though there are several programming languages out there, and each programming language has its own take on visibility modifiers, the purpose of this article is to provide insight into how visibility modifiers work within Ethereum smart contracts written in Solidity and then to contrast the difference between modifiers of Ethereum smart contracts written in the Solidity and traditional Java visibility modifiers

What are Solidity Visibility Modifiers and How do they Compare with JAVA?

Solidity visibility modifiers and Java visibility modifiers are different in significant ways. 

Whereas a piece of Java code is executed within a self-contained Java runtime sitting on a single machine, Solidity’s runtime environment is the Ethereum blockchain. The unit of programming in Solidity is called a “smart contract”, which is roughly analogous to what Java calls a “class”. 

A smart contract written in Solidity resides within the Ethereum blockchain at a specific address. Once a contract is deployed to the Ethereum blockchain, the instance of the contract is indeed addressable via a unique blockchain address and you can view this instance of deployed contract at an address as what JAVA would call an “object”. Like classes and objects in JAVA, a Solidity “smart contract’ contains properties and methods. 

Access to a Solidity contract’s properties and methods is governed through visibility modifiers which govern how Ethereum wallets and other contracts on the Ethereum blockchain are able to access them. Visibility modifiers in Solidity can be attached either to the properties of the contract or the functions themselves. 

Solidity features the following visibility modifiers:

  • The Public visibility modifier allows any caller, be it external or an internal method, to have access to the property or method attached to the modifier.
  • The Private visibility modifier indicates that the property or function it is attached to is only callable by code within the same smart contract itself.  (Note: the ‘private’ modifier doesn’t restrict visibility into the value of a property on the blockchain, but only its accessibility within Solidity code) 
  • The Internal modifier is similar to private, except that it’s also accessible to child contracts that inherit from a parent contract. 
  • The External is similar to public, however it can only be attached to functions and it explicitly restricts access to it outside the contract.

You can attach external, public, internal or private visibility modifiers to Solidity functions, but you cannot add external to state variables. Internal is the default visibility level for Solidity states variables whereas public is the default visibility modifier for Solidity functions.

Here are the syntaxes for adding visibility modifiers to variables and functions respectively: 

  • Data type [visibility modifier] variableName;
  • function nameOfFunction(<parameter types>) [visibility modifier]  [pure|view|payable]  [returns (<return types>)]  {/* … */}.

Note: Unlike the parameter types, the return types cannot be empty. If a function does not return anything, the whole returns (<return types>) part has to be omitted.

Solidity Public and External Visibility Modifiers

Public and external visibility modifiers are slightly different: external cannot be attached to state variables and cannot be called by child contracts. When you add a public keyword to a state variable, a getter function is automatically generated by the compiler. The getter function and the variable will have the same name.

You can attach public and external keywords to variable and functions as follows:

  • Data type [public|external] variableName;
  • function nameOfFunction(<parameter types>) [public|external]  [pure|view|payable]  [returns (<return types>)]  {/* … */}

Example

Here is a code sample featuring public and external visibility modifiers:

  uint public count = 0;

    function incrementByOne() public {

        count++;

    }

    function lastElement() external view returns(uint) {

        return count;

    }

Solidity Private and Internal Visibility Modifiers

The only difference between private and internal visibility modifiers is that child contracts can call internal variables and functions. You can attach private and internal keywords to variable and functions as follows:

  • Data type [private|internal] variableName;
  • function nameOfFunction(<parameter types>)  [private|internal] [pure|view|payable]  [returns (<return types>)]  {/* … */}

Example

Here is a code sample featuring private and internal visibility modifiers:

contract StudentDB {

    string[] internal listOfStudent;

    function addNewStudent(string memory name) internal {

        listOfStudent.push(name);

    }

}

contract ManagingStudent is StudentDB {

    function addingStudentName(string memory name) public {

        addingStudentName(name);

    }

    function numberOfStudent() public view returns(uint) {

        return listOfStudent.length;

    }

}

Securing Contracts Using Visibility Modifiers

Your smart contract’s security depends on the visibility modifiers you attach to each piece of code. When securing your smart contract, you should first look for variables and functions that are public and external.

Next, you should analyze the logic of each public and external function to detect vulnerabilities. 

An example of vulnerability is a helper function that executes specific instructions for main functions. Hackers can bypass main functions with require statements by calling helper functions.

Once you detect the variables and functions that need restrictions, change their visibility modifiers to private if you want them to be called only in the contract in which they are declared, otherwise use internal if you think they should be accessible in deriving contracts. 

When you change visibility modifiers to private, each private function should follow the naming convention which starts with an underscore. As e.g: function _functionName() private { /*…*/};.

Conclusion

Solidity visibility modifiers play a key role in the security of Ethereum smart contracts. 

This article covered the differences between Solidity and Java visibility modifiers, Solidity’s take on visibility and syntaxes. 

Using Solidity visibility modifiers appropriately is a good practice for securing DApps and reducing gas fees.