Assessment reports>GTE>Threat Model>postFillOrder

Function: postFillOrder(address account, PostFillOrderArgs args)

This function allows to fill a buy and sell order for the account. It can be called by the account itself or an operator authorized for this account. Also, this function is designed to handle matching with existing orders. It provides the execution result to the CLOBManager contract for token settlement and applies fees. The postFillOrder function reverts if the type of the order is FILL_OR_KILL and the order was not fully filled during matching. If the fillOrderType is IMMEDIATE_OR_CANCEL, the order may be partially filled.

Inputs

  • account

    • Control: Full control.

    • Constraints: Should be msg.sender itself or an approved operator for the account.

    • Impact: The account on whose behalf the order is being filled.

  • args

    • Control: Full control.

    • Constraints: args.priceLimit % tickSize == 0 && args.priceLimit != 0.

    • Impact: Contains amount, priceLimit, side, amountIsBase, fillOrderType, and settlement.

Branches and code coverage

Intended branches

  • The order type is FILL_OR_KILL, the order was fully filled, and the args.amountIsBase is true.

  • The order type is FILL_OR_KILL, the order was fully filled, and the args.amountIsBase is false.

  • The order type is IMMEDIATE_OR_CANCEL, the order was partly filled, and the args.amountIsBase is true.

  • The order type is IMMEDIATE_OR_CANCEL, the order was partly filled, and the args.amountIsBase is false.

  • The order type is IMMEDIATE_OR_CANCEL, the order was fully filled, and the args.amountIsBase is true.

  • The order type is IMMEDIATE_OR_CANCEL, the order was fully filled, and the args.amountIsBase is false.

  • The takerFee is equal to the expected amount.

  • The multiple expired orders have been closed and fee was not charged from them.

  • There were multiple matches, and the maker fee is equal to the expected amount.

Negative behavior

  • The order type is FILL_OR_KILL, the order was not fully filled, and the args.amountIsBase is true.

  • The order type is FILL_OR_KILL, the order was not fully filled, and the args.amountIsBase is false.

  • The caller is not an account or operator of the account.

Function call analysis

  • BookLib.assertLimitPriceInBounds(ds, args.priceLimit)

    • What is controllable? args.priceLimit.

    • If the return value is controllable, how is it used and how can it go wrong? This function does not return a value.

    • What happens if it reverts, reenters or does other unusual control flow? Reverts if priceLimit is zero or price % tickSize != 0.

  • BookLib.incrementNextOrderId(ds)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the next order ID.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • OrderLib.toOrder(args, orderId, account)

    • What is controllable? args.side, args.amount, and args.price.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the new Order object.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> BookLib.getBestAsk(ds)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the best minimum price askTree.minimum().

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> OrderLib.isExpired(bestAskOrder)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? Returns true when the cancelTimestamp is not null and is less than the current block.timestamp; otherwise, it returns false.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._removeExpiredAsk(ds, bestAskOrder) -> TransientMakerData.addBaseToken(order.owner, baseTokenAmount)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? This function does not return a value.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here. The baseAmount will be saved for the owner of the expired order in the TransientMakerData memory.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._removeExpiredAsk(ds, bestAskOrder) -> BookLib.removeOrderFromBook(ds, order)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? This function does not return a value.

    • What happens if it reverts, reenters or does other unusual control flow? The metadata.quoteTokenOpenInterest and metadata.baseTokenOpenInterest will be decremented — depends on the side of the order, and order.id will be deleted from the orders list. Also, bidTree and askTree will be updated in addition to orders.nextOrderId and orders.prevOrderId.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._matchIncomingOrder(ds, bestAskOrder, incomingOrder, incomingOrder.amount, amountIsBase) -> FixedPointMathLib.min(matchedBase, incomingOrder.amount)

    • What is controllable? incomingOrder.amount.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the minimum value between matchedBase and incomingOrder.amount.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._matchIncomingOrder(ds, bestAskOrder, incomingOrder, incomingOrder.amount, amountIsBase) -> BookLib.getQuoteTokenAmount(ds, matchedPrice, matchData.baseDelta)

    • What is controllable? If the provided incomingOrder.amount is less than matchedBase, the matchData.baseDelta will be equal to the incomingOrder.amount and fully controlled by the caller.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the quote-tokens amount calculated using the provided matchedPrice and matchData.baseDelta amount. The result can be rounded down to zero if matchData.baseDelta * matchedPrice is less than config.baseSize.

    • What happens if it reverts, reenters or does other unusual control flow? Can revert as a result of overflow during matchData.baseDelta * matchedPrice calculation if matchedPrice or matchData.baseDelta is too large.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._matchIncomingOrder(ds, bestAskOrder, incomingOrder, incomingOrder.amount, amountIsBase) -> FixedPointMathLib.min(matchedBase, BookLib.getBaseTokenAmount(ds, matchedPrice, incomingOrder.amount))

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the minimum amount between the matchedBase and the result of the getBaseTokenAmount function execution, which calculates the base-token amount using the provided-by-the-caller incomingOrder.amount and the matchedPrice.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._matchIncomingOrder(ds, bestAskOrder, incomingOrder, incomingOrder.amount, amountIsBase) -> BookLib.getBaseTokenAmount(ds, matchedPrice, incomingOrder.amount)

    • What is controllable? incomingOrder.amount.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the base-token amount, which is calculated using the provided-by-the-caller incomingOrder.amount and the matchedPrice. Can return zero if quoteAmount * self.config.baseSize is less than price.

    • What happens if it reverts, reenters or does other unusual control flow? Can revert as a result of overflow during quoteAmount * self.config.baseSize calculation if quoteAmount is too large.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._matchIncomingOrder(ds, bestAskOrder, incomingOrder, incomingOrder.amount, amountIsBase) -> BookLib.getQuoteTokenAmount(ds, matchedPrice, matchData.baseDelta)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the quote-tokens amount calculated using the provided matchedPrice and matchData.baseDelta amount. The result can be rounded down to zero if matchData.baseDelta * matchedPrice is less than config.baseSize.

    • What happens if it reverts, reenters or does other unusual control flow? Can revert as a result of overflow during matchData.baseDelta * matchedPrice calculation if matchedPrice or matchData.baseDelta is too large.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._matchIncomingOrder(ds, bestAskOrder, incomingOrder, incomingOrder.amount, amountIsBase) -> TransientMakerData.addQuoteToken(matchedOrder.owner, matchData.quoteDelta)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? This function does not return a value.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here. The matchData.quoteDelta will be saved for the owner of the matched order in the TransientMakerData memory.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._matchIncomingOrder(ds, bestAskOrder, incomingOrder, incomingOrder.amount, amountIsBase) -> TransientMakerData.addBaseToken(matchedOrder.owner, matchData.baseDelta)

    • What is controllable? matchData.baseDelta can be controlled by the caller, if this amount is less than the matchedOrder.amount.

    • If the return value is controllable, how is it used and how can it go wrong? This function does not return a value.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here. The matchData.baseDelta will be saved for the owner of the matched order in the TransientMakerData memory.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._matchIncomingBid(ds, newOrder, args.amountIsBase) -> this._matchIncomingOrder(ds, bestAskOrder, incomingOrder, incomingOrder.amount, amountIsBase) -> BookLib.removeOrderFromBook(ds, matchedOrder)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? This function does not return a value.

    • What happens if it reverts, reenters or does other unusual control flow? The metadata.quoteTokenOpenInterest and metadata.baseTokenOpenInterest will be decremented — depends on the side of the order, and order.id will be deleted from the orders list. Also, bidTree and askTree will be updated in addition to orders.nextOrderId and orders.prevOrderId.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._settleIncomingOrder(ds, account, Side.BUY, args.settlement, result.totalQuoteTokenSent, result.totalBaseTokenReceived) -> TransientMakerData.getMakerCreditsAndClearStorage()

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the current state of the maker's credits, including the expired orders and matched orders.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • this._processFillBidOrder(ds, account, newOrder, args) -> this._settleIncomingOrder(ds, account, Side.BUY, args.settlement, result.totalQuoteTokenSent, result.totalBaseTokenReceived) -> factory.settleIncomingOrder(settleParams)

    • What is controllable? settleParams.taker, settleParams.side, and settleParams.settlement are controlled by the caller.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the takerFee amount, which is used for events and as a part of the returned PostFillOrderResult.

    • What happens if it reverts, reenters or does other unusual control flow? If settlement is set to INSTANT, the function might revert during safeTransferFrom if the taker does not have enough balance or has not approved a sufficient allowance. Also, it reverts if the balance of the factory contract is not enough to transfer tokens to the taker. If settlement is set to ACCOUNT, the function might revert during the debitAccount function call if the taker does not have enough internal token balance.

  • this._processFillAskOrder(ds, account, newOrder, args) -> this._matchIncomingAsk(ds, newOrder, args.amountIsBase) -> BookLib.getBestBid(ds)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the best maximum price bidTree.minimum().

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • this._processFillAskOrder(ds, account, newOrder, args) -> this._matchIncomingAsk(ds, newOrder, args.amountIsBase) -> OrderLib.isExpired(bestBidOrder)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? Returns true when the cancelTimestamp is not null and is less than the current block.timestamp; otherwise, it returns false.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • this._processFillAskOrder(ds, account, newOrder, args) -> this._matchIncomingAsk(ds, newOrder, args.amountIsBase) -> this._removeExpiredBid(ds, bestBidOrder) -> BookLib.getQuoteTokenAmount(ds, order.price, order.amount)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? Returns the quote-tokens amount calculated using the expired order.price and order.amount. The result can be rounded down to zero if order.amount * order.price is less than config.baseSize.

    • What happens if it reverts, reenters or does other unusual control flow? Can revert as a result of overflow during order.amount * order.price calculation if order.price or order.amount is too large.

  • this._processFillAskOrder(ds, account, newOrder, args) -> this._matchIncomingAsk(ds, newOrder, args.amountIsBase) -> this._removeExpiredBid(ds, bestBidOrder) -> TransientMakerData.addQuoteToken(order.owner, quoteTokenAmount)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? This function does not return a value.

    • What happens if it reverts, reenters or does other unusual control flow? There are no problems here.

  • this._processFillAskOrder(ds, account, newOrder, args) -> this._matchIncomingAsk(ds, newOrder, args.amountIsBase) -> this._removeExpiredBid(ds, bestBidOrder) -> BookLib.removeOrderFromBook(ds, order)

    • What is controllable? N/A.

    • If the return value is controllable, how is it used and how can it go wrong? This function does not return a value.

    • What happens if it reverts, reenters or does other unusual control flow? The metadata.quoteTokenOpenInterest and metadata.baseTokenOpenInterest will be decremented — depends on the side of the order, and order.id will be deleted from the orders list. Also, bidTree and askTree will be updated in addition to orders.nextOrderId and orders.prevOrderId.

Zellic © 2025Back to top ↑