Function Modifiers

view / pure

When a function doesn't actually change state in Solidity — e.g. it doesn't change any values or write anything we could declare it as a viewfunction, meaning it's only viewing the data but not modifying it:

pragma solidity ^0.8.0;

contract GreetingContract {
    string private greeting;

    constructor() {
        greeting = "Hello, World!";
    }

    function sayHello() public view returns (string memory) {
        return greeting;
    }
}

Solidity also contains purefunctions, which means you're not even accessing any data in the app. Consider the following:

function _multiply(uint a, uint b) private pure returns (uint) {
    return a * b;
}

This function doesn't even read from the state of the app — its return value depends only on its function parameters. So in this case we would declare the function as pure.

Ownable Contracts

Below is the Ownable contract taken from the OpenZeppelin Solidity library. OpenZeppelin is a library of secure and community-vetted smart contracts that you can use in your own DApps.

The Ownablecontract does the following:

  1. When a contract is created, its constructor sets the Ownable to msg.sender(the person who deployed it).

  2. It adds an onlyOwner modifier, which can restrict access to certain functions to only the Ownable.

  3. It allows you to transfer the contract to a new Ownable.

onlyOwner is such a common requirement for contracts that most Solidity DApps start with a copy/paste or import of this Ownable contract, and then their first contract inherits from it.

Function Modifiers - Generic

A function modifier looks just like a function, but uses the keyword modifier instead of the keyword function. And it can't be called directly like a function can — instead we can attach the modifier's name at the end of a function definition to change that function's behavior.

Let's take a closer look by examining onlyOwner (see code extract above)

  • Notice the onlyOwner modifier on the renounceOwnership function.

  • When you call renounceOwnership, the code inside onlyOwner executes first.

  • Then when it hits the _;statement in onlyOwner, it goes back and executes the code inside renounceOwnership.

  • While there are other ways you can use modifiers, one of the most common use-cases is to add a quick require check before a function executes.

  • In the case of onlyOwner, adding this modifier to a function makes it so only the owner of the contract (you, if you deployed it) can call that function.

Giving the owner special powers over the contract like this is often necessary, but it could also be used maliciously. For example, the owner could add a backdoor function.

  • Modifiers can all be stacked together on a function definition.

  • Modifiers are executed in the order they are listed in the function declaration.

  1. First, the logic inside the onlyOwner modifier is executed. This modifier typically checks whether the caller of the function is the owner of the contract.

  2. After onlyOwner completes its execution, anotherModifier is executed next. The specific logic of this modifier depends on its implementation.

  3. Finally, if all modifiers execute successfully (i.e., none of them revert), the body of the test function is executed.

Function Modifiers with Arguments

Function modifiers can also take arguments.

  • You can see here that the olderThan modifier takes arguments just like a function does.

  • And that the driveCar function passes its arguments to the modifier.

Payable Modifier

payablefunctions are a special type of function that can receive ETH.

  • This allows for some really interesting logic, like requiring a certain payment to the contract in order to execute a function.

  • Here, msg.valueis a way to see how much ETH was sent to the contract, and ether is a built-in unit.

  • What happens here is that someone would call the function from web3.js (from the DApp's JavaScript front-end) as follows:

  • Notice the valuefield, where the javascript function call specifies how much ether to send (0.001).

  • If you think of the transaction like an envelope, and the parameters you send to the function call are the contents of the letter you put inside, then adding a value is like putting cash inside the envelope — the letter and the money get delivered together to the recipient.

Function Modifier Example

Last updated