Function Modifiers in Solidity
Emeruche Ikenna
Posted on September 24, 2022
Originally published at coleruche.com
If you are familiar with Solidity, you may have noticed or come across code like:
pragma solidity ^0.7.0;
contract TestContract {
uint private score;
function getScore() external view returns(uint){
return score;
}
}
The view
key in the getScore
function above is an example of Modifiers in Solidity.
Modifiers in Solidity are little helper functions that modify a function that it is attached to. Modifiers are called before the main function is called and we may need this behaviour in cases like:
- Validating an input.
- Making checks before modifying the contract.
- Running a set of tasks or functions before running the main one.
There are built-in modifiers in Solidity and we can as well create our own custom modifiers.
Built-in Modifiers
The view
example above is an example of the built-ins. There exists other examples like pure
and payable
.
view
This modifier is used to explicitly inform Solidity that you intend to only read internal state from the contract. You are not allowed to modify or alter any state variable using a function containing this modifier. Attempting to do this will lead to an error and canceling the code execution.
For example, using the sample code above, we are not allowed to do this:
function getScore() external view returns(uint){
score += 1; // TypeError: Function declared as view, but this expression (potentially) modifies the state
return score;
}
pure
This modifier is used to inform Solidity that we have no intention of reading or modifying the contract state. In view
, we can read but cannot set/alter state, while in pure
we cannot even read the state. For, example:
function getScore() external pure returns(uint){
return score; // TypeError: Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
}
You may think, "When do we need this then?". We may need this modifier when we wish to make some internal calculations and checks where we do not need any variables from existing state. For example,
pragma solidity ^0.7.0;
contract TestContract {
uint private scoreA;
uint private scoreB;
function addScores(uint a, uint b) internal pure returns (uint) {
return a * b;
}
function addAndGetScores() external view returns (uint) {
return addScores(scoreA, scoreB);
}
}
Above, addScores
accepts arguments and only carries out instructions based on them. They do not read state or change it.
payable
This other modifier is used to inform Solidity that we expect some ether in form of Wei to be passed in along with the function. It is simply called as such:
pragma solidity ^0.7.0;
contract TestContract {
uint private score;
function setScore(uint newScore) external payable {
score = newScore;
}
}
When we do this without sending in some ether with the transcation, the function execution will fail.
Custom Modifiers
We may also decide to create our own custom modifiers that handle specific tasks before executing our functions. To do this, we simply define the modifier using the modifier
keyword and can also use them alongside other modifiers:
pragma solidity ^0.7.0;
contract TestContract {
uint private score;
address manager;
constructor() {
manager = msg.sender;
}
modifier isManager() {
require(msg.sender == manager);
_;
}
function setScore(uint newScore) external payable isManager {
score = newScore;
}
}
Let us break down the contract code above:
- We added a new variable to store our manager, whicn in this case is the address of the wallet or contract that deploys this
TestContract
. - We set this manager in the constructor.
- The next line is where we declare our modifier called
isManager
which just checks that the caller of the function it is attached to is the manager. This adds some layer of protection on top of our contract by preventing the general public or unintended users to modify the state of our contract.
The modifier functuon above has a line of code with just an underscore "_". This special symbol informs Solidity that this is the point where we inject the main function block. So it checks that the caller is the manager, and then runs score = newScore;
Also note that modifiers can also accept arguments like regular functions.
pragma solidity ^0.7.0;
contract TestContract {
uint private score;
address manager;
constructor() {
manager = msg.sender;
}
modifier checkFee(uint fee) {
require(fee > 100000);
_;
}
function setScore(uint newScore) external payable checkFee(msg.value) {
score = newScore;
}
}
In the code above, the new checkFee
modifier expects to be called with a value which it checks to be greater than 100000wei. This modifier is now used in setScore
and the value of ether sent with the transaction is passed as the parameter.
Posted on September 24, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.