Table of contents
Table of Content
Introduction
Components of a Solidity Function
FunctionName
Parameters
Visibility keywords
public
external
internal
private
State mutability keywords
View
Pure
Payable
Fallback Function
Function Modifiers
Summary
Introduction
A function is simply a group of code that can be reused anywhere in a program. By creating a function, we reduce the need to write the same code repeatedly, which saves the excessive use of memory and decreases the runtime of a program.
In Solidity, functions are first-class citizens, we use them to carry out various operations in a smart contract. Within a contract, we can define functions that can be called by an Externally-Owned-Account(EOA) transaction or another contract.
The syntax used to declare a function in Solidity is as follows:
function FunctionName([parameters]) {public|private|internal|external} [pure|constant|view|payable] [modifiers] [returns (return types)]
We will have a detailed look at the various components seen in the function syntax, For now, let's look at a simple function example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract MyContract {
uint value
// A function to read value
function getValue() external view returns(uint) {
return value;
}
// A function to modify value
function setValue(uint new_value) external {
value = new_value
}
}
In the preceding code, we declare simple getter(a function that gets the value of a specific property) and setter(a function that sets the value of a property) functions. Now let's look at the components of a function in more detail.
Components of a Solidity Function
FunctionName
This is the name of the function(usually seen after the function
keyword) used to call the function in a transaction(from an EOA) or from another contract. In our example, we have two functions named getValue
and setValue
respectively.
Parameters
After the name, we define the arguments that we want the function to accept, with their names and type. In our example, the setValue
function accepts a new_value
argument of the uint
type.
Visibility keywords
Function visibility keywords allow you to define how a function can be accessed. Note, that any function or data inside a contract is always visible on the public blockchain, meaning that anyone can see the code or data. The keywords described here only affect how and when a function can be called.
public The visibility of all functions is set to public
by default. This means the function can be called from within its contract, an EOA transaction, or another contract.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract MyContract {
uint value
// visibility set to public
function getValue() public view returns(uint) {
return value;
}
// A function to modify value
function setValue(uint new_value) external {
value = new_value
}
}
external A function that uses the external
keyword can only be called outside its contract. It cannot be called from within its contract unless explicitly prefixed with the this
keyword.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract MyContract {
uint value
// visibility set to external
function getValue() external view returns(uint) {
return value;
}
// A function to modify value
function setValue(uint new_value) external {
value = new_value
// external visibility bypassed
}
// calls getValue()
function callGetValue() public view returns (uint) {
// External visibility? Who cares
return this.getValue();
}
}
internal A function that uses the internal keyword is only accessible within its contract—It cannot be called by another contract or EOA transaction. However, they can be called by child contracts(those that inherit the contract the function is declared in).
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
// Parent.sol
contract Parent {
uint data;
function foo() internal {}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
// Child.sol
import "./Parent.sol";
contract Child is Parent {
function bar() external {
// call function in parent
foo();
// manipulate parent data
data++;
}
}
private Private functions are similar to internal functions but cannot be called by child contracts.
State mutability keywords
These are keywords that affect the behavior of a function.
view A function marked as view does not modify any value, it can read a value.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract MyContract {
uint value
// getValue is read-only because of view
function getValue() external view returns(uint) {
return value;
}
// A function to modify value
function setValue(uint new_value) external {
value = new_value
}
}
pure A pure function does not read or modify any stored variables. It can only operate on arguments and return data, without reference to any stored data.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract Test {
function getResult() public pure returns(uint product, uint sum){
uint a = 1;
uint b = 2;
product = a * b;
sum = a + b;
}
}
payable A payable function is a function that can receive payment or ether into a contract.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract MoneyContract {
function pay(address payable to, uint amount) external {
//Send ether to the address stored in the "to" variable
to.transfer(amount) // 100 wei
}
function receive() public payable {
//Check the amount of ether received
msg.value
//Check ether balance in the smart contract
address(this).balance
}
}
In the preceding code, we can send ether receive
function in the MoneyContract
.
Fallback Function
A fallback function is a special function in Solidity with neither a name, parameters, or return values. It is executed when:
A function that does not exist in a contract is called.
When ether is sent to a contract without an explicitly declared function to receive it—the
receive()
can also be used to receive the ether in this case.
Characteristics of a fallback function:
It is an unnamed function.
It accepts no arguments, only the
fallback()
declaration is required.It does not return any value.
It must be used with the
external
visibility keyword.It is constrained to 2300 gas when it is called by a
transfer()
orsend()
method in another function—this enables the function call to be cheap.It must use the
payable
keyword for it to receive ether sent to the contract.A contract can only have one fallback function.
receive()
vs fallback()
The receive()
function is similar to the fallback()
function. It cannot have arguments, or return values, and must have external
visibility and payable
state mutability. However, it has only one purpose—to receive ether. It is called when ether is sent to a contract with no calldata. If the receive()
function does not exist, the fallback()
function is used.
The preceding diagram shows the logic used in a contract that either or both receive()
and fallback()
.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
//1. Sending ether in a smart contract
//2. Receiving ether
//-via a name function call
//-via a non-name function call
contract MyContract {
//1.
function foo(address payable to, uint amount) external {
// transfer ether from the address stored in "to"
to.transfer(100) // 100 wei
}
//2. named-function call
function bar() external payable {
//Check the amount of ether received
msg.value
//Check ether balance in the smart contract
address(this).balance
}
// native fallback function.
fallback() external payable {
// check the amount of ether received
msg.value
// check ether balance in the smart contract
address(this).balance
}
// native receive function.
receive() external payable {
// check the amount of ether received
msg.value
// check ether balance in the smart contract
address(this).balance
}
}
Function Modifiers
A modifier is simply a function used to modify the behavior of another function(s). In solidity, a modifier is used to enhance and add functionality to function(s) without modifying the function(s) itself— It can be executed before or after the function executes its code.
A modifier is declared with the following syntax in solidity:
modifier ModifierName([parameters]) {
// modifier definition
...some condition
// merge wildcard(executes function be modified)
_;
}
The wild card(_;
) symbol indicates the end of the modifier and the beginning of the function(which replaces the modifier at runtime) that the modifier is modifying.
The wildcard symbol is mandatory for all modifiers.
The wildcard can be used anywhere in a modifier.
If the wildcard is placed at the end of a modifier, the condition is verified before the "modified" function is executed. However, if it is placed at the beginning the function is executed before the condition is verified.
Using a Modifier
Example: A function without a modifier.
contract FunctionModifier {
bool public paused;
uint public count;
function setPause(bool _paused) external {
paused = _paused;
}
// increased when not paused
function inc() external {
require(!paused, "paused");
count += 1;
}
// decrease when not paused
function dec() external {
require(!paused, "paused");
count -= 1;
}
}
In the preceding code, the count
variable is increased and decreased by inc
and dec
functions respectively. Now let's modify these functions using a function modifier.
contract FunctionModifier {
bool public paused;
uint public count;
function setPause(bool _paused) external {
paused = _paused;
}
// pause functionality
modifier whenNotPaused() {
require(!paused, "paused");
_;
}
function inc() external whenNotPaused {
count += 1;
}
function dec() external whenNotPaused {
count -= 1;
}
}
In the preceding code, the whenNotPaused
function modifier is used to modify the inc
and dec
functions by adding the pause functionality.
Chaining Modifiers and Passing Arguments
Let's see how multiple modifiers can be used in a function, and how argument(s) can be passed to a modifier.
Example:
contract FunctionModifier {
bool public paused;
uint public count;
function setPause(bool _paused) external {
paused = _paused;
}
// pause functionality
modifier whenNotPaused() {
require(!paused, "paused");
_;
}
// modifier accepts argument
modifier cap(uint _x) {
require(_x < 100, "x >= 100");
_;
}
// using multiple modifiers & argument
function incBy(uint _x) external whenNotPaused cap(_x) {
count += x;
}
}
In the preceding code, the incBy
by function uses both the whenNotPaused
and cap
modifiers. The cap
modifier accepts an argument, which is provided by the function it modifies.
Summary
In this article, you learned the following about functions in solidity:
The declaration syntax
How to control access to a function with visibility keywords.
How to modify function behavior with state-mutability keywords.
How to use the
fallback
function.How to use function modifiers to extend the capability of a function.