Summary
resolveDispute()
might revert by malicious sellers with the tokens with blocklists.
Vulnerability Details
Some well-known tokens have blocklists.
The seller can make resolveDispute()
revert with 1 wei using frontrunning.
function resolveDispute(uint256 buyerAward) external onlyArbiter nonReentrant inState(State.Disputed) {
uint256 tokenBalance = i_tokenContract.balanceOf(address(this));
uint256 totalFee = buyerAward + i_arbiterFee;
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);
}
}
-
Create an escrow with the token with a blacklist
-
The seller disputes right away or waits until the buyer disputes without doing his task.
-
When the arbiter is going to call resolveDispute() for the buyer’s preference, the seller transfers 1 wei to the escrow by frontrunning
-
Then the token balance for the seller will be positive even if the arbiter was going to transfer all funds to the buyer, and the seller can DOS by reverting the transfer.
Impact
The buyers wouldn't receive back their funds after disputes.
Tools Used
Manual Review
Recommendations
We can modify resolveDispute()
to accept type(uint256).max
and transfer all funds to the buyer at that time.
Then the seller won't have an opportunity to DOS because there is no transfer for him.
function resolveDispute(uint256 buyerAward) external onlyArbiter nonReentrant inState(State.Disputed) {
uint256 tokenBalance = i_tokenContract.balanceOf(address(this));
if (buyerAward == type(uint256).max) {
buyerAward = tokenBalance - i_arbiterFee;
}
uint256 totalFee = buyerAward + i_arbiterFee;
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);
}
}