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

The seller can DoS resolveDispute()

Summary

The seller can prevent resolveDispute() from working by adding himself to the blocklist and funneling a small amount of token into the contract every time so that the call to resolveDispute() will keep failing.

Vulnerability Details

Should a dispute arises, the arbiter can call resolveDispute() and attempt to resolve the dispute. Note that the token amount goes three ways, to the buyer, the arbiter, and the remaining to the seller.

function resolveDispute(uint256 buyerAward) external onlyArbiter nonReentrant inState(State.Disputed) {
uint256 tokenBalance = i_tokenContract.balanceOf(address(this));
uint256 totalFee = buyerAward + i_arbiterFee; // Reverts on overflow
if (totalFee > tokenBalance) {
revert Escrow__TotalFeeExceedsBalance(tokenBalance, totalFee);
}
s_state = State.Resolved;
emit Resolved(i_buyer, i_seller);
if (buyerAward > 0) {
i_tokenContract.safeTransfer(i_buyer, buyerAward);
}
if (i_arbiterFee > 0) {
i_tokenContract.safeTransfer(i_arbiter, i_arbiterFee);
}
tokenBalance = i_tokenContract.balanceOf(address(this));
if (tokenBalance > 0) {
i_tokenContract.safeTransfer(i_seller, tokenBalance);
}
}

For this DoS to work, three things must be present.

  • The token does have a blocklist

  • The sender is on the blocklist.

  • The escrow uses an Arbiter

Here's is how the seller can DoS the function. Context: The buyer creates an escrow and puts 50,000 USDC inside. He sets the Arbiter address and the arbiter fee is $100.

  1. The seller immediately calls initiateDispute()

  2. The Arbiter attempts to call resolveDispute(). In order for the function to work, the Arbiter must put in 49,900 USDC as the parameter because his fees is $100, so that adds up to $50,000. In an ideal scenario, the Buyer gets back 49,900, the Arbiter gets $100 and the seller gets $0.

  3. Before the Arbiter calls resolveDispute(), the seller uses another account and deposits 1 wei of USDC into the contract. Now, the Arbiter has to change his input parameter to be lesser otherwise the contract will revert. Even if the arbiter puts a lower amount, there is an extra 1 wei of USDC that is sent to the seller.

  4. Because the seller is in the USDC blacklist, the transfer of 1 wei of USDC will always fail and thus the resolveDispute() function will fail.

  5. Every time the Arbiter changes the parameter in resolveDispute(), the seller can frontrun the Arbiter by depositing 1 wei to ensure that the transaction fails.

Impact

The buyer cannot get back his amount. Depending on the sum the buyer puts inside the contract, the DoS may be very lucrative. Also, the DoS attack is not expensive at all.

Tools Used

Manual Review

Recommendations

There should be another function present in the contract to allow the Arbiter to withdraw the balance inside the contract since the Arbiter is trustable anyways. The Arbiter can do the calculation off chain and transfer the contract balance back to the buyer if such a DoS attack happens.

Support

FAQs

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