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 ?
Ethereum Stack Exchange is a question and answer site for users of Ethereum, the decentralized application platform and smart contract enabled blockchain. It only takes a minute to sign up.
Sign up to join this communityFrom 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.
_
is placed in the modifier._
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.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. 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);
}
}
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
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
@oIG
, some interesting test results. @Nicolas Massart
, how does the modifier work in Swift?
Jun 10, 2016 at 8:18