Root + Impact
Description
function buyOrder(uint256 _orderId) public {
Order storage order = orders[_orderId];
if (order.seller == address(0)) revert OrderNotFound();
if (!order.isActive) revert OrderNotActive();
if (block.timestamp >= order.deadlineTimestamp) revert OrderExpired();
order.isActive = false;
uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
uint256 sellerReceives = order.priceInUSDC - protocolFee;
@> iUSDC.safeTransferFrom(msg.sender, address(this), protocolFee);
@> iUSDC.safeTransferFrom(msg.sender, order.seller, sellerReceives);
IERC20(order.tokenToSell).safeTransfer(msg.sender, order.amountToSell);
totalFees += protocolFee;
emit OrderFilled(_orderId, msg.sender, order.seller);
}
Risk
Likelihood:
Impact:
Proof of Concept
Here in the sample step by step scenario you can notice that the buyer should have equivalent USDC balance to process two transfer froms and in case the buyer has low balance then the transaction will revert.
order.priceInUSDC = 1000
FEE = 3
PRECISION = 100
Token being sold: 10 wETH
msg.sender: the buyer
order.seller: the seller
protocolFee = (1000 * 3) / 100 = 30
sellerReceives = 1000 - 30 = 970
Buyer send 30 USDC to the protocol
Buyer then sends 970 USDC to the
Protocol then sends 10 wETH to the buyer
Total fees increased to 30 USDC
Recommended Mitigation
The way to mitigate is to first pull order price in USDC from the buyer and then send to protocol. The contract will then internally calculate protocol fee and the amount that seller will receive which is then transferred to the seller fro
function buyOrder(uint256 _orderId) public {
Order storage order = orders[_orderId];
// Validation checks
if (order.seller == address(0)) revert OrderNotFound();
if (!order.isActive) revert OrderNotActive();
if (block.timestamp >= order.deadlineTimestamp) revert OrderExpired();
order.isActive = false;
+ iUSDC.safeTransferFrom(msg.sender, address(this), order.priceInUSDC);
uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
uint256 sellerReceives = order.priceInUSDC - protocolFee;
- iUSDC.safeTransferFrom(msg.sender, address(this), protocolFee);
- iUSDC.safeTransferFrom(msg.sender, order.seller, sellerReceives);
+ iUSDC.safeTransfer(order.seller, sellerReceives);
IERC20(order.tokenToSell).safeTransfer(msg.sender, order.amountToSell)
totalFees += protocolFee;
emit OrderFilled(_orderId, msg.sender, order.seller);
}