Gas Optimizations List
Number |
Optimization Details |
Instances |
[G-01] |
Use assembly for address(0) comparison to save gas |
4 |
[G-02] |
Use nested if and, avoid multiple check combinations using && |
1 |
[G-03] |
Function calls can be cached rather than re calling , saves gas |
1 |
Total 3 issues.
[G-01] Use assembly for address(0)
comparison to save gas
4 instances - 1 file:
Instance#1-4 :
File: /src/Escrow.sol
40: if (address(tokenContract) == address(0)) revert Escrow__TokenZeroAddress();
41: if (buyer == address(0)) revert Escrow__BuyerZeroAddress();
42: if (seller == address(0)) revert Escrow__SellerZeroAddress();
...
103: if (i_arbiter == address(0)) revert Escrow__DisputeRequiresArbiter();
src/Escrow.sol#L40-L42 src/Escrow.sol#L103
Recommended Changes: Make a internal pure function and write assembly logic there. Call whenever
comparison with address(0) needed. It will make code modular and save some gas also during comparison.
- 40: if (address(tokenContract) == address(0)) revert Escrow__TokenZeroAddress();
+ 40: if (_checkAddressZero(address(tokenContract))) revert Escrow__TokenZeroAddress();
- 41: if (buyer == address(0)) revert Escrow__BuyerZeroAddress();
+ 41: if (_checkAddressZero(buyer)) revert Escrow__BuyerZeroAddress();
- 42: if (seller == address(0)) revert Escrow__SellerZeroAddress();
+ 42: if (_checkAddressZero(seller)) revert Escrow__SellerZeroAddress();
....
-103: if (i_arbiter == address(0)) revert Escrow__DisputeRequiresArbiter();
+103: if (_checkAddressZero(i_arbiter)) revert Escrow__DisputeRequiresArbiter();
....
//@audit adding 1 pure function to check for address(0) using assembly to save some gas
+ function _checkAddressZero(
+ address _address
+ ) internal pure returns (bool isAddressZero) {
+ assembly {
+ isAddressZero := eq(_address, 0)
+ }
+ }
[G-02] Use nested if and, avoid multiple check combinations using &&
Using nested if is cheaper gas wise than using && multiple check combinations. There are more advantages, such as easier
to read code and better coverage reports.
1 instance - 1 file:
Instance#1:
File: /src/Escrow.sol
67: if (msg.sender != i_buyer && msg.sender != i_seller) {
68: revert Escrow__OnlyBuyerOrSeller();
69: }
src/Escrow.sol#L67-L69
Recommended Changes: Use nested if
- 67: if (msg.sender != i_buyer && msg.sender != i_seller) {
+ if (msg.sender != i_buyer) {
+ if (msg.sender != i_seller) {
68: revert Escrow__OnlyBuyerOrSeller();
69: }
+ }
[G-03] Function calls can be cached rather than re calling save gas
1 instance - 1 file:
Instance#1 : External contract i_tokenContract.balanceOf(address(this))
can be called once to save gas instead of re-calling in line 125. We can use tokenBalance - totalFee
as remaining tokenBalance of this Escrow contract. Because from above only buyerAward
and i_arbiterFee
is transferred from this contract at line 120 and 123 whose total is totalFee
at line 111.
File: /src/Escrow.sol
110: uint256 tokenBalance = i_tokenContract.balanceOf(address(this));
111: uint256 totalFee = buyerAward + i_arbiterFee;
if (totalFee > tokenBalance) {
revert Escrow__TotalFeeExceedsBalance(tokenBalance, totalFee);
}
s_state = State.Resolved;
emit Resolved(i_buyer, i_seller);
119: if (buyerAward > 0) {
i_tokenContract.safeTransfer(i_buyer, buyerAward);
}
122: if (i_arbiterFee > 0) {
i_tokenContract.safeTransfer(i_arbiter, i_arbiterFee);
}
125: tokenBalance = i_tokenContract.balanceOf(address(this));
/src/Escrow.sol#L110-L125
Recommended Changes: Instead of re-calling replace it with tokenBalance - totalFee
because totalFee
is transferred from this contract in line 120 and 123 combined, remaining will be the token balance. This subtraction can be marked unchecked due to line 112 if condition it can't underflow.
110: uint256 tokenBalance = i_tokenContract.balanceOf(address(this));
111: uint256 totalFee = buyerAward + i_arbiterFee; // Reverts on overflow
...
- 125: tokenBalance = i_tokenContract.balanceOf(address(this));
+ unchecked{
+ 125: tokenBalance = tokenBalance - totalFee;
+ }