From 7e760ef4965daa67de9737d2ebf7b0d26b74b94e Mon Sep 17 00:00:00 2001 From: Shebin John Date: Fri, 10 Jan 2025 13:08:26 +0100 Subject: [PATCH] [Certora Audit] G-08. Use shift right/left instead of division/multiplication if possible (#895) This pull request includes optimizations to the gas usage in the `Safe.sol` and `StorageAccessible.sol` contracts by replacing multiplication operations with bitwise shift operations. While the `DIV / MUL` opcode uses 5 gas, the `SHR / SHL` opcode only uses 3 gas. Gas optimization changes: * [`contracts/Safe.sol`](diffhunk://#diff-587b494ea631bb6b7adf4fc3e1a2e6a277a385ff16e1163b26e39de24e9483deL168-R169): Replaced the multiplication operation `* 64` with the bitwise shift operation `<< 6` to reduce gas costs in the `gasleft` check. * [`contracts/common/StorageAccessible.sol`](diffhunk://#diff-a7dd65d90b0567bb9ba14ecd4ff414529a934cd3752ccf309800fad93fba354eL18-R18): Replaced the multiplication operation `* 32` with the bitwise shift operation `<< 5` to reduce gas costs when creating a new bytes array. --- contracts/Safe.sol | 3 ++- contracts/common/StorageAccessible.sol | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/Safe.sol b/contracts/Safe.sol index 3632b3dd3..2e13aed3a 100644 --- a/contracts/Safe.sol +++ b/contracts/Safe.sol @@ -165,7 +165,8 @@ contract Safe is // We require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500) // We also include the 1/64 in the check that is not sent along with a call to counteract potential shortings because of EIP-150 - if (gasleft() < ((safeTxGas * 64) / 63).max(safeTxGas + 2500) + 500) revertWithError("GS010"); + // We use `<< 6` instead of `* 64` as SHR / SHL opcode only uses 3 gas, while DIV / MUL opcode uses 5 gas. + if (gasleft() < ((safeTxGas << 6) / 63).max(safeTxGas + 2500) + 500) revertWithError("GS010"); // Use scope here to limit variable lifetime and prevent `stack too deep` errors { uint256 gasUsed = gasleft(); diff --git a/contracts/common/StorageAccessible.sol b/contracts/common/StorageAccessible.sol index c9862910f..d4081e96f 100644 --- a/contracts/common/StorageAccessible.sol +++ b/contracts/common/StorageAccessible.sol @@ -15,7 +15,8 @@ abstract contract StorageAccessible { * @return the bytes that were read. */ function getStorageAt(uint256 offset, uint256 length) public view returns (bytes memory) { - bytes memory result = new bytes(length * 32); + // We use `<< 5` instead of `* 32` as SHR / SHL opcode only uses 3 gas, while DIV / MUL opcode uses 5 gas. + bytes memory result = new bytes(length << 5); for (uint256 index = 0; index < length; ++index) { /* solhint-disable no-inline-assembly */ /// @solidity memory-safe-assembly