40,000 USDC
View results
Submission Details
Severity: gas
Valid

GAS OPTIMIZATIONS (1300 gas save)

GAS OPTIMIZATIONS

All the gas optimizations are determined based opcodes and sample tests.

Count Issues Instances Gas Saved
[G-1] Reduce gas usage by moving to Solidity 0.8.19 or later 2 -
[G-2] Modifiers are redundant if used only once or not used at all 3 120
[G-3] abi.encode is more efficient than abi.encodePacked 2 180
[G-4] Use nested if and, avoid multiple check combinations 1 15
[G-5] Constructors can be marked payable 1 105
[G-6] The compiler uses opcodes GT and ISZERO for solidity code that uses >, but only requires LT for >= 1 3
[G-7] State variables should be cached in stack variables rather than re-reading them from storage 2 700
[G-8] Use assembly to emit an event 4 152
[G-9] Use assembly to check for address(0) 4 24

Total Gas saves 1300

[G-01] Reduce gas usage by moving to Solidity 0.8.19 or later

There are 2 instances of this issue:

[G-02] Modifiers are redundant if used only once or not used at all

Gas save: 120 Gas

File: /src/Escrow.sol
94: function confirmReceipt() external onlyBuyer inState(State.Created) {
95: s_state = State.Confirmed;
96: emit Confirmed(i_seller);
97:
98: i_tokenContract.safeTransfer(i_seller, i_tokenContract.balanceOf(address(this)));
99: }
File: /src/Escrow.sol
102: function initiateDispute() external onlyBuyerOrSeller inState(State.Created) {
103: if (i_arbiter == address(0)) revert Escrow__DisputeRequiresArbiter();
104: s_state = State.Disputed;
105: emit Disputed(msg.sender);
106: }
File: /src/Escrow.sol
109: function resolveDispute(uint256 buyerAward) external onlyArbiter nonReentrant inState(State.Disputed) {
110: uint256 tokenBalance = i_tokenContract.balanceOf(address(this));
111: uint256 totalFee = buyerAward + i_arbiterFee; // Reverts on overflow
112: if (totalFee > tokenBalance) {
113: revert Escrow__TotalFeeExceedsBalance(tokenBalance, totalFee);
114: }

[G-03] abi.encode is more efficient than abi.encodePacked (Save 180 )

abi.encode uses less gas than abi.encodePacked: the gas saved depends on the number of arguments, with an average of ~90 per argument.

Gas save: 180 gas

There are 2 instances of this issue.

Test :

pragma solidity 0.8.18;
contract Gas {
// 417 gas
function notOptimized() external returns(uint gasUsed){
uint256 value = 123;
uint startGas = gasleft();
abi.encodePacked(value, value);
gasUsed = startGas - gasleft();
}
// 321 gas
function optimized() external returns(uint gasUsed){
uint256 value = 123;
uint startGas = gasleft();
abi.encode(value, value);
gasUsed = startGas - gasleft();
}
}
File:/src/EscrowFactory.sol
76: abi.encodePacked(
77: byteCode, abi.encode(price, tokenContract, buyer, seller, arbiter, arbiterFee)
78: )
File: /src/EscrowFactory.sol
71: abi.encodePacked(

[G-04] Use nested if and, avoid multiple check combinations

Using nested is cheaper than using && multiple check combinations. There are more advantages, such as easier to read code and better coverage reports.

Gas save: 15 gas

File: /src/Escrow.sol
67: if (msg.sender != i_buyer && msg.sender != i_seller) {
68: revert Escrow__OnlyBuyerOrSeller();
69: }

[G-05] Constructors can be marked payable

Payable functions cost less gas to execute, since the compiler does not have to add extra checks to ensure that a payment wasn't provided. A constructor can safely be marked as payable, since only the deployer would be able to pass funds, and the project itself would not pass any funds.

Gas save: 105 gas

[G-06] The compiler uses opcodes GT and ISZERO for solidity code that uses >, but only requires LT for >=,

Gas saves: 3 gas

File: /src/Escrow.sol
112: if (totalFee > tokenBalance) {
113: revert Escrow__TotalFeeExceedsBalance(tokenBalance, totalFee);
114: }

[G-07] State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses

Gas save: 700 gas

File: /src/Escrow.sol
==>//@audit i_seller
94: function confirmReceipt() external onlyBuyer inState(State.Created) {
s_state = State.Confirmed;
emit Confirmed(i_seller);
i_tokenContract.safeTransfer(i_seller, i_tokenContract.balanceOf(address(this)));
}
File: /src/Escrow.sol
==> //@audit i_buyer ,i_arbiterFee and i_seller
109: function resolveDispute(uint256 buyerAward) external onlyArbiter nonReentrant inState(State.Disputed) {
110: uint256 tokenBalance = i_tokenContract.balanceOf(address(this));
111: uint256 totalFee = buyerAward + i_arbiterFee; // Reverts on overflow
112: if (totalFee > tokenBalance) {
113: revert Escrow__TotalFeeExceedsBalance(tokenBalance, totalFee);
114: }
115:
116: s_state = State.Resolved;
117: emit Resolved(i_buyer, i_seller);
118:
119: if (buyerAward > 0) {
120: i_tokenContract.safeTransfer(i_buyer, buyerAward);
121: }
122: if (i_arbiterFee > 0) {
123: i_tokenContract.safeTransfer(i_arbiter, i_arbiterFee);
124: }
125: tokenBalance = i_tokenContract.balanceOf(address(this));
126: if (tokenBalance > 0) {
127: i_tokenContract.safeTransfer(i_seller, tokenBalance);
128: }
129: }

[G-08] Use assembly to emit an event

To efficiently emit events, it's possible to utilize assembly by making use of scratch space and the free memory pointer. This approach has the advantage of potentially avoiding the costs associated with memory expansion.

However, it's important to note that in order to safely optimize this process, it is preferable to cache and restore the free memory pointer.

A good example of such practice can be seen in Solady's codebase.

Gas save: 152 Gas

File: /src/Escrow.sp;
96: emit Confirmed(i_seller);
105: emit Disputed(msg.sender);
117: emit Resolved(i_buyer, i_seller);
File: /src/EscrowFactory.sol
51: emit EscrowCreated(address(escrow), msg.sender, seller, arbiter);

[G-09] Use assembly to check for address(0)

A simple zero address check can be written in assembly to save some gas.

Gas save: 24 gas

File: /src/Escrow.sol
41: if (address(tokenContract) == address(0)) revert Escrow__TokenZeroAddress();
42: if (buyer == address(0)) revert Escrow__BuyerZeroAddress();
43: if (seller == address(0)) revert Escrow__SellerZeroAddress();
103: if (i_arbiter == address(0)) revert Escrow__DisputeRequiresArbiter();

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.