58

I often see _ in modifiers

modifier onlyOwner() {
    if (msg.sender != owner) throw;
    _
}

Does it execute any code or is it meant to make the code easier to read ?

q9f
  • 32,913
  • 47
  • 156
  • 395
dor
  • 725
  • 1
  • 5
  • 5

1 Answers1

55

Update Oct 12 2016

From Solidity version 0.4.0+, you now need to add a semicolon after _. See Solidity - Version 0.4.0:

  • Change _ to _; in modifiers.

The tests below only work in Solidity < v 0.4.0.



Summary

  • The code for the function being modified is inserted where the _ is placed in the modifier.
  • You can add more than one _s in the modifier code. And the code of the function being modified is inserted in each place where _ is located in the modifier. See modifier checkThree. This may be prevented by later versions of the solc compiler.
  • The modifiers gets called in the sequence they were defined (checkOne checkTwo checkThree) and at the end of the function, they are called in reverse. The modifiers seem to be applied like a stack. In this example anyway.



Details

From Solidity Features - Function Modifiers :

PT Modifiers can be used to easily change the behaviour of functions, for example to automatically check a condition prior to executing the function. They are inheritable properties of contracts and may be overridden by derived contracts.

contract owned {
  function owned() { owner = msg.sender; }
  address owner;

  // This contract only defines a modifier but does not use it - it will
  // be used in derived contracts.
  // The function body is inserted where the special symbol "_" in the
  // definition of a modifier appears.
  modifier onlyowner { if (msg.sender == owner) _ }
}


Here's an example from EtherScan.io - The DAO - Source Code.

The modifier onlyTokenholders restricts "modified" functions from be executed by non-tokenholders.

modifier onlyTokenholders {
    if (balanceOf(msg.sender) == 0) throw;
        _
}

Here is the vote(...) function with the onlyTokenHolders modifier:

function vote(
    uint _proposalID,
    bool _supportsProposal
) onlyTokenholders noEther returns (uint _voteID) {

    Proposal p = proposals[_proposalID];
    if (p.votedYes[msg.sender]
        || p.votedNo[msg.sender]
        || now >= p.votingDeadline) {

        throw;
    }

The code within the vote(...) function is only executed if the modifier check does not throw an error from the statement if (balanceOf(msg.sender) == 0) throw;. The _ represents the body of the vote(...) function.


From Learn X in Y minutes - Where X=Solidity, here is an example where the _ is not at the end of the modifier function:

// underscore can be included before end of body,
// but explicitly returning will skip, so use carefully
modifier checkValue(uint amount) {
    _
    if (msg.value > amount) {
        msg.sender.send(amount - msg.value);
    }
}


Let's take '_' for a test run

Here is some code to test _:

contract TestModifier {

    string[] public messages;
    uint256 testVariable;

    function numberOfMessages() constant returns (uint256) {
        return messages.length;
    }

    modifier checkOne {
        messages.push("checkOne - 1");
        if (testVariable == 123) 
            throw;
        _
        messages.push("checkOne - 2");
        if (testVariable == 123) 
            throw;
    }

    modifier checkTwo {
        messages.push("checkTwo - 1");
        if (testVariable == 123) 
            throw;
        _
        messages.push("checkTwo - 2");
        if (testVariable == 123) 
            throw;
    }

    modifier checkThree {
        messages.push("checkThree - 1");
        if (testVariable == 123) 
            throw;
        _
        messages.push("checkThree - 2");
        if (testVariable == 123) 
            throw;
        _
        messages.push("checkThree - 3");
        if (testVariable == 123) 
            throw;
    }

    function test() checkOne checkTwo checkThree returns (uint256) {
        messages.push("test - 1");
        testVariable = 345;
        messages.push("test - 2");
        return testVariable;
    }
}

Flattened the code

> var testModifierSource='contract TestModifier { string[] public messages; uint256 testVariable; function numberOfMessages() constant returns (uint256) { return messages.length; } modifier checkOne { messages.push("checkOne - 1"); if (testVariable == 123)  throw; _ messages.push("checkOne - 2"); if (testVariable == 123)  throw; } modifier checkTwo { messages.push("checkTwo - 1"); if (testVariable == 123)  throw; _ messages.push("checkTwo - 2"); if (testVariable == 123)  throw; } modifier checkThree { messages.push("checkThree - 1"); if (testVariable == 123)  throw; _ messages.push("checkThree - 2"); if (testVariable == 123)  throw; _ messages.push("checkThree - 3"); if (testVariable == 123)  throw; } function test() checkOne checkTwo checkThree returns (uint256) { messages.push("test - 1"); testVariable = 345; messages.push("test - 2"); return testVariable; }}'
undefined

Inserted contract into the blockchain:

> var testModifierCompiled = web3.eth.compile.solidity(testModifierSource);
undefined
> var testModifierContract = web3.eth.contract(testModifierCompiled.TestModifier.info.abiDefinition);
var testModifier = testModifierContract.new({
    from:web3.eth.accounts[0], 
    data: testModifierCompiled.TestModifier.code, gas: 1000000}, 
    function(e, contract) {
      if (!e) {
        if (!contract.address) {
          console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");
        } else {
          console.log("Contract mined! Address: " + contract.address);
          console.log(contract);
        }
    }
})
...
Contract mined! Address: 0xd2ca2d34da6e50d28407f78ded3a07962b56181c
[object Object]

Sent a transaction to call the test() function:

> testModifier.test(eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: testModifierCompiled.TestModifier.code,
  gas: 1000000
});

Checked the results:

> var i;
> for (i = 0; i < testModifier.numberOfMessages(); i++) {
    console.log(testModifier.messages(i));
}
checkOne - 1
checkTwo - 1
checkThree - 1
test - 1
test - 2


Taking Out The Return Statement In test():

I removed the return statement so the source code for test is:

function test() checkOne checkTwo checkThree returns (uint256) {
    messages.push("test - 1");
    testVariable = 345;
    messages.push("test - 2");
    // return testVariable;
}

And re-ran the test to produce the following results:

var i;
undefined
> for (i = 0; i < testModifier.numberOfMessages(); i++) {
..     console.log(testModifier.messages(i));
.. }
checkOne - 1
checkTwo - 1
checkThree - 1
test - 1
test - 2
checkThree - 2
test - 1
test - 2
checkThree - 3
checkTwo - 2
checkOne - 2
undefined
BokkyPooBah
  • 40,274
  • 14
  • 123
  • 193
  • 1
    re: The _ represents the body of the vote(...) function. Represents as in making it easier to read ? I've been writing modifiers without the _ and it doesn't seem to be a "special symbol" that does anything in the EVM, it seems to be more like a aesthetic thing. Does it have anything to do with how the code runs ? – dor Jun 10 '16 at 05:20
  • 3
    Even with your long answer I still don't get what it's used for... sorry. Does it have the same meaning as in Swift language? – Nicolas Massart Jun 10 '16 at 05:41
  • @oIG, some interesting test results. @Nicolas Massart, how does the modifier work in Swift? – BokkyPooBah Jun 10 '16 at 08:18
  • @dor Bokky said it clearly : it tells the function where to go. If your modifier is {action 1; _; action2;}, when called it will do action 1, then the function, then action 2. If you don't specify it I assume the underscore is implicitly placed at the end of the modifier. – Teleporting Goat Oct 28 '16 at 08:38
  • 2
    This is a very good and detailed answer and should be accepted. :( – Teleporting Goat Oct 28 '16 at 08:40
  • I think modifier as decorator in python. – Jaynti Kanani May 23 '17 at 06:27
  • 4
    "throw" is deprecated in favour of "revert() – Dipankar Oct 01 '17 at 03:30
  • This made it very clear for me to understand where the non modifier code is inserted, how the flow of modifiers execute, and when not to use the return statement if we use multiple modifiers. Thank you! – WowBow Oct 16 '20 at 08:43
  • What I got is quite different from you, which might be due to version differences, but I do see your point of illustrating how "_" is translated into commands on the stack. – Maxareo Mar 12 '21 at 01:40
  • TL;DR; That's a fancy way to explain a callback. – Anderson May 18 '21 at 16:41