In my understanding, there are some situations, where the compiler can't calculate, how much Gas it takes to complete the function call. The most obvious example – when your function is calling some function from the another contract. The compiler doesn't have the source of the another contract so it can't tell you, how much Gas is necessary, to complete the call.
But even when the function doesn't call any external function, the compiler also can't tell you the cost. A small example is listed bellow:
pragma solidity ^0.4.16;
contract Test {
address[] owners;
function addOwner(address newOwner) external {
owners.push(newOwner);
}
}
The warning message is:
Gas requirement of function browser/ballot.sol:Test.addOwner(address)
unknown or not constant. If the gas requirement of a function is
higher than the block gas limit, it cannot be executed. Please avoid
loops in your functions or actions that modify large areas of storage
(this includes clearing or copying arrays in storage)
What is the reason of this warning? I think maybe it's because of changing the state, but I'm not sure.
Thank in advance!
Best Answer
You can get the gas estimations by adding
--gas
tosolc
:However, there are many cases when the gas estimator reports
infinite
gas. It doesn't necessarily mean that there is an infinite loop in your code or that your code is incorrect but just the estimator is quite restrictive when making decisions about how much gas can be consumed by the code. In particular, any backward jumps or loops in the assembly code will make it report infinite gas.Detailed answer below
The warning comes from the Remix' static code analyser. From remix sources
Debugging shows that the
gas
isnull
.Gas estimations come from the solidity compiler. In this particular case the compiler estimated the gas for the
addOwner
function as infinity. You can see it by running solc compiler with--gas
option:From solidity compiler sources you can see that
functional estimation
is done in theGasEstimator.cpp
which in turn uses PathGasMeter.cpp to estimate the maximum possible gas consumption (I marked with arrow the place where infinite gas estimation is returned).Backward jumps indicate a loop which might result in unbounded gas consumption.
The piece of assembly output from
solc --asm Test.sol
that has a backward jump is:This piece of opcodes clears the storage in case the length of an array is reduced. Since the storage is expensive, every time it's cleared the gas is refunded to the transaction sender.
Why would that contract need to reduce the length of the array you might ask. The reason is
is replaced by
The first line where the length of the array is modified is further expanded by the function that's changing the array length which in case the length is lesser than it was before will clear the storage slots not used by the array. This function iterates through unused slots and clears them one by one.
Although our contract never needs to reduce the array size the assembler includes this piece of code nevertheless which causes the gas estimator to report the infinite max gas usage.
You can try executing the
addOwner(address)
function multiple times. The used gas is always the same: 48829 gas. However if you add another function to the contract:and try calling it you will see that the used gas depends on by how much you reduce the array length.
Related question: Infinite gas estimation from solc for simple function