The settleAskMaker
function does not return the full collateral to ask makers when they settle their points, particularly when the offer's points are only partially used. This results in ask makers receiving only the sales revenue that was already available for withdraw, not the full amount of collateral they are entitled to, leaving part of their collateral stuck in the contract.
When creating ask offers, makers are required to deposit collateral equivalent to at least 100% of the value they are offering:
This collateral acts as a security measure and can be liquidated by offer takers if the ask makers do not settle their points during the settlement period, as outlined in closeBidTaker.
During the settlement period, ask makers can call the settleAskMaker function to settle the points they sold. The issue arises when the offer’s points are partially used (offer.usedPoints < offer.points
). If the maker settles all the used points, the function does not return the full collateral back to the maker:
As shown above, if the ask maker settles all his used points, and the offer points were used partially, the offer status is Ongoing
and the following block will be executed to return the collateral back to the offer maker:
In the code above, if the ask maker settles all the used points and the offer points were partially used, the logic executed will refund only the usedAmount
(the amount corresponding to the used points) as sales revenue, not the full collateral that was initially deposited. This is incorrect because the sales revenue is already available for the maker’s withdrawal once the points are sold, as handled in PreMarket::createTaker
:
Consider the following example, which is demonstrated by the test case below:
Alice, an ask-offer maker, lists 1,000 points for sale at $1 per unit and deposits $1,000 as collateral.
Bob, a buyer, purchases 500 points from Alice for $500. Alice [can withdraw the $500 immediately](https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/PreMarkets.sol#L934-L940)
The owner updates market info, sets the TGE event, and the settlment period starts.
Alice needs to settle only 500 points for Bob. So, she calls DeliveryPlace::settleAskMaker
4.1 Because alice's offer is Ongoing
and she settles all required points, the following block from settleAskMaker
will be executed:
Alice will only receive the sales revenue of 500 USDC, which was already available after Bob’s purchase, instead of the full 1,000 USDC collateral. This results in Alice not receiving the full collateral amount she is entitled to.
Please, notice that Alice had to get at the end 1500 USDC (500 USDC from Bob's purchase, and 1000 USDC back from the deposited collateral since she settled all the used points). But, she got 1000 USDC only (500 USDC from collateral is not refunded)
To reproduce to test below, some parts of the codebase need to be updated given that there are other vulnerabilities connected here:
Ongoing
status needs to be updated after someone takes the offer (explained in detail in another report):
Include check for Ongoing
status on DeliveryPlace::settleAskMaker
(explained in details in another report):
Fix the vulnerability that allows users to keep withdrawing in TokenManager::withdraw
(explained in details in another report):
Now, copy and paste the following test case into test/PreMarkets.t.sol
:
The full collateral is not returned to ask makers after settling their points, leaving part of their collateral stuck in the contract.
The settleAskMaker
function is incorrectly crediting the maker with revenue that is already available for withdrawal instead of returning the collateral. Below is a suggested fix for the settleAskMaker
function:
Valid high, in settleAskTaker/settleAskMaker, if the original offer maker performs a partial final settlement, the existing checks [here](https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/DeliveryPlace.sol#L356-L358) and [here](https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/DeliveryPlace.sol#L230-L232) will cause an revert when attempting to complete a full settlement, resulting in their collateral being locked and requiring a rescue from the admin. To note, although examples in the documentation implies settlement in a single click, it is not stated that partial settlements are not allowed, so I believe it is a valid user flow.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.