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

Med - Funds in Disputed Escrow are locked permanently if Arbiter is unavailable

Summary

The arbiter is "trusted", which means he will not undertake any malicious actions. However, it does not mean the arbiter is immortal nor invulnerable.

If through unfortunate circumstances the arbiter would be permanently unavailable, the funds in a disputed escrow will be locked permanently.

Vulnerability Details

When a seller and a buyer are in conflict and call initiateDispute(), the arbiter is the only person who can resolve the dispute and distribute the funds.

When, through circumstances beyond his control, he is unable to perform the arbitrage, then there is no recourse possible and the funds will be permanently locked and lost to the seller and buyer.

Examples of said causes:

  • Vehicle accident

  • Sickness

  • 5$-wrench attack

  • Suicide

  • Natural disasters

  • Kidnapped by the CCP

  • etc..

Assuming that CodeHawks will be successful as a platform for private audits, there will be dozens if not hundreds of escrows created, disputed and confirmed every month. Over time, the aforementioned case will unfortunately happen and there is currently no mechanism to unblock the funds.

Impact

Medium

All funds in the escrow are permanently locked so the impact is certainly High but since the occurrence is very low, so we submitted this finding as Medium.

Tools Used

Manual review

Recommend Mitigation

Implement logic whereby the seller and buyer, after mutual agreement, can invoke an emergency function to resolve the escrow and split the funds.

bool private emergencyApprovalSeller;
bool private emergencyApprovalBuyer;
modifier onlySeller() {
if (msg.sender != i_seller) {
revert Escrow__OnlySeller();
}
_;
}
function setEmergencyApprovalSeller() external onlySeller inState(State.Disputed){
emergencyApprovalSeller = true;
}
function setEmergencyApprovalbuyer() external onlyBuyer inState(State.Disputed){
emergencyApprovalBuyer = true;
}
function emergencyResolveDispute() external nonReentrant inState(State.Disputed) {
require(emergencyApprovalSeller && emergencyApprovalBuyer, "Approval from both Seller and Buyer is required in order to invite the emergencyResolveDispute");
s_state = State.Resolved;
emit emergencyResolved(i_buyer, i_seller);
i_tokenContract.safeTransfer(i_buyer,i_tokenContract.balanceOf(address(this))/2);
i_tokenContract.safeTransfer(i_seller,i_tokenContract.balanceOf(address(this))/2);
}

Support

FAQs

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