sherlock-audit / 2023-06-dinari-judging

5 stars 4 forks source link

toshii - The order lifecycle outlined in DirectBuyIssuer will revert in most cases for filled orders #79

Closed sherlock-admin closed 1 year ago

sherlock-admin commented 1 year ago

toshii

medium

The order lifecycle outlined in DirectBuyIssuer will revert in most cases for filled orders

Summary

In most cases, checks whether the filled amount is less than or equal to the amount taken from escrow will revert given the outlined order lifecycle for the DirectBuyIssuer contract, resulting in attempts to fill orders failing.

Vulnerability Detail

The order lifecycle outlined in the natspec comments detail the following flow:

/// 1. User requests an order (requestOrder)
/// 2. Operator takes escrowed payment (takeEscrow)
/// 3. [Optional] Operator partially fills the order (fillOrder)
/// 4. [Optional] User requests cancellation (requestCancel)
/// 5. Operator returns unused payment to contract (returnEscrow)

Effectively, the operator will first take a reasonable amount of funds from the escrow for an order. Due to changes in market price, a different amount of these funds might be required to purchase a given amount of assetToken. In practice this will often mean that the amount taken from escrow is not the same as the fillAmount used to purchase some receivedAmount of assetToken. Then, based on this lifecycle, the excess funds taken from escrow will be returned, after the operator fulfills the order.

Based on the order lifecycle, the operator will then fill the order by calling fillOrder, which calls _fillOrderAccounting. In _fillOrderAccounting there is the following check:

uint256 escrow = getOrderEscrow[orderId];
if (fillAmount > orderState.remainingOrder - escrow) revert AmountTooLarge();

The issue with this statement is that effectively if fillAmount is less than the amount taken from escrow, this will revert. However, this will happen in most cases due to changes in market price of the asset from when the operator took funds from escrow, and when they actually purchase the asset. For example, let's assume orderState.remainingOrder is 1000. The operator takes 500 from escrow to buy 5 tokens at a price of 100 per token. The price of each token then increases to 110, meaning that 500 can now only purchase 4 tokens at a total price of 440. The fillAmount here will be 440. In the check we get the following statement: 440 > 1000 - 500 or 440 > 500, which will revert.

Impact

Due to the order lifecycle, most calls to fulfill orders in the DirectBuyIssuer contract will revert, resulting in the inability to fulfill orders.

Code Snippet

Referenced lines of code: https://github.com/sherlock-audit/2023-06-dinari/blob/main/sbt-contracts/src/issuer/DirectBuyIssuer.sol#L114-L127

Tool used

Manual Review

Recommendation

The order lifecycle should be updated so that the excess funds taken from escrow are first returned prior to the operator attempting to fulfill the order.