aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/contract_wrappers/exchange_wrapper.ts20
-rw-r--r--src/types.ts10
-rw-r--r--src/utils/order_validation_utils.ts42
3 files changed, 65 insertions, 7 deletions
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts
index 694f3b71b..d2a3bcfa3 100644
--- a/src/contract_wrappers/exchange_wrapper.ts
+++ b/src/contract_wrappers/exchange_wrapper.ts
@@ -28,6 +28,7 @@ import {
LogCancelContractEventArgs,
LogWithDecodedArgs,
MethodOpts,
+ ValidateOrderFillableOpts,
} from '../types';
import {assert} from '../utils/assert';
import {utils} from '../utils/utils';
@@ -624,6 +625,25 @@ export class ExchangeWrapper extends ContractWrapper {
return exchangeAddress;
}
/**
+ * Checks if order is still fillable and throws an error otherwise. Useful for orderbook
+ * pruning where you want to remove stale orders without knowing who the taker will be.
+ * @param signedOrder An object that conforms to the SignedOrder interface. The
+ * signedOrder you wish to validate.
+ * @param opts An object that conforms to the ValidateOrderFillableOpts
+ * interface. Allows specifying a specific fillTakerTokenAmount
+ * to validate for.
+ */
+ public async validateOrderFillableOrThrowAsync(
+ signedOrder: SignedOrder, opts?: ValidateOrderFillableOpts,
+ ): Promise<void> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ const zrxTokenAddress = await this._getZRXTokenAddressAsync();
+ const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined;
+ await this._orderValidationUtils.validateOrderFillableOrThrowAsync(
+ signedOrder, zrxTokenAddress, expectedFillTakerTokenAmount,
+ );
+ }
+ /**
* Checks if order fill will succeed and throws an error otherwise.
* @param signedOrder An object that conforms to the SignedOrder interface. The
* signedOrder you wish to fill.
diff --git a/src/types.ts b/src/types.ts
index 29fb40e73..2d069f596 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -434,6 +434,16 @@ export interface Artifact {
}
/*
+ * expectedFillTakerTokenAmount: If specified, the validation method will ensure that the
+ * supplied order maker has a sufficient allowance/balance to fill this amount of the order's
+ * takerTokenAmount. If not specified, the validation method ensures that the maker has a sufficient
+ * allowance/balance to fill the entire remaining order amount.
+ */
+export interface ValidateOrderFillableOpts {
+ expectedFillTakerTokenAmount?: BigNumber.BigNumber;
+}
+
+/*
* defaultBlock: The block up to which to query the blockchain state. Setting this to a historical block number
* let's the user query the blockchain's state at an arbitrary point in time. In order for this to work, the
* backing Ethereum node must keep the entire historical state of the chain (e.g setting `--pruning=archive`
diff --git a/src/utils/order_validation_utils.ts b/src/utils/order_validation_utils.ts
index 815cd0115..6f7522c41 100644
--- a/src/utils/order_validation_utils.ts
+++ b/src/utils/order_validation_utils.ts
@@ -1,3 +1,4 @@
+import * as _ from 'lodash';
import {ExchangeContractErrs, SignedOrder, Order, ZeroExError} from '../types';
import {ZeroEx} from '../0x';
import {TokenWrapper} from '../contract_wrappers/token_wrapper';
@@ -12,6 +13,23 @@ export class OrderValidationUtils {
this.tokenWrapper = tokenWrapper;
this.exchangeWrapper = exchangeWrapper;
}
+ public async validateOrderFillableOrThrowAsync(
+ signedOrder: SignedOrder, zrxTokenAddress: string, expectedFillTakerTokenAmount?: BigNumber.BigNumber,
+ ): Promise<void> {
+ const orderHash = utils.getOrderHashHex(signedOrder);
+ const unavailableTakerTokenAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
+ this.validateRemainingFillAmountNotZeroOrThrow(
+ signedOrder.takerTokenAmount, unavailableTakerTokenAmount,
+ );
+ this.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
+ let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
+ if (!_.isUndefined(expectedFillTakerTokenAmount)) {
+ fillTakerTokenAmount = expectedFillTakerTokenAmount;
+ }
+ await this.validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync(
+ signedOrder, fillTakerTokenAmount, zrxTokenAddress,
+ );
+ }
public async validateFillOrderThrowIfInvalidAsync(signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber.BigNumber,
takerAddress: string,
@@ -24,16 +42,13 @@ export class OrderValidationUtils {
throw new Error(ZeroExError.InvalidSignature);
}
const unavailableTakerTokenAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
- if (signedOrder.makerTokenAmount.eq(unavailableTakerTokenAmount)) {
- throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
- }
+ this.validateRemainingFillAmountNotZeroOrThrow(
+ signedOrder.takerTokenAmount, unavailableTakerTokenAmount,
+ );
if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) {
throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
}
- const currentUnixTimestampSec = utils.getCurrentUnixTimestamp();
- if (signedOrder.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
- throw new Error(ExchangeContractErrs.OrderFillExpired);
- }
+ this.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
await this.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress,
);
@@ -147,4 +162,17 @@ export class OrderValidationUtils {
}
}
}
+ private validateRemainingFillAmountNotZeroOrThrow(
+ takerTokenAmount: BigNumber.BigNumber, unavailableTakerTokenAmount: BigNumber.BigNumber,
+ ) {
+ if (takerTokenAmount.eq(unavailableTakerTokenAmount)) {
+ throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ }
+ }
+ private validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber.BigNumber) {
+ const currentUnixTimestampSec = utils.getCurrentUnixTimestamp();
+ if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
+ throw new Error(ExchangeContractErrs.OrderFillExpired);
+ }
+ }
}