Learn About the Four Types of Visibility Specifiers in Solidity
In Solidity, visibility specifiers define how and from where a function or variable can be accessed. There are four visibility levels:
1. public
Accessible everywhere:
Inside the contract.
Derived (inherited) contracts.
External accounts (users, dApps).
Other contracts.
Use Cases:
When you want a function or variable to be accessible by anyone, including external users.
For public state variables, Solidity automatically generates a getter function.
Example:
uint public num; // Auto-generated getter for this variable
function add(uint a, uint b) public pure returns (uint) {
return a + b;
}
2. private
Accessible only within the contract where it is defined.
Not accessible in derived (inherited) contracts.
Most restrictive visibility level.
Use Cases:
When you want to restrict a function or variable to the current contract only.
For internal logic or sensitive data that shouldn't be exposed to other contracts or external users.
Example:
contract Example {
uint private secret = 42; // Only this contract can access it
function getSecret() public view returns (uint) {
return secret; // Allowed within the same contract
}
}
contract Derived is Example {
// Cannot access `secret` directly
// uint mySecret = secret; // This will throw an error
}
3. internal
Accessible:
Within the contract where it is defined.
In derived (inherited) contracts.
Not accessible externally or by other contracts.
Use Cases:
- When you want to share logic or data between the contract and its derived contracts but prevent access by external entities or unrelated contracts.
Example:
contract Parent {
uint internal value = 10; // Accessible to derived contracts
function increment() internal view returns (uint) {
return value + 1; // Internal function
}
}
contract Child is Parent {
function useIncrement() public view returns (uint) {
return increment(); // Allowed in derived contracts
}
}
4. external
Accessible:
- By other contracts or external accounts (users, dApps).
Not accessible internally using direct calls.
- To call an
external
function internally, usethis.functionName()
.
- To call an
Use Cases:
Functions designed to interact with external entities.
Saves gas compared to
public
for external function calls.
Example:
contract Example {
function externalFunction(uint a) external pure returns (uint) {
return a * 2;
}
function callExternalFunction() public view returns (uint) {
// Using `this` to call an external function internally
return this.externalFunction(10);
}
}
Comparison Table:
Visibility | Accessible Within Contract | Accessible by Derived Contracts | Accessible Externally | Notes |
public | โ | โ | โ | State variables auto-get getter functions. |
private | โ | โ | โ | Most restrictive. |
internal | โ | โ | โ | Shared with derived contracts. |
external | โ (direct calls) | โ | โ | Use this for internal calls. |
Best Practices:
Use
public
for functions/variables meant for external interactions.Use
private
for sensitive logic or data.Use
internal
for shared logic/data across inherited contracts.Use
external
for functions primarily called from outside, especially if they donโt need internal access.