Tadle

Tadle
DeFiFoundry
27,750 USDC
View results
Submission Details
Severity: low
Valid

Several functions will break if missing Virgin status update in `createTaker` is addressed

Vulnerability Details

PRE CONTEXTE: This issue arises after addressing the missing update of the Ongoing status when someone take an offer

The current implementation of createTaker does not update the offer status from Virgin to Ongoing (as explained in other report), after addressing that issue, several functions in the protocols will break and will not work as intended:

  1. The PreMarkets::abortAskOffer function allows ask-offer owners to abort their offers and reclaim their collateral, while simultaneously aborting all ongoing deals with existing takers. Aborting an ask offer should be allowed under the following conditions:

    1. Virgin Status: The offer has no takers, allowing the owner to be refunded the full collateral

    2. Canceled Status: The offer has takers but was subsequently canceled. The owner can still abort the used points.

    3. Ongoing Status: The offer has active takers, and the owner aborts the offer. All existing takers are aborted, and the owner is refunded with his collateral(if there are no sub-trades in case of Turbo mode).

    The issue is that the third scenario (Ongoing status) is not included in the status check, preventing the abortion of ask offers with an Ongoing status:

    function abortAskOffer(address _stock, address _offer) external {
    // ...
    if (
    offerInfo.offerStatus != OfferStatus.Virgin &&
    offerInfo.offerStatus != OfferStatus.Canceled // @audit `Ongoing` status is not allowed
    ) {
    revert InvalidOfferStatus();
    }
    // ...
    }
  2. Bid offer makers deposit the amount of points they are willing to purchase in advance when creating an offer using the createOffer function in PreMarket. Sellers (bid takers) then call createTaker to sell points to bid offer makers when the market is online.

    During the settlement period, bid offer makers are expected to use the DeliveryPlace::closeBidOffer function to withdraw the collateral for the unused pointsthose points that were not bought. However, the function currently only allows refunds for offers that remain in the Virgin status:

    function closeBidOffer(address _offer) external {
    (
    OfferInfo memory offerInfo,
    MakerInfo memory makerInfo,
    ,
    MarketPlaceStatus status
    ) = getOfferInfo(_offer);
    if (_msgSender() != offerInfo.authority) {
    revert Errors.Unauthorized();
    }
    if (offerInfo.offerType == OfferType.Ask) {
    revert InvalidOfferType(OfferType.Bid, OfferType.Ask);
    }
    if (
    status != MarketPlaceStatus.AskSettling &&
    status != MarketPlaceStatus.BidSettling
    ) {
    revert InvaildMarketPlaceStatus();
    }
    >>> if (offerInfo.offerStatus != OfferStatus.Virgin) { // @audit
    revert InvalidOfferStatus();
    }
    // ...
    }

    As seen above, the check if (offerInfo.offerStatus != OfferStatus.Virgin) revert InvalidOfferStatus() restricts refunds to offers that have not been traded (i.e., Virgin status). If an offer’s points were partially used—meaning a bid taker sold some, but not all, of the offer points—and the market enters the settlement period, bid makers will be unable to retrieve the deposit for the unused points, causing these funds to remain stuck in the contract.

  3. Ask offer settlements should be initiated by the offer maker under one of the following conditions:

    1. Virgin Status: The offer has not been traded (no takers), allowing the offer maker to settle 0 points and receive their full collateral, as demonstrated by this code block in settleAskMaker

    2. Closed Status: The offer was canceled, but some points were used before cancellation, which still need to be settled.

    3. Ongoing Status: The offer has takers, and offer points need to settled.

    The problem lies in the fact that the third scenario (Ongoing status) is not included in the status check, preventing the settlement of ask offers with an Ongoing status:

    function settleAskMaker(address _offer, uint256 _settledPoints) external {
    // ...
    if (
    offerInfo.offerStatus != OfferStatus.Virgin &&
    offerInfo.offerStatus != OfferStatus.Canceled
    ) {
    revert InvalidOfferStatus();
    }
    // ...
    }
  4. The PreMarkets::closeOffer function allows offer owners to cancel their offers and receive a refund based on the unused points amount, calculated by getRefundAmount. Cancellation of an offer should be permissible in one of the following scenarios (as confirmed by the sponsor):

    1. Virgin Status: The offer has no takers, and the owner is refunded the full collateral.

    2. Ongoing Status: The offer has takers, and the owner is refunded with the unused points.

    The problem is that the closeOffer function currently restricts cancellations to offers with a Virgin status only:

    function closeOffer(address _stock, address _offer) external {
    OfferInfo storage offerInfo = offerInfoMap[_offer];
    StockInfo storage stockInfo = stockInfoMap[_stock];
    if (stockInfo.offer != _offer) {
    revert InvalidOfferAccount(stockInfo.offer, _offer);
    }
    if (offerInfo.authority != _msgSender()) {
    revert Errors.Unauthorized();
    }
    >>> if (offerInfo.offerStatus != OfferStatus.Virgin) {
    revert InvalidOfferStatus();
    }
    // ...
    }

    As shown above, the function enforces that the offer must have a Virgin status (no takers) for it to be canceled, but it does not allow the cancellation of offers with an Ongoing status, preventing the owner from canceling the offer and receiving a refund for the unused points.

Impact

Several functions will break if the missing Virgin status update in createTaker is addressed

Tools Used

Manual Review

Recommendations

After addressing the missing update of offer status from Virgin to Ongoing in createTaker, consider iterating over the mentioned functions above and include the status Ongoing in the offer's status check

Updates

Lead Judging Commences

0xnevi Lead Judge 12 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-PreMarkets-createTaker-Ongoing-Status

The reason for severity for this issue and duplicates are very similar to issue #1164. However, in this case, the issues correctly identified that offer statuses should be updated accordingly based on points transaction (partially filled orders = `Ongoing`, fully filled orders = `Filled`). There is currently no impact on the tadle system since the above two statuses are unused, and while sementically `Virgin` status is not the correct status representing whether taker orders can be created agains maker offers, it can still be performed without a DoS. However, if we are basing it off of the correct status implementation (i.e. `Ongoing` phase appropriately updated when takers create taker offers), then the DoS will occur, essentially blocking any taker offers from being created by subsequent takers for partially filled orders. All issues that does not mention the DoS impact will be low severity, since they still correctly highlighted the wrong status accounting. All issues that mention the possible bypass of `Virgin` status is incorrect, because the usedPoint checks will always ensure points are filled only up to the points posted for offer as seen [here](https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/PreMarkets.sol#L180-L186). Note for downgrade to low severity: Agree with appeals and low severity, this is more of a status accounting error and does not have any impact, since the statuses consistently do not utilize a switch from Vigin to Ongoing/Filled and the protocol can function appropriately even without the use of such statuses (presuming other bugs are fixed), the DoS impact will not occur.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.