aboutsummaryrefslogtreecommitdiffstats
path: root/packages/order-utils/src/order_state_utils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/order-utils/src/order_state_utils.ts')
-rw-r--r--packages/order-utils/src/order_state_utils.ts140
1 files changed, 140 insertions, 0 deletions
diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts
new file mode 100644
index 000000000..36171f526
--- /dev/null
+++ b/packages/order-utils/src/order_state_utils.ts
@@ -0,0 +1,140 @@
+import {
+ ExchangeContractErrs,
+ OrderRelevantState,
+ OrderState,
+ OrderStateInvalid,
+ OrderStateValid,
+ SignedOrder,
+} from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import * as _ from 'lodash';
+
+import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
+import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
+import { getOrderHashHex } from './order_hash';
+import { RemainingFillableCalculator } from './remaining_fillable_calculator';
+
+const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001;
+
+export class OrderStateUtils {
+ private _balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher;
+ private _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher;
+ private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
+ const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add(
+ orderRelevantState.filledTakerTokenAmount,
+ );
+ const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
+ if (availableTakerTokenAmount.eq(0)) {
+ throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ }
+
+ if (orderRelevantState.makerBalance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerBalance);
+ }
+ if (orderRelevantState.makerProxyAllowance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerAllowance);
+ }
+ if (!signedOrder.makerFee.eq(0)) {
+ if (orderRelevantState.makerFeeBalance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance);
+ }
+ if (orderRelevantState.makerFeeProxyAllowance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
+ }
+ }
+ const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount
+ .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
+ .dividedBy(signedOrder.makerTokenAmount);
+ if (
+ orderRelevantState.remainingFillableTakerTokenAmount.lessThan(
+ minFillableTakerTokenAmountWithinNoRoundingErrorRange,
+ )
+ ) {
+ throw new Error(ExchangeContractErrs.OrderFillRoundingError);
+ }
+ }
+ constructor(
+ balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher,
+ orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher,
+ ) {
+ this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher;
+ this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
+ }
+ public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
+ const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder);
+ const orderHash = getOrderHashHex(signedOrder);
+ try {
+ OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState);
+ const orderState: OrderStateValid = {
+ isValid: true,
+ orderHash,
+ orderRelevantState,
+ };
+ return orderState;
+ } catch (err) {
+ const orderState: OrderStateInvalid = {
+ isValid: false,
+ orderHash,
+ error: err.message,
+ };
+ return orderState;
+ }
+ }
+ public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
+ const zrxTokenAddress = this._orderFilledCancelledFetcher.getZRXTokenAddress();
+ const orderHash = getOrderHashHex(signedOrder);
+ const makerBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ );
+ const makerProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ );
+ const makerFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ );
+ const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ );
+ const filledTakerTokenAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
+ const cancelledTakerTokenAmount = await this._orderFilledCancelledFetcher.getCancelledTakerAmountAsync(
+ orderHash,
+ );
+ const unavailableTakerTokenAmount = await this._orderFilledCancelledFetcher.getUnavailableTakerAmountAsync(
+ orderHash,
+ );
+ const totalMakerTokenAmount = signedOrder.makerTokenAmount;
+ const totalTakerTokenAmount = signedOrder.takerTokenAmount;
+ const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount);
+ const remainingMakerTokenAmount = remainingTakerTokenAmount
+ .times(totalMakerTokenAmount)
+ .dividedToIntegerBy(totalTakerTokenAmount);
+ const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
+ const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]);
+
+ const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress;
+ const remainingFillableCalculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable();
+ const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable();
+ const orderRelevantState = {
+ makerBalance,
+ makerProxyAllowance,
+ makerFeeBalance,
+ makerFeeProxyAllowance,
+ filledTakerTokenAmount,
+ cancelledTakerTokenAmount,
+ remainingFillableMakerTokenAmount,
+ remainingFillableTakerTokenAmount,
+ };
+ return orderRelevantState;
+ }
+}