Overview of Visibility Specifiers

Solidity provides four main visibility specifiers for functions and state variables:

  • public
  • private
  • internal
  • external

Each specifier has distinct characteristics that determine how and where they can be accessed.

Visibility Specifier Breakdown

SpecifierDescriptionCan be Accessed By
publicAccessible from anywhere, including other contracts and externally.Any contract or account
privateOnly accessible within the contract where it is defined.The contract itself
internalAccessible within the contract and by derived contracts.The contract and its descendants
externalCan only be called from outside the contract, not internally.Other contracts or accounts

1. Public Functions and Variables

Public functions and variables can be accessed from any context, making them versatile but potentially exposing sensitive data. When you declare a variable as public, Solidity automatically creates a getter function for it.

pragma solidity ^0.8.0;

contract PublicExample {
    uint public publicVariable = 10;

    function publicFunction() public view returns (uint) {
        return publicVariable;
    }
}

In this example, publicVariable can be accessed by anyone, and publicFunction() can be called from any external account or contract.

2. Private Functions and Variables

Private functions and variables are only accessible within the contract itself. This encapsulation is useful for protecting sensitive logic and data.

pragma solidity ^0.8.0;

contract PrivateExample {
    uint private privateVariable = 20;

    function getPrivateVariable() public view returns (uint) {
        return privateVariable;
    }

    function privateFunction() private view returns (uint) {
        return privateVariable * 2;
    }
}

Here, privateVariable can only be accessed within PrivateExample, and privateFunction() cannot be called from outside the contract.

3. Internal Functions and Variables

Internal functions and variables can be accessed within the contract and by derived contracts. This is particularly useful for creating base contracts that can be extended.

pragma solidity ^0.8.0;

contract InternalExample {
    uint internal internalVariable = 30;

    function internalFunction() internal view returns (uint) {
        return internalVariable;
    }
}

contract DerivedExample is InternalExample {
    function getInternalVariable() public view returns (uint) {
        return internalFunction(); // Accessing internal function from base contract
    }
}

In this case, internalVariable and internalFunction() are accessible in DerivedExample, allowing for code reuse.

4. External Functions

External functions are designed to be called from outside the contract. They cannot be called internally using the this keyword or directly.

pragma solidity ^0.8.0;

contract ExternalExample {
    uint private externalVariable = 40;

    function externalFunction() external view returns (uint) {
        return externalVariable;
    }
}

To call externalFunction(), it must be invoked from another contract or an external account. Internal calls to this function will result in an error.

Best Practices for Visibility Specifiers

  1. Minimize Visibility: Always use the least permissive visibility that meets your requirements. For instance, prefer private or internal over public unless external access is necessary.
  1. Use external for Heavy Functions: When functions are intended to be called externally and are expected to handle large inputs, use external as it can be more gas-efficient than public.
  1. Avoid Unintended Access: Be cautious with public variables, as they expose the state of your contract. If you want to control access, consider using private or internal with public getter functions.
  1. Document Visibility: Clearly document the intended visibility of functions and variables in your code comments. This helps other developers understand the access level and purpose.
  1. Testing: Always test visibility scenarios to ensure that functions and variables behave as expected in different contexts.

Conclusion

Understanding and correctly implementing visibility specifiers in Solidity is vital for developing secure and efficient smart contracts. By adhering to best practices and being mindful of access levels, developers can protect their contracts from unintended interactions and vulnerabilities.

Learn more with useful resources