There are two aspects to consider when choosing between assert()
and require()
- Gas efficiency
- Bytecode analysis
1. Gas efficiency
assert(false)
compiles to 0xfe
, which is an invalid opcode, using up all remaining gas, and reverting all changes.
require(false)
compiles to 0xfd
which is the REVERT
opcode, meaning it will refund the remaining gas. The opcode can also return a value (useful for debugging), but I don't believe that is supported in Solidity as of this moment. (2017-11-21)
2. Bytecode analysis
From the docs (emphasis mine)
The require function should be used to ensure valid conditions, such as inputs, or contract state variables are met, or to validate return values from calls to external contracts. If used properly, analysis tools can evaluate your contract to identify the conditions and function calls which will reach a failing assert. Properly functioning code should never reach a failing assert statement; if this happens there is a bug in your contract which you should fix.
The above excerpt is a reference to the still (as of 2017-11-21) experimental and undocumented SMTChecker
.
I use a few heuristics to help me decide which to use.
Use require()
to:
- Validate user inputs
- Validate the response from an external contract
ie. use require(external.send(amount))
- Validate state conditions prior to executing state changing operations, for example in an
owned
contract situation
- Generally, you should use
require
more often,
- Generally, it will be used towards the beginning of a function.
Use assert()
to:
- check for overflow/underflow
- check invariants
- validate contract state after making changes
- avoid conditions which should never, ever be possible.
- Generally, you should use
assert
less often
- Generally, it will be use towards the end of your function.
Basically, assert
is just there to prevent anything really bad from happening, but it shouldn't be possible for the condition to evaluate to false.
Historical note:
The require()
and assert()
functions were added to Solidity prior to the Byzantium fork, in v0.4.10
. Prior to Byzantium, they behaved identically, but already compiled to different opcodes. This meant that some contracts deployed before Byzantium behaved differently after the fork, the main difference being that began refunding unused gas.
State Variables
From the solidity docs here,
State variables are values which are permanently stored in contract
storage.
and can be declared in a contract as follow,
contract SimpleStorage {
uint storedData; // State variable
// ...
}
Local variables
Carries the usual meaning, that their context is within a function and cannot be accessed outside. Usually these are the variables that we create temporarily to hold values in calculating or processing something. Local variables (of struct, array or mapping) reference storage as pointed out here, local variable will act as an alias for pre-existing one in the storage. Check the example quoted below to understand what happens.
A common mistake is to declare a local variable and assume that it
will be created in memory, although it will be created in storage:
/// THIS CONTRACT CONTAINS AN ERROR
pragma solidity ^0.4.0;
contract C {
uint someVariable;
uint[] data;
function f() {
uint[] x;
x.push(2);
data = x;
}
}
The type of the local variable x is uint[] storage, but since storage
is not dynamically allocated, it has to be assigned from a state
variable before it can be used. So no space in storage will be
allocated for x, but instead it functions only as an alias for a
pre-existing variable in storage.
What will happen is that the compiler interprets x as a storage
pointer and will make it point to the storage slot 0 by default. This
has the effect that someVariable (which resides at storage slot 0) is
modified by x.push(2).
The correct way to do this is the following:
pragma solidity ^0.4.0;
contract C {
uint someVariable;
uint[] data;
function f() {
uint[] x = data;
x.push(2);
}
}
Defaults for the storage location
Here are defaults for the storage location depending on which type of variable it concerns (source):
state variables are always in storage
function arguments are in memory by default
local variables of struct, array or mapping type reference storage by default
local variables of value type (i.e. neither array, nor struct nor mapping) are stored in the stack
Best Answer
is just a conditional execution of the
// do something
block. So if that condition is not met, the// do something
block is skipped. But the execution moves to the next line after that block.You use require when you wish to revert the entire state changes so far in the function if some condition is not met. For example,
In case
_input
is less than 100, you don't want evensender
to be updated tomsg.sender
. So when therequire
fails, the entire transaction is reverted. This may not seem so relevant in the function body above. But there are instances where you call another contract, transfer some tokens, etc. For such situations, therequire
is an extremely safe way to handle failures or conditions not being met in solidity.Please refer this: https://solidity.readthedocs.io/en/v0.4.25/control-structures.html?highlight=require#error-handling-assert-require-revert-and-exceptions