Wide Pistachio Worm
High
the reliance of endAuction
function to the current reserveToken balance of the pool makes the outcome of the auction can be manipulated.
function endAuction() external auctionExpired whenNotPaused {
if (state != State.BIDDING) revert AuctionAlreadyEnded();
if (currentCouponAmount < totalBuyCouponAmount) {
state = State.FAILED_UNDERSOLD;
@> } else if (totalSellReserveAmount >= (IERC20(sellReserveToken).balanceOf(pool) * poolSaleLimit) / 100) {
state = State.FAILED_POOL_SALE_LIMIT;
} else {
state = State.SUCCEEDED;
Pool(pool).transferReserveToAuction(totalSellReserveAmount);
IERC20(buyCouponToken).safeTransfer(beneficiary, IERC20(buyCouponToken).balanceOf(address(this)));
}
emit AuctionEnded(state, totalSellReserveAmount, totalBuyCouponAmount);
}
anyone with enough reserveToken balance or derivative token (lev/bondETH) can manipulate to change the outcome of current auction status to SUCCEEDED
or FAILED_POOL_SALE_LIMIT
(totalSellReserveAmount >= (IERC20(sellReserveToken).balanceOf(pool) * poolSaleLimit) / 100)
the condition above can be manipulated by Pool::create
or Pool::redeem
because these two function change the state of IERC20(sellReserveToken).balanceOf(pool)
at the end of auction duration (typically 10 days) or when the amount of Auction::totalBuyCouponAmount
is already fulfilled.
No response
No response
consider a situation where:
poolSaleLimit
= 90
case if pool balance = 100
and totalSellReserveAmount
= 85
:
the auction would SUCCEEDED. but user with enough derivative token can redeem with the output of ~5.57
reserveToken and then call Auction::endAuction
the result would be
totalSellReserveAmount >= poolBalance * poolSaleLimit/100
85 >= (100 - 5.57) * 90/100
85 >= 94.43 * 90/100
85 >= 84.987 =>>> false =>>> status changed from SUCCEEDED to FAILED_POOL_SALE_LIMIT
case if pool balance = 100
and totalSellReserveAmount
= 60
:
the auction would SUCCEEDED. but user with enough derivative token can redeem with the output of ~33.34
reserveToken and then call Auction::endAuction
the result would be
totalSellReserveAmount >= poolBalance * poolSaleLimit/100
60 >= (100 - 33.34) * 90/100
60 >= 66.66 * 90/100
60 >= 59.994 =>>> false =>>> status changed from SUCCEEDED to FAILED_POOL_SALE_LIMIT
case if pool balance = 100
and totalSellReserveAmount
= 105
:
the auction would FAILED_POOL_SALE_LIMIT. but user with enough reserveToken can call create with the input of ~16.67
reserveToken and then call Auction::endAuction
the result would be
totalSellReserveAmount >= poolBalance * poolSaleLimit/100
105 >= (100 + 16.67) * 90/100
105 >= 116.67 * 90/100
105 >= 105.003 =>>> true=>>> status changed from FAILED_POOL_SALE_LIMIT to SUCCEEDED
all the above manipulation require the user to have relative high amount of derivative/reserve token, but the fact that they can call create/redeem again to get whatever the original amount and token they have, this griefing attack can be considered to only require gas fee paid by attacker
auction result can be manipulated, possibly to prevent the auction from succeeding. making the user loss the potential amount of coupon token from the auction bidder.
No response
when the auction is active (typically 10 days), consider to lock the create
and redeem
function inside the corresponding Pool
contract