Solidity state variables and functions should have visibility defined, along with their declaration. This visibility defines how a function and a state variable will be accessible from within the contract, as well as from outside of the contract.
There are four different visibility modifiers, that is, public, external, internal, and private, present in Solidity:
- public: A function defined as public is accessible from both inside and outside of the contract. Solidity generates a getter function for public state variables, and these are also accessible from outside of the contract via these getter functions.
- external: A function defined as external is only accessible from outside of the contract. Internally, the function will not be accessible directly. To access an external function within the same contract, you would have to call this.functionName(). For state variables, you cannot use the external keyword.
- internal: A function or variable defined as internal is only accessible internally within the contract. Also, if Contract X inherits from Contract Y, which has internal functions or variables, Contract X can access all of Contract Y's internal functions and variables. When no visibility is specified, the variable would, by default, take internal visibility.
- private: A function or variable defined as private is only accessible internally within the same contract. A private variable or function is not accessible in derived contracts.
The following table explains different visibilities and how they apply to state variables and functions:
Visibility | Applies To | Accessibility |
public | Both state variables and functions | Within the contract and outside of the contract |
external | Only functions | Only from outside of the contract |
internal | Both state variables and functions | Internally, also in derived contracts |
private | Both state variables and functions | Internally, only within the same contract |
To understand this better, let's look at the following example:
contract SuperContract {
uint internal data;
function multiply(uint _a) private pure returns (uint)
{ return _a * 2;}
function setData(uint _a) internal { data = _a; }
function externalFn() external returns (uint) { /*...*/ }
function publicFn() public returns (uint) { /*...*/ }
}
contract VisibilityExample is SuperContract {
function readData() public {
//Following commented calls: error: not accessible
//uint result = multiply(2);
//externalFn();
//Following calls: Allowed access
data = data * 5; //variable accessible
setData(10); //function accessible
this.externalFn();
publicFn();
}
}
//Contract accessing VisibilityExample contract
contract ExternalContract {
VisibilityExample ve = VisibilityExample(0x1);
function accessOtherContract() public {
//Following commented calls: error: not accessible
//ve.setData(10);
//ve.multiply(10);
//Following calls: Allowed access
ve.externalFn();
ve.publicFn();
ve.readData();
}
}
As you can see, ExampleContract can call the externalFn(), publicFn(), and readData() functions.
Let's understand the visibility and accessibility by looking at preceding example. The following examples are shown in example code that follows in this chapter:
- The VisibilityExample contract inherits from SuperContract. Refer to the Contract inheritance section of this chapter for more information.
- The state variable data present in SuperContract is also accessible in VisibilityExample and can be modified directly.
- The multiply() function is not accessible in the VisibilityExample contract, as its visibility is private. This function is only accessible inside SuperContract.
- The externalFn() function is not accessible directly inside the VisibilityExample contract. However, you can access it by using this.externalFn().
- ExternalContract is deployed separately from the VisibilityExample contract. This means that both of the contracts have different contract addresses. However, ExternalContract can call the functions of the VisibilityExample contract.
Next, let's have a look at getter functions for state variables.