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 ?

1 Answer 1

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
10
  • 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, 2016 at 5: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? Jun 10, 2016 at 5:41
  • @oIG, some interesting test results. @Nicolas Massart, how does the modifier work in Swift? Jun 10, 2016 at 8:18
  • 2
    This is a very good and detailed answer and should be accepted. :( Oct 28, 2016 at 8:40
  • 4
    "throw" is deprecated in favour of "revert()
    – Dipankar
    Oct 1, 2017 at 3:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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