Function modifiers in Solidity
Exclusively define permissions for accessing the functions in a contract
In general, there are access modifiers in well known programming languages like Java, .Net to restrict the visibility or accessibility of functions, variables etc.. They are defined using the keywords like public, private, protected, internal... Similarly, solidity also have access modifiers like public, private, external to define the accessibility of methods or variables.
But, solidity programming has a special feature called FUNCTION MODIFIERS to define custom access to functions in solidity contract. i.e. If a particular condition is satisfied, then only the function tagged with the modifier will be accessible. On of the ways to define this condition is using the require which is like a ternary operator in the conventional programming language.
boolean haveVote= (age>18)?true:false;
In the above code, as we all know that if the condition age>18 is satisfied, then the result will be true i.e. the LHS of : expression else it would be false.
Similarly the require in solidity also follows a similar fashion.
modifier countMoreThanZero(uint n){
require(count-n>0,"count will reach to less than zero, can't perform operation");
_;
}
Here, count-n>0 is the condition to be satisfied. If it is not satisfied the string following it would act as error message and restrict access to the tagged function.
If the condition is satisified, then in the next line _; represents that the tagged function body will be executed.
let us clarify it with a simple example.
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract ModifierEx{
uint public count;
constructor(uint n){
count=n;
}
modifier countMoreThanZero(uint n){
uint temp=0;
temp=count-n;
require(temp>0,"count will reach to less than zero, can't perform operation");
_;
}
//function will be executed if its resulting value on count will be remained greater than zero
function decrByN(uint n) public countMoreThanZero(n){
count-=n;
}
function incrByN(uint n) public{
count+=n;
}
}
Here, if we look into decrByN function, we see that we mentioned the modifier name in its signature. It indicates that the modifier will be executed before the control enters into the function body.
As mentioned before, if the condition specified in the modifier is not satisfied the error message is thrown and the decrByN will not be executed anymore. Instead, if the condition in the modifier is satisfied, the next line i.e. _; will be executed i.e. it returns the control to actual function body, hence the actual function body will be executed.
As you might have a doubt that this condition checks could be done with the help of if- else statements like any other programming languages. Yup! that's absolutely true, but what you need to observe is the modifier is offering you with the flexibility or modularity or reusability (whatever you say) i.e. we can impose multiple conditions for accessing a function by just adding the modifier names in the function signature. It makes the code hassle free and maintainable.
Let us see the output where an error message is thrown. It might be clumsy to understand the output as a beginner.
The error message we specified in the modifier will be displayed which indicates that the condition is failed.
At the time of this execution the count value is 1 and I set the decByN value argument to 5, which will make the count value negative. So, the condition will fail as we mentioned.
For this example, you might not feel the impact what it makes, but as you dive through real world use cases, you need to write larger contracts with tens or hundreds of functions in it. At that time you can explicitly write the accessing conditions to each and every function in the contract. Then you will feel the advantage of modifiers.
Let us take another example where you declare a variable in the contract which can only be modified by the contract owner/ only a particular wallet address which might be a real world scenario like password resets.
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract OwnerOnlyModifier{
address public owner;
uint val;
constructor(uint value){
owner=0x5e17b14ADd6c386305A32928F985b29bbA34Eff5;
val=value;
}
modifier onlyOwner{
require(msg.sender==owner,'you are not the owner');
_;
}
function incrementValue(uint newVal) onlyOwner public {
val+= newVal;
}
function decrementValue(uint newVal) onlyOwner public {
val-= newVal;
}
function setVal(uint value) onlyOwner public{
val= value;
}
function getVal() public view returns(uint){
return val;
}
}
Keywords covered in the article
modifier
require
Follow & Connect ๐ค
๐ Github
๐ Linkedin
๐ Website