Understanding Visibility in Solidity

When using Solidity, it's crucial to know the different visibility specifiers to create efficient and secure smart contracts. In this blog post, we will look into visibility specifiers in Solidity—public, private, internal, and external—and talk about what they mean with a clear example.

What is Visibility in Solidity?

Visibility in Solidity decides where and how you can access functions and state variables in a contract. Each specifier has a specific use:

  1. Public: Can be accessed everywhere, including:

    • Outside the contract (through the blockchain or other contracts).

    • Inside the contract itself.

    • Inherited contracts.

    • Other contracts using an instance of the contract.

  2. Private: Can be accessed only within the contract where it is defined.

  3. Internal: Can be accessed within the contract and in inherited contracts.

  4. External: Can be accessed outside the contract but not inside the contract itself.

Visibility Table Overview

Here’s a quick reference table summarizing the accessibility of each specifier:

Let’s see how this table translates into code.


Solidity Example: Understanding Visibility

The following contract demonstrates the usage of visibility specifiers in Solidity.

// SPDX-License-Identifier: MIT  
pragma solidity >=0.7.0 <0.9.0;  

contract demo {
    //public function
    function f1() public pure returns(uint){
        return 1;
    }
    //private function
    function f2() private pure returns(uint){
        return 2;
    }
    //internal function
    function f3() internal pure returns(uint){
        return 3;
    }
    //external function
    function f4() external pure returns(uint){
        return 4;
    }
}

// Derived contract from demo contract
contract child is demo {
    uint public x = f3();
}

// Other contract 
contract otherContract {
    demo obj = new demo();
    uint y = obj.f4();
}

Key Observations

1. Public Functions

  • Public functions are accessible everywhere, including directly via Remix IDE or other external tools.

  • In the example above, both f1() and f4() are visible when the Demo contract is deployed.

2. Private Functions

  • Private functions like f2() are accessible only within the same contract.

  • Attempting to call f2() from the Child or Other contract results in a compilation error.

3. Internal Functions

  • Internal functions like f3() can be accessed within the contract itself and derived contracts (e.g., the Child contract).

  • They are not accessible from an external contract instance (e.g., Other contract).

4. External Functions

  • External functions like f4() can be accessed by other contracts or external tools but cannot be invoked from within the same contract.

Pro Tips for Mastering Visibility

  • Use private for functions or variables that should not be visible outside the contract.

  • Use internal for variables or functions that need to be shared with derived contracts.

  • Use public for functions that should be accessible everywhere, including from outside.

  • Use external when a function is meant to be called from outside the contract, but not from within it.


Conclusion

Understanding visibility in Solidity is important for creating smart contracts that work well and are safe. By selecting the right visibility specifier, you can control how and where your contract's functions and variables can be accessed.