DEV Community

Cover image for Function Modifiers in Solidity
Shlok Kumar
Shlok Kumar

Posted on

Function Modifiers in Solidity

Modifiers are special functions used to modify the behavior of other functions, allowing for extra conditions

In Solidity, modifiers say what is happening in a way that is clear and easy to read. They are like the decorator pattern in Object-Oriented Programming. A modifier is a piece of code that is added to a function to change its behavior. For example, checking a condition automatically before running the function (this is mainly what they are used for). Modifiers are helpful because they cut down on duplicate code. You can use the same modifier in more than one function if your smart contract checks for the same condition.

When should we use modifiers?

Modifiers are most often used to check a condition automatically before a function is run. If the function doesn't meet the requirement of the modifier, an exception is thrown, and the function stops running.

The wildcard symbol

A _; is the merge wildcard symbol. It combines the code for the function with the code for the modifier where the _; is. In other words, where the special symbol _; appears in the modifier's definition, the body of the function (to which the modifier is attached) will be added.

In order to work, a modifier must have the symbol _; in its body. It must be done. The place where you write the _; symbol will decide if the function has to be executed before, in between or after the modifier code. You can place the _; at the beginning, middle or end of your modifier body.

In practice, (especially until you understand how modifiers work really well), the safest usage pattern is to place the _; at the end. In this scenario, the modifier serves as a consistent validation check, so to check a condition upfront and then carry on.

You can put the _; at the beginning, in the middle, or at the end of the body of your modifier. In practice, the safest way to use the _; is at the end. This is especially true until you really understand how modifiers work. In this case, the modifier is used as a consistent validation check, so that a condition can be checked up front and then the process continues.

Modifiers can be used to:

  • Restrict access to a function

  • Validate inputs of a function

  • Guard against the reentrancy hack

Solidity implementation of function modifiers:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract FunctionModifier {
    // We will use these variables to demonstrate how to use
    // modifiers.
    address public owner;
    uint public x = 10;
    bool public locked;

    constructor() {
        // Set the transaction sender as the owner of the contract.
        owner = msg.sender;
    }

    // Modifier to check that the caller is the owner of
    // the contract.
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        // Underscore is a special character only used inside
        // a function modifier and it tells Solidity to
        // execute the rest of the code.
        ;
    }

    // Modifiers can take inputs. This modifier checks that the
    // address passed in is not the zero address.
    modifier validAddress(address addr) {
        require(_addr != address(0), "Not valid address");
        ;
    }

    function changeOwner(address newOwner) public onlyOwner validAddress(_newOwner) {
        owner = _newOwner;
    } 
Enter fullscreen mode Exit fullscreen mode

Returning multiple variables from functions:

There are several ways to return outputs from a function. The following code implements the various permutations and combinations while returning function outputs.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Function {
    // Functions can return multiple values.
    function returnMany()
        public
        pure
        returns (
            uint,
            bool,
            uint
        )
    {
        return (1, true, 2);
    }

    // Return values can be named.
    function named()
        public
        pure
        returns (
            uint x,
            bool b,
            uint y
        )
    {
        return (1, true, 2);
    }

    // Return values can be assigned to their name.
    // In this case the return statement can be omitted.
    function assigned()
        public
        pure
        returns (
            uint x,
            bool b,
            uint y
        )
    {
        x = 1;
        b = true;
        y = 2;
    }

    // Use destructuring assignment when calling another
    // function that returns multiple values.
    function destructuringAssignments()
        public
        pure
        returns (
            uint,
            bool,
            uint,
            uint,
            uint
        )
    {
        (uint i, bool b, uint j) = returnMany();

        // Values can be left out.
        (uint x, , uint y) = (4, 5, 6);

        return (i, b, j, x, y);
    }

    // Cannot use map for either input or output

    // Can use array for input
    function arrayInput(uint[] memory _arr) public {}

    // Can use array for output
    uint[] public arr;

    function arrayOutput() public view returns (uint[] memory) {
        return arr;
    }
}
Enter fullscreen mode Exit fullscreen mode

For more content, follow me at - https://linktr.ee/shlokkumar2303

Top comments (0)