
Understanding Solidity Visibility and Access Control
When a state variable or function is declared without a visibility modifier, Solidity defaults to public for state variables and external for functions. However, this default behavior may not always be secure, especially when sensitive data or restricted operations are involved. Proper use of visibility modifiers ensures that your contract is both secure and efficient.
This article will explore the different visibility levels, demonstrate their usage with code examples, and highlight best practices for access control in Solidity.
Visibility Modifiers in Solidity
Solidity provides four visibility modifiers:
| Modifier | Access Scope |
|---|---|
public | Accessible from any external address and internal to the contract. |
private | Accessible only within the contract that defines it. |
internal | Accessible within the contract and derived contracts. |
external | Accessible only from outside the contract, not from internal calls. |
Let’s look at an example to illustrate the use of these modifiers.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract VisibilityExample {
uint public publicVar = 100;
uint private privateVar = 200;
uint internal internalVar = 300;
function getPrivateVar() public view returns (uint) {
return privateVar;
}
function getInternalVar() internal view returns (uint) {
return internalVar;
}
function getInternalViaPublic() public view returns (uint) {
return getInternalVar();
}
function setPublicVar(uint newValue) external {
publicVar = newValue;
}
}In this example:
publicVaris publicly readable and can be modified by external contracts.privateVaris accessible only withinVisibilityExample.internalVaris accessible withinVisibilityExampleand any contract that inherits from it.- The
getInternalVarfunction isinternal, so it cannot be called from outside the contract. - The
getInternalViaPublicfunction ispublicand can be used to access theinternalVarvalue externally.
Best Practices for Access Control
Access control in Solidity is often implemented using modifiers to restrict function access. A common pattern is to use require with a condition and a custom error message.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract AccessControl {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
uint public restrictedData = 42;
function changeData(uint newData) public onlyOwner {
restrictedData = newData;
}
}In the AccessControl contract:
- The
onlyOwnermodifier ensures that only the contract owner can call thechangeDatafunction. - The
msg.senderis compared to the storedowneraddress. - If the sender is not the owner, the transaction is reverted with the message
"Not the owner".
This pattern is widely used in real-world applications to enforce role-based access control.
External vs Internal Functions
Understanding the difference between external and internal functions is key to optimizing gas usage and preventing vulnerabilities.
- External functions are called using the ABI and cannot be called directly from within the contract.
- Internal functions are more efficient and are used for internal logic.
Here's an example demonstrating the gas efficiency of internal functions:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract FunctionVisibility {
uint public counter;
function incrementExternal() external {
counter += 1;
logCounter();
}
function incrementInternal() external {
_increment();
}
function _increment() internal {
counter += 1;
logCounter();
}
function logCounter() internal view {
// Some internal logic
}
}In this contract, calling _increment directly from another internal function is more gas-efficient than using an external function call. Always prefer internal functions for internal logic.
Summary of Visibility and Control
| Modifier | Can be accessed externally? | Can be accessed internally? | Can be inherited? |
|---|---|---|---|
| public | ✅ | ✅ | ✅ |
| private | ❌ | ✅ | ❌ |
| internal | ❌ | ✅ | ✅ |
| external | ✅ | ❌ | ✅ |
Use the table above to quickly reference the correct visibility modifier based on your access requirements.
