Skip to content

Latest commit

 

History

History
189 lines (135 loc) · 4.74 KB

File metadata and controls

189 lines (135 loc) · 4.74 KB

Keen Metal Loris

Medium

A new bid will impact the buy/sell rate of previous lowest bidder.

Summary

In Auction, when sum of sellCouponAmount of all bidders is over totalBuyCouponAmount by new bid, it impact buy/sell rate of lowest bidder.

Root Cause

https://github.com/sherlock-audit/2024-12-plaza-finance/blob/main/plaza-evm/src/Auction.sol#L275

function removeExcessBids() internal {
    ...
    if (amountToRemove >= sellCouponAmount) {
        ...
    } else {
        uint256 proportion = (amountToRemove * 1e18) / sellCouponAmount;
        
        // Reduce the current bid's amounts
        currentBid.sellCouponAmount = sellCouponAmount - amountToRemove;
        currentCouponAmount -= amountToRemove;

        uint256 reserveReduction = ((currentBid.buyReserveAmount * proportion) / 1e18);
        currentBid.buyReserveAmount = currentBid.buyReserveAmount - reserveReduction;
        totalSellReserveAmount -= reserveReduction;
       
        ... 
    }
    ...
}

uint256 proportion = (amountToRemove * 1e18) / sellCouponAmount; ... uint256 reserveReduction = ((currentBid.buyReserveAmount * proportion) / 1e18);

By the Precision Loss / Rounding Error, the buy/sell rate of previous lowest bidder is changed.

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

No response

Impact

Due to the "Precision Loss / Rounding" issue, the bidder may be removed from bidding. or if the bidder become winner, the auction needs to pay more.

PoC

    address BidderFirst = address(1); //user 1

    vm.startPrank(BidderFirst);
    usdc.mint(BidderFirst, 398000000000);
    usdc.approve(address(auction), 398000000000);
    auction.bid(1000, 398000000000);
    vm.stopPrank();


    address BidderSecond = address(2); //user 2
    uint256 secondBidAmount = 4816;
    uint256 secondSellAmount = 602000000000;

    vm.startPrank(BidderSecond);
    usdc.mint(BidderSecond, secondSellAmount);
    usdc.approve(address(auction), secondSellAmount);
    auction.bid(secondBidAmount, secondSellAmount);
    vm.stopPrank();



    (, uint256 buyAmountBefore, uint256 buySellAmountBefore,,,) = auction.bids(2);
    
    console2.log("-----before some is removed------");
    console2.log("-- buy Amount : ");
    console2.log(buyAmountBefore);
    console2.log("-- sell Amount : ");
    console2.log(buySellAmountBefore);
    console2.log("-- sell/buy rate : ");
    console2.log(buySellAmountBefore / buyAmountBefore);
    

    address BidderThird = address(3);  //user 3
    vm.startPrank(BidderThird);
    usdc.mint(BidderThird, 2000000000);
    usdc.approve(address(auction), 2000000000);
    auction.bid(14, 2000000000);
    vm.stopPrank();


    (, uint256 buyAmountAfter, uint256 buySellAmountAfter,,,) = auction.bids(2);
    
    console2.log("-----after some is removed------");
    console2.log("-- buy Amount : ");
    console2.log(buyAmountAfter);
    console2.log("-- sell Amount : ");
    console2.log(buySellAmountAfter);
    console2.log("-- sell/buy rate : ");
    console2.log(buySellAmountAfter / buyAmountAfter);
    console2.log("-- predict buy amount : ");
    console2.log(buyAmountBefore * buySellAmountAfter / buySellAmountBefore);

The result is following. -----before some is removed------ -- buy Amount : 4816 -- sell Amount : 602000000000 -- sell/buy rate : 125000000 -----after some is removed------ -- buy Amount : 4801 -- sell Amount : 600000000000 -- sell/buy rate : 124973963 -- predict buy amount : 4800

When the user2 bid, the sell/buy rate is 125000000, but after new bid, the rate is 124973963. So the buy amount is changed 4801 instead of 4800.

Mitigation

In Auction.sol

function removeExcessBids() internal {
    ...
    if (amountToRemove >= sellCouponAmount) {
        ...
    } else {
-      uint256 proportion = (amountToRemove * 1e18) / sellCouponAmount;
        
        // Reduce the current bid's amounts
        currentBid.sellCouponAmount = sellCouponAmount - amountToRemove;
        currentCouponAmount -= amountToRemove;

-       uint256 reserveReduction = ((currentBid.buyReserveAmount * proportion) / 1e18);

+      uint256 reserveReduction = (amountToRemove * currentBid.buyReserveAmount) / sellCouponAmount;

        currentBid.buyReserveAmount = currentBid.buyReserveAmount - reserveReduction;
        totalSellReserveAmount -= reserveReduction;
       
        ... 
    }
    ...
}

After modify the code, the result of Poc is following.

-----before some is removed------ -- buy Amount : 4816 -- sell Amount : 602000000000 -- sell/buy rate : 125000000 -----after some is removed------ -- buy Amount : 4800 -- sell Amount : 600000000000 -- sell/buy rate : 125000000 -- predict buy amount : 4800