Solidity is a high-level programming language specifically designed for developing smart contracts on the Ethereum blockchain. In Solidity, functions are a fundamental building block, allowing developers to define the behavior of a smart contract. Understanding the various types of functions in Solidity and the use cases of Solidity functions is crucial for creating secure and efficient smart contracts.
What is a Solidity Function?
In Solidity, a function is a piece of code that performs a specific task. These functions can be called by other contracts or external applications. Functions define the actions a smart contract can perform, and their proper design is crucial for the contract’s integrity and functionality. Solidity functions can be categorized into several types based on their purpose and visibility.
Functions are almost always associated with state or global variables. If you don’t know how Solidity variables work, make sure to check our latest article about them. And if you’ve come here from the article, then you’re at the right place!
Basic Syntax
Like any other programming language, functions have three key ingredients to them:
- A set of inputs
- An execution code
- An output
To define a function in a Solidity contract, you simply have to use the function
keyword followed by the desired function name. After that, you open a set of parentheses that will enclose any input your function will receive. That is proceeded by the function’s modifiers, one of which is the visibility modifier which needs to be specified. For now we will put it as public then we will touch on it later. And last but not least we have the function output.
A function doesn’t always need to have an input or an output, but it almost always has some logic to do.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Functions {
function funcWithoutInputsNorOutputs() public {
// ...
}
function funcWithInput(uint256 _input) public {
// ...
}
function funcWithOutput() public returns (uint256) {
// ...
return 0;
}
function funcWithInputAndOutput(uint256 _input) public returns (uint256) {
// ...
return 0;
}
}
Inputs & Outputs
Function inputs are declared just like regular variables, and we can have as many of them as we want.
To declare a basic input (uint256, bool, address…), we simply give the type and name of the input inside the function parentheses, and add a comma to insert another one.
Complex inputs on the other hand (string, array…), have to be additionally prefixed with the memory
or calldata
keywords. These tell the function where the variables will live throughout the life-cycle of the function call.
The difference between memory
and calldata
is that calldata
marks the variable as read only which means it cannot be modified within the function. If you don’t plan on making changes to your variables, its better to prefix them with calldata
as it makes the function call more efficient.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Functions {
function withBasicInput(uint256 _num, bool _bool, address _account) public {
// ...
}
function withComplexInput(
uint256[] memory _nums,
string memory _str
) public {
// ...
}
function withUnchangedComplexInput(
uint256[] calldata _nums,
string calldata _str
) public {
// ...
}
function withEverything(
address _account,
uint256[] memory _nums,
string calldata _str
) public {
// ...
}
}
Function outputs behave in a similar manner, but you can only specify one output at a time.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Functions {
function withBasicOutput() public returns (uint256) {
// ...
}
function withComplexOutput() public returns (uint256[] memory) {
// ...
}
// This is not a common pattern, but it is possible
function withUnchangedComplexOutput(
uint256[] calldata _nums
) public returns (uint256[] calldata) {
// ...
return _nums;
}
}
Visibility
Similar to variables, functions can have visibility modifiers including public, private, internal, and an additional visibility external which is special to them. And like variables, visibility modifiers specify who can access a function, including the contract itself.
- Public functions can be accessed by everyone including other functions in the contract.
- Private functions can only be accessed by the other functions in the contract.
- Internal functions can be accessed by the contract and any contract the inherits it. Its important to note that the inherited contract calls its own version of the function and not the original function itself.
- External functions can only be called from outside the contract. This can be achieved by calling the functions programatically, either from other contracts or through tools like ethers.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Functions {
function publicFunc() public {
// ...
}
function externalFunc() external {
// ...
}
function internalFunc() internal {
// ...
}
function _privateFunc() private {
// ...
}
function callOtherFuncs() public {
publicFunc();
// externalFunc(); // This will error
internalFunc();
_privateFunc();
}
}
contract FunctionsClone is Functions {
function callOtherFuncsClone() public {
publicFunc();
// externalFunc(); // This will error
internalFunc();
// _privateFunc(); // This will error
}
}
Types of Functions in Solidity
The last important thing you need to know about functions is their types, which dictate their functionalities. These types of functions in Solidity are view, pure, and payable.
- View functions don’t mutate any state variables, but can read them and return their values.
- Pure functions don’t mutate neither read the contract variables, and are used to perform calculations. The output of a pure function will always be the same given the same set of inputs.
- Payable functions are the ones that can receive money (Ether), and are fundamental to all financial transaction on the Ethereum blockchain.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Functions {
uint256 private _num;
function normalFunc(uint256 num_) public {
_num = num_;
}
function viewFunc() public view returns (uint256) {
return _num;
}
function pureFunc(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
function payableFunc() public payable {
// ...
}
}
A Real World Example for Solidity Functions
Here’s how you would use functions in an actual solidity contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Functions {
uint256 private _num;
function getNum() public view returns (uint256) {
return _num;
}
function addToNum(uint256 num_) external payable {
uint256 sum = _sum(_num, num_);
_num = sum;
}
function _sum(uint256 a, uint256 b) private pure returns (uint256) {
return a + b;
}
}
The “Secret” Functions
Similar to global variables, Solidity offers a set of special functions for contracts to work with. We will talk about those in an upcoming “secret” functions article 😉
Hope you got the idea of various types of functions in Solidity and the use cases of Solidity functions!
1. What are the two types of functions in Solidity?
- The two main types of functions in Solidity are state-changing functions and view functions.
2. What are private and public functions in Solidity?
- Private functions can only be called from within the same contract, not from any derived contracts or external entities. Public functions, on the other hand, can be called both internally and externally, including from derived contracts.
3. What are internal functions in Solidity?
- Internal functions in Solidity are similar to private functions, but they can also be accessed by contracts that inherit from the contract where the internal function is defined.
4. What are the different types of calls in Solidity?
- In Solidity, there are external calls (call, callcode, delegatecall, and staticcall) that call external contracts, and internal calls that call functions within the same contract.
5. What is a function in Solidity syntax?
- A function in Solidity is declared using the function keyword, followed by the function name, parameters within parentheses, visibility keywords (like private, public, internal, external), and the return types. Example: function myFunction(uint _param) public returns(uint) {…}.
6. What are public functions in Solidity?
- Public functions in Solidity are the ones that can be called both internally (from within the same contract or an inheriting contract) and externally (from other contracts or transactions).
7. What is the difference between public and private functions?
- The key difference between public and private functions lies in their accessibility. Public functions can be called from anywhere, including from outside the contract, while private functions can only be called from inside the contract where they are defined.
8. Why would one use a public function
- Public functions are used when we want to allow other contracts or external parties to interact with our contract.
9. What is the difference between public and private functions?
- Public functions can be called from anywhere, including from other contracts or from external transactions. Private functions, on the other hand, can only be called from the contract in which they’re declared.
10. Is the constructor of a contract always public in Solidity?
- No, from Solidity version 0.7.0 onwards, constructors can’t be marked as public or internal. They’re implicitly internal, meaning a contract can only be created directly, and not by a contract that inherits from it