3.18 Gas & Costs
Costly Operations
Certain operations in Solidity
are considered costly or expensive in terms of the amount of Gas units they use.
If such operations are used inside loops they end up consuming a lot of Gas which could result in unexpected behavior.
The best example of a costly operation in Solidity
is that of state variable updates. Remember that in Solidity
state variables are stored in the storage area of the EVM. Updates to such state variables use the SSTORE
instruction of the EVM which are one of the most Gas expensive.
As of the latest upgrade from Berlin, SSTORE
costs 20000 Gas units, if they are a cold store where the state variable is being updated for the first time in the context of this transaction. Or they cost 5000 Gas units if it is a warm store, in which case this variable has already been updated in the context of this transaction.
So either 5000 or 20000 Gas units are consumed every time a state variable is updated, so as you can imagine, if such updates are done inside loops, then they could end up consuming a lot of Gas and result in an OOG error, if the amount of Gas supplied in this transaction is less than what is required.
The solution here is to use local variables instead of state variables as much as possible. The reason is because local variables are allocated in memory, and memory updates using MSTORE
only cost 3 Gas units compared to the 5000 or 20000 that storage updates cost.
So this notion of costly operations being used inside the loops leading to OOG errors and in the worst case leading to a denial of service (DoS) can be mitigated by caching and using local variables as much as possible instead of storage variables.
Costly Calls
Similar to state variable updates, external calls inside loops should also be used very carefully. The reason is external calls cost 2600 Gas as of the latest upgrade. This is more of a concern if the index of the loop is controlled by the user, because in that case the number of iterations of the loop is also user controlled.
That could result in a denial of service, if one of the calls inside the loops reverts or if the execution runs Out-of-Gas because the Gas applied in the transaction wasn't enough.
So the mitigation here is to avoid or reduce a number of external calls made inside loops and also check that the loop index can't be user controlled or that it is bounded to a small number of iterations, this again is in the context of preventing opportunities for denial of service.
Block Gas Limit
Costly operations such as state variable updates and external calls especially made inside loops are also relevant in the context of the block Gas Limit.
Remember that Ethereum blocks have a notion of a block Gas Limit which limits the total amount of Gas units consumed by all the transactions included in the block to a maximum upper bound. This upper bound until recently was 15 million Gas units. This has changed significantly in how it works because of EIP1559, but the notion of a block Gas Limit still remains.
The reason why this is relevant is because, if expensive operations are used inside loops where the loop index may be user controlled. Then such expensive operations may result in an Out-of-Gas error, this Out-of-Gas could not only come from the amount of Gas units supplied in the transaction that resulted in all this execution, but it could also arise because of the Gas consumed by this transaction exceeding the Gas Limit for this block.
So the mitigation here is again to evaluate the loops and make sure that a lot of these expensive operations are not used inside the loops and also to check if the loop index is user controlled, and if it can be bounded to a small finite number, so that opportunities for denial of service are prevented.
Gas Griefing
Gas Griefing is a security concept that becomes interesting in the context of transaction relayers.
Remember that on Ethereum, users can submit transactions to the smart contracts on the blockchain or alternatively they can submit what are known as meta-transactions which are sent to the transaction relayers, where they do not need to be paid for Gas. The relayers in turn, forward such transactions to the blockchain with the appropriate amount of Gas.
In this scenario the users typically compensate the relays for the Gas out of that. In such situations it becomes necessary for the users, to trust the transaction relayers, to submit those transactions or forward their transactions with a sufficient amount of Gas, so that their transactions do not fail.
Last updated