Description
When a user tries to buy an order, the buyOrder functionality is set up in such a way that it takes out 3% from the priceInUSDC amount, stores this amount as protocolFees and sends the remainder to the order seller as sellerReceives.
The presence of this logic which is not defined in the protocol documentation will make the order seller to receive less amount than what has been initially listed in the order, thereby losing a part of his funds to fees.
Risk
Likelihood:
This occurs whenever a user buys an existing sellOrder
Impact:
This affects the seller's funds
Proof of Concept
1. A user creates a `sellOrder` and includes a desired price in USDC to sell at.
2. Another user decides to buy that order by calling `buyOrder`
3. The validation checks are done and when successful, he proceeds to buy the order
4. The `buyOrder` function transfers `3%` of the `sellOrder` USDC amount as fee from the buyers wallet to the `OrderBook` smart contract
5. The `buyOrder` then deducts this fee from the `priceInUSDC`, storing it as `sellerReceives` variable and transfers `sellerReceives` from the buyer to the seller.
Place this in the TestOrderBook.t.sol Test Suite
function test_userReceivesLessThanPriceInUSDC() public {
vm.startPrank(bob);
weth.approve(address(book), 2e18);
uint256 bobId = book.createSellOrder(
address(weth),
2e18,
5_000e6,
2 days
);
vm.stopPrank();
vm.startPrank(dan);
usdc.approve(address(book), 200_000e6);
book.buyOrder(bobId);
assert(usdc.balanceOf(bob) < 5_000e6);
console.log(
"Bob's USDC balance after Dan buys his order: ",
usdc.balanceOf(bob)
);
}
**Recommended Mitigation:**
Recommended Mitigation
Remove the fee logic entirely. Another alternative is to set up the `createOrder` function in such a way that it adds the supposed fee to the `amountToSell` so that when a user buys that order, the buyer covers the fee charge.
function createSellOrder(
address _tokenToSell,
uint256 _amountToSell,
uint256 _priceInUSDC,
uint256 _deadlineDuration
) public returns (uint256) {
.
.
.
+ uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
+ uint256 finalAmountToSell = _amountToSell + protocolFee;
+ uint256 finalPriceInUSDC = _priceInUSDC + protocolFee;
IERC20(_tokenToSell).safeTransferFrom(
msg.sender,
address(this),
_amountToSell
);
- // Store the order
- orders[orderId] = Order({
- id: orderId,
- seller: msg.sender,
- tokenToSell: _tokenToSell,
- amountToSell: _amountToSell,
- priceInUSDC: _priceInUSDC,
- deadlineTimestamp: deadlineTimestamp,
- isActive: true
- });
+ // Store the order
+ orders[orderId] = Order({
+ id: orderId,
+ seller: msg.sender,
+ tokenToSell: _tokenToSell,
+ amountToSell: finalAmountToSell,
+ priceInUSDC: finalPriceInUSDC,
+ deadlineTimestamp: deadlineTimestamp,
+ isActive: true
+ });
.
.
.
}
```