6

This question already has an answer here:

I don't know why nobody is asking this question (I've done my homework and did tons of Googling for an answer) but I am having hard time understanding how function modifiers actually work.

Sure it's trivial when you just use one modifier, or if you use the _ notation always at the end. But I come across code that looks something like this:

modifier modA {
  // verify something
  _;
  // verify something
  _;
}
modifier modB {
  // verify something
  _;
  // verify something
  _;
}
modifier modC {
  // verify something
  _;
  // verify something
  _;
}

function Fun() modA modB modC {
  // Do something
}

Then I finally realize that I have had no idea what is going on underneath.

In above case, how is it supposed to work? Looking at the documentation doesn't really help because it just says the modifiers simply replaces the _ with the function code. But what if there are multiple of these?

So if we go from modA to modB, do the _ get replaced with the next modifier? What do they mean by "get replaced by the original function"?

marked as duplicate by Crema, Ismael, eth Nov 2 '17 at 9:47

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

3

I think that a good way to understand what is happening is to experiment with

I created this contract in Remix to test/prove some of my assumptions about what was happening with modifiers:


pragma solidity ^0.4.18;

contract modifierTest {
    uint public modState1;
    uint public modState2;
    uint public modState3;

    modifier modA() {
        modState1 = modState1 + 1;
        _;
    }

    modifier modB() {
        modState2 = modState2 + 1;
        _;
        modState2 = modState2 + 1;
        _;
    }

    function func() public modA modB {
        modState3 = modState3 + 1;
    }
}

Initially, all of the contract's state variables are set to/default to 0.

After we execute the func function and examine the state variables we see the following result:


modState1 - uint256: 1
modState2 - uint256: 2
modState3 - uint256: 2
  • We can see that modifier modA was only run once.
  • We can see that modifier modB was only run once.
  • We can see that having more than one _; is valid syntax
  • We can see that the func function was called twice

From this, we can understand that the ; in *modA* modifier was replaced by the *modB* modifier's code and that the *;*s in the modB modifier's body were replaced by the func function's code.

So if we go from modA to modB, do the _ get replaced with the next modifier? What do they mean by "get replaced by the original function"?

Hopefully, this example demonstrates how the _ is replaced by the next modifier and that the final modifier's _ is replaced by the function's code.

  • Important to note is that func gets called twice, because you have multiple _; inside modB, and not because there are multiple modifiers. – Jack O'neill Aug 20 at 16:23
1

Assume a contract-wide variable 'a' with a value of '0' and a function F with a modifier M:

function F() M() {
   a = 1;
}

modifier M() {
   if (a == 1) throw;
   _
}

this will not throw. But this version:

modifier M() {
   _
   if (a == 1) throw;
}

will throw. Think of modifiers as code replacement (pre-compile in C++ terms) as opposed to 'calling into'. Modifiers are not functions. It's exactly as if the function F looked like this in each case, which if you assume 'a' has a value of '0' to start has two differing behaviors:

function F() {
   if (a == 1) throw;
   a = 1;
}

and

function F() {
   a = 1;
   if (a == 1) throw;
}

To figure it out, no matter how complicated, simply copy the modifier code into the proper location in the function being modified. You might have to copy many levels deep, but that's what the pre-compiler is doing.

I'm surprised to see two _ in your examples. I didn't know you could do that. It seems like a very bad practice to me to use _ twice in a modifier. It makes the code tremendously difficult to understand conceptually. At least it does for me.

  • I've seen a couple of examples online where the modifiers have multiple _s. Here's one: ethereum.stackexchange.com/a/5864/21958 - Also, I think understand what you're saying but could you clarify the answer to make sure? As I understood, you're saying function F() doesn't get executed until it goes through each modifier M1(), M2(), M3().. one by one, having M1() replace F() first, then replace the result with M2(), and so forth. And only when it reaches the last modifier it actually executes the function? – Vlad Oct 31 '17 at 15:33
  • Take the body of the code in F and replace M’s underbar with it. Then take the result if that and it literally becomes the body of F. This all happens during the compilation step, not at run time. Does that help? – Thomas Jay Rush Oct 31 '17 at 17:43

Not the answer you're looking for? Browse other questions tagged or ask your own question.