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

Token prices could get stuck forever if a dispute is started but the arbiter never calls the resolveDispute

Summary

  • Funds can get stuck in the Escrow contract if a dispute is started but the arbiter never calls the resolveDispute

Vulnerability Details

  • When a dispute is initiated, the only way to get the tokens out of the Escrow contract is when the arbiters resolve the dispute, but what about when something happens to the arbiter?

  • Let's say that after one month (or whatever the threshold window to resolve disputes is) the arbiter hasn't called the resolveDispute() because of any reason [lost keys, lack of responsibility, arbiter passes away :-( ], and the reason of why the dispute was initiated has already been sorted out between the buyer and the caller, the funds will get stuck forever depending on the cause for the arbiter to have not already resolved the dispute.

  • In these type of events, the buyer and caller have already come to an agreement, and it would be very unfair for both parties to get their funds stuck because of the arbiter.

Tools Used

Manual Audit

Recommendations

  • Implement a fail-safe path that allows the buyer and seller to come to an agreement without the intervention of the disputer.

  • Allow the buyer & the seller to resolve the dispute by themselves if X time has passed since the Dispute was initiated and hasn't been resolved.

contract Escrow is ... {
+ uint initiatedDisputeTimestap;
+ uint THRESHOLD_TO_SOLVE_DISPUTE;
+ bool buyerAcceptsSolvingDispute;
+ bool sellerAcceptsSolvingDispute;
+ uint256 BUYER_refundsForBuyer;
+ uint256 SELLER_refundsForBuyer;
...
...
...
function initiateDispute() external onlyBuyerOrSeller inState(State.Created) {
if (i_arbiter == address(0)) revert Escrow__DisputeRequiresArbiter();
s_state = State.Disputed;
+ initiatedDisputeTimestap = block.timestamp;
emit Disputed(msg.sender);
}
+ function resolveDisputePeacefully(bool solved, uint _refundForBuyer) external inState(State.Disputed) {
+ if(msg.sender == i_buyer) {
+ buyerAcceptsSolvingDispute = solved;
+ BUYER_refundsForBuyer = _refundForBuyer
+ }
+ else if(msg.sender == i_seller) {
+ sellerAcceptsSolvingDispute = solved
+ SELLER_refundsForBuyer = _refundForBuyer
+ }
+ }
+ function solveDisputeteWithoutDisputer() external nonReentrant inState(State.Disputed) {
+ uint timeBeingDisputed = block.timestamp - initiatedDisputeTimestap;
+ if(timeBeingDisputed > THRESHOLD_TO_SOLVE_DISPUTE) {
+ if( (buyerAcceptsSolvingDispute && sellerAcceptsSolvingDispute) && (BUYER_refundsForBuyer == SELLER_refundsForBuyer ) ) {
+ i_tokenContract.safeTransfer(i_buyer, BUYER_refundsForBuyer);
+ tokenBalance = i_tokenContract.balanceOf(address(this));
+ if (tokenBalance > 0) {
+ i_tokenContract.safeTransfer(i_seller, tokenBalance);
+ }
+ s_state = State.Resolved;
+ }
+ }
+ }
}

Support

FAQs

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