aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils/order_state_utils.ts
blob: 36a4b68d636dbaed01896ae94f11f1799904e062 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import * as _ from 'lodash';
import BigNumber from 'bignumber.js';
import {
    ExchangeContractErrs,
    SignedOrder,
    OrderRelevantState,
    MethodOpts,
    OrderState,
    OrderStateValid,
    OrderStateInvalid,
} from '../types';
import {ZeroEx} from '../0x';
import {TokenWrapper} from '../contract_wrappers/token_wrapper';
import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper';
import {utils} from '../utils/utils';
import {constants} from '../utils/constants';

export class OrderStateUtils {
    private tokenWrapper: TokenWrapper;
    private exchangeWrapper: ExchangeWrapper;
    constructor(tokenWrapper: TokenWrapper, exchangeWrapper: ExchangeWrapper) {
        this.tokenWrapper = tokenWrapper;
        this.exchangeWrapper = exchangeWrapper;
    }
    public async getOrderStateAsync(signedOrder: SignedOrder, methodOpts?: MethodOpts): Promise<OrderState> {
        const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder, methodOpts);
        const orderHash = ZeroEx.getOrderHashHex(signedOrder);
        try {
            this.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, methodOpts?: MethodOpts): Promise<OrderRelevantState> {
        const zrxTokenAddress = await this.exchangeWrapper.getZRXTokenAddressAsync();
        const orderHash = ZeroEx.getOrderHashHex(signedOrder);
        const makerBalance = await this.tokenWrapper.getBalanceAsync(
            signedOrder.makerTokenAddress, signedOrder.maker, methodOpts,
        );
        const makerProxyAllowance = await this.tokenWrapper.getProxyAllowanceAsync(
            signedOrder.makerTokenAddress, signedOrder.maker, methodOpts,
        );
        const makerFeeBalance = await this.tokenWrapper.getBalanceAsync(
            zrxTokenAddress, signedOrder.maker, methodOpts,
        );
        const makerFeeProxyAllowance = await this.tokenWrapper.getProxyAllowanceAsync(
            zrxTokenAddress, signedOrder.maker, methodOpts,
        );
        const filledTakerTokenAmount = await this.exchangeWrapper.getFilledTakerAmountAsync(orderHash, methodOpts);
        const canceledTakerTokenAmount = await this.exchangeWrapper.getCanceledTakerAmountAsync(orderHash, methodOpts);
        const unavailableTakerTokenAmount =
          await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash, methodOpts);
        const totalMakerTokenAmount = signedOrder.makerTokenAmount;
        const totalTakerTokenAmount = signedOrder.takerTokenAmount;
        const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount);
        const remainingMakerTokenAmount = remainingTakerTokenAmount.times(totalMakerTokenAmount)
                                                                   .dividedToIntegerBy(totalTakerTokenAmount);
        const fillableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
        const remainingFillableMakerTokenAmount = BigNumber.min(fillableMakerTokenAmount, remainingMakerTokenAmount);
        // TODO: Handle edge case where maker token is ZRX with fee
        const orderRelevantState = {
            makerBalance,
            makerProxyAllowance,
            makerFeeBalance,
            makerFeeProxyAllowance,
            filledTakerTokenAmount,
            canceledTakerTokenAmount,
            remainingFillableMakerTokenAmount,
        };
        return orderRelevantState;
    }
    private validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
        const unavailableTakerTokenAmount = orderRelevantState.canceledTakerTokenAmount.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);
            }
        }
        // TODO Add linear function solver when maker token is ZRX #badass
        // Return the max amount that's fillable
    }
}