Some of the checks in the constructor of the Escrow Contract are unreachable:
40: if (address(tokenContract) == address(0)) revert Escrow__TokenZeroAddress(); can never be reached because there would already have been a revert in the EscrowFactory Contract since tokens from this address are already transferred.
41: if (buyer == address(0)) revert Escrow__BuyerZeroAddress(); cannot be reached because buyer in EscrowFactory is always passed as msg.sender, and msg.sender cannot be the zero address.
Recommendation:
Move all the checks to the EscrowFactory Contract, as it also saves gas when all the checks are performed before deploying a new Escrow Contract.
File: src/EscrowFactory
20: function newEscrow(
21: uint256 price,
22: IERC20 tokenContract,
23: address seller,
24: address arbiter,
25: uint256 arbiterFee,
26: bytes32 salt
27: ) external returns (IEscrow) {
28: if (address(tokenContract) == address(0)) revert Escrow__TokenZeroAddress();
29: if (seller == address(0)) revert Escrow__SellerZeroAddress();
30: if (arbiterFee >= price) revert Escrow__FeeExceedsPrice(price, arbiterFee);
31: address computedAddress = computeEscrowAddress(
32: type(Escrow).creationCode,
33: address(this),
34: uint256(salt),
35: price,
36: tokenContract,
37: msg.sender,
38: seller,
39: arbiter,
40: arbiterFee
41: );
42: tokenContract.safeTransferFrom(msg.sender, computedAddress, price);
43: if (tokenContract.balanceOf(computedAddress) < price) revert Escrow__MustDeployWithTokenBalance();
44: Escrow escrow = new Escrow{salt: salt}(
45: price,
46: tokenContract,
47: msg.sender,
48: seller,
49: arbiter,
50: arbiterFee
51: );
52: if (address(escrow) != computedAddress) {
53: revert EscrowFactory__AddressesDiffer();
54: }
55: emit EscrowCreated(address(escrow), msg.sender, seller, arbiter);
56: return escrow;
57: }
The buyer address does not need to be checked as it is always msg.sender.
Please note: The custom errors needed for the checks must be copied from IEscrow.sol to IEscrowFactory.sol.
Required error messages from IEscrow.sol:
8: error Escrow__FeeExceedsPrice(uint256 price, uint256 fee);
9: error Escrow__OnlyBuyer();
10: error Escrow__OnlyBuyerOrSeller();
11: error Escrow__OnlyArbiter();
12: error Escrow__InWrongState(State currentState, State expectedState);
13: error Escrow__MustDeployWithTokenBalance();
14: error Escrow__TotalFeeExceedsBalance(uint256 balance, uint256 totalFee);
15: error Escrow__DisputeRequiresArbiter();
16: error Escrow__TokenZeroAddress();
17: error Escrow__BuyerZeroAddress();
18: error Escrow__SellerZeroAddress();
Gas Savings
Gas saving when attempting to deploy a new Escrow Contract with the seller's address as the zero address.
Gas consumption of the described scenario with the current checks in the constructor:
| src/EscrowFactory.sol:EscrowFactory contract |
|
|
|
|
|
| Function Name |
min |
avg |
median |
max |
# calls |
| newEscrow |
76396 |
76396 |
76396 |
76396 |
1 |
Gas consumption of the described scenario with the proposed checks in EscrowFactory:
| src/EscrowFactory.sol:EscrowFactory contract |
|
|
|
|
|
| Function Name |
min |
avg |
median |
max |
# calls |
| newEscrow |
410 |
410 |
410 |
410 |
1 |
In this scenario, 75986 gas could be saved. Similar amounts of gas can be saved if other checks revert since the checks are performed before executing the code to deploy the Escrow Contracts.