Hardcoded Gas Values Remediation
Overview
Fixed gas values baked into external calls (transfer(), send(), call{gas: 2300}) assume opcode prices that change. The Istanbul hard fork raised SLOAD cost and broke receivers relying on the 2300-gas stipend; Berlin added cold/warm access pricing; L2 rollups price execution differently again. Hardcoded gas calibrated for one environment fails in another.
Related Detector: Hardcoded Gas Values
Recommended Fix
Before (Vulnerable)
function sendETH(address to) external {
payable(to).transfer(1 ether); // Only 2300 gas -- may fail post-Istanbul
}
After (Fixed)
function sendETH(address to) external {
(bool success, ) = to.call{value: 1 ether}(""); // Forwards remaining gas
require(success, "ETH transfer failed");
}
Using call with no gas cap forwards whatever gas remains, so the receiver’s receive/fallback can run its logic regardless of how opcode costs shift. Checking the boolean return value ensures a failed transfer reverts the transaction instead of being silently ignored. Pair this with a reentrancy guard, since call no longer limits the recipient.
Alternative Mitigations
- Where you genuinely need to bound forwarded gas, derive the limit from
gasleft()at runtime rather than a literal constant. - Use the pull-payment pattern so recipients withdraw their own funds and gas concerns shift to them.
- When deploying to an L2, re-validate any remaining gas assumptions against that chain’s pricing model.
Common Mistakes
- Replacing
transferwithcallbut dropping the success check, turning a safe failure into a silent loss. - Adding
callwithout a reentrancy guard, since unbounded gas lets the recipient reenter. - Keeping a literal
call{gas: 2300}and assuming it is still safe because it “always worked.”