Yes, there is, perhaps a better way to phrase this is how to test for throw conditions - or now, rather assert(), require(), and revert(). For example, in the case above, you want to make sure that when someone other than the owner calls a function, then the function throws and any state changes are rolled back to their original state. The best way to do this is with Solidity unit tests using Truffle. Unfortunately, when using JavaScript unit tests, when a throw condition is encountered, then the test "breaks" - with Solidity unit tests, you can test many throw conditions at the same time without stopping.
What you will want to use is a proxy contract to “to wrap the contract call to a raw call and return whether it succeeded or not.”. A proxy contract should look like the below:
contract ThrowProxy {
address public target;
bytes data;
function ThrowProxy(address _target) {
target = _target;
}
//prime the data using the fallback function.
function() {
data = msg.data;
}
function execute() returns (bool) {
return target.call(data);
}
}
In the creation of your throwProxy contract, your _target
would be the address of the contract that you would like to test.
In your Solidity unit test, you would include a test like the below:
function testTheThrow() {
MyContract mycontract = new MyContract();
ThrowProxy throwproxy = new ThrowProxy(address(mycontract));
MyContract(address(throwproxy)).functionThatShouldThrow();
bool r = throwproxy.execute.gas(200000)();
Assert.isFalse(r, "Should be false because is should throw!");
}
In the example above, we are using the fallback function from the throwProxy contract to compile the call data to execute the functionThatShouldThrow()
by using the execute()
function from the throwProxy to use the .call() method on your original target, MyContract
. Recall that .call() return a Boolean as to whether execution is successful or not - it does not include any returns from the function. From here, saving the r
as to the Boolean value as to whether the functionThatShouldThrow()
is successful or not. From here, you can use the Assert.sol library to assert that r
is in fact false because the function should have thrown.
I often find I want a software client to be able to check but I also want a modifier for readability, so it often ends up like this:
modifier onlyIfAllowed() {
require(isAllowed(args));
_;
}
function isAllowed() public view returns(bool allowed) {
allowed = ...
}
function safer() public onlyIfAllowed ...
Of course, it would also be fine to do that in-line.
function inline() public {
require(isAllowed());
// carry on
Ultimately, it is a matter of personal preference. I find it more readable to go like this:
function tricky() public onlyOwner onlyIfAllowed onlySomethingElse { ...
... so the actual purpose of the function doesn't get mixed up with repetitive steps. It's more DRY, in my opinion. Not everyone agrees. The Vyper team, for example, consider modifiers an anti-feature that obscures important logic that they feel should be in-line.
Hope it helps.
Best Answer
Update Oct 12 2016
From Solidity version 0.4.0+, you now need to add a semicolon after
_
. See Solidity - Version 0.4.0:The tests below only work in Solidity < v 0.4.0.
Summary
_
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. Seemodifier checkThree
. This may be prevented by later versions of thesolc
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.Details
From Solidity Features - Function Modifiers :
Here's an example from EtherScan.io - The DAO - Source Code.
The modifier
onlyTokenholders
restricts "modified" functions from be executed by non-tokenholders.Here is the
vote(...)
function with theonlyTokenHolders
modifier:The code within the
vote(...)
function is only executed if the modifier check does not throw an error from the statementif (balanceOf(msg.sender) == 0) throw;
. The_
represents the body of thevote(...)
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:Let's take '_' for a test run
Here is some code to test
_
:Flattened the code
Inserted contract into the blockchain:
Sent a transaction to call the
test()
function:Checked the results:
Taking Out The Return Statement In
test()
:I removed the return statement so the source code for
test
is:And re-ran the test to produce the following results: