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

Consider allowing the buyer to refund their tokens after a certain amount of time has passed

Summary

initiateDispute() checks that i_arbiter is not the zero address, meaning the use of an arbiter is optional. In the case where no arbiter exists, tokens can effectively become locked in the contract indefinitely. While it could be argued that this is an inherent risk that must be taken buy the buyer and seller, it can be avoided by allowing the buyer to withdraw the tokens after a predetermined amount of time has passed.

Impact

In the event that no arbiter is used, and the seller refuses to adhere to the agreement, the tokens would be stranded in the Escrow contract indefinitely, as the buyer would have no incentive to release the tokens to the seller, and likely wouldn't want to.

Tools Used

Manual review

Recommendations

+ uint256 private immutable i_deadline;
constructor(
uint256 price,
IERC20 tokenContract,
address buyer,
address seller,
+ uint256 deadline,
address arbiter,
uint256 arbiterFee
) {
if (address(tokenContract) == address(0)) revert Escrow__TokenZeroAddress();
if (buyer == address(0)) revert Escrow__BuyerZeroAddress();
if (seller == address(0)) revert Escrow__SellerZeroAddress();
if (arbiterFee >= price) revert Escrow__FeeExceedsPrice(price, arbiterFee);
if (tokenContract.balanceOf(address(this)) < price) revert Escrow__MustDeployWithTokenBalance();
+ if (deadline < block.timestamp) revert Escrow__DeadlineInPast();
i_price = price;
i_tokenContract = tokenContract;
i_buyer = buyer;
i_seller = seller;
i_arbiter = arbiter;
i_arbiterFee = arbiterFee;
+ i_deadline = deadline;
}
+ function withdraw() external onlyBuyer inState(State.Created) {
+ require(block.timestamp > i_deadline, "deadline not met");
+ s_state = State.Expired;
+
+ i_tokenContract.safeTransfer(i_buyer, i_tokenContract.balanceOf(address(this)));
+ }

Support

FAQs

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