diff options
Diffstat (limited to 'packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts')
-rw-r--r-- | packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts | 98 |
1 files changed, 55 insertions, 43 deletions
diff --git a/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts b/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts index 8141dd73c..b98ef3240 100644 --- a/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts +++ b/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts @@ -1,71 +1,83 @@ -import { - SignedOrder, -} from '../types'; -import { BigNumber } from 'bignumber.js'; +import {SignedOrder} from '../types'; +import {BigNumber} from 'bignumber.js'; + export class RemainingFillableCalculator { - private _signedOrder: SignedOrder; - private _isMakerTokenZRX: boolean; - private _transferrableMakerTokenAmount: BigNumber; - private _transferrableMakerFeeTokenAmount: BigNumber; - private _remainingMakerTokenAmount: BigNumber; - private _remainingMakerFeeAmount: BigNumber; + private signedOrder: SignedOrder; + private isMakerTokenZRX: boolean; + // Transferrable Amount is the minimum of Approval and Balance + private transferrableMakerTokenAmount: BigNumber; + private transferrableMakerFeeTokenAmount: BigNumber; + private remainingMakerTokenAmount: BigNumber; + private remainingMakerFeeAmount: BigNumber; constructor(signedOrder: SignedOrder, isMakerTokenZRX: boolean, transferrableMakerTokenAmount: BigNumber, transferrableMakerFeeTokenAmount: BigNumber, remainingMakerTokenAmount: BigNumber) { - this._signedOrder = signedOrder; - this._isMakerTokenZRX = isMakerTokenZRX; - this._transferrableMakerTokenAmount = transferrableMakerTokenAmount; - this._transferrableMakerFeeTokenAmount = transferrableMakerFeeTokenAmount; - this._remainingMakerTokenAmount = remainingMakerTokenAmount; - this._remainingMakerFeeAmount = remainingMakerTokenAmount.times(signedOrder.makerFee) - .dividedToIntegerBy(signedOrder.makerTokenAmount); + this.signedOrder = signedOrder; + this.isMakerTokenZRX = isMakerTokenZRX; + this.transferrableMakerTokenAmount = transferrableMakerTokenAmount; + this.transferrableMakerFeeTokenAmount = transferrableMakerFeeTokenAmount; + this.remainingMakerTokenAmount = remainingMakerTokenAmount; + this.remainingMakerFeeAmount = remainingMakerTokenAmount.times(signedOrder.makerFee) + .dividedToIntegerBy(signedOrder.makerTokenAmount); } public computeRemainingMakerFillable(): BigNumber { if (this.hasSufficientFundsForFeeAndTransferAmount()) { - return this._remainingMakerTokenAmount; + return this.remainingMakerTokenAmount; } - if (this._signedOrder.makerFee.isZero()) { - return BigNumber.min(this._remainingMakerTokenAmount, this._transferrableMakerTokenAmount); - } else { - return this.calculatePartiallyFillableMakerTokenAmount(); + if (this.signedOrder.makerFee.isZero()) { + return BigNumber.min(this.remainingMakerTokenAmount, this.transferrableMakerTokenAmount); } + return this.calculatePartiallyFillableMakerTokenAmount(); } public computeRemainingTakerFillable(): BigNumber { - return this.computeRemainingMakerFillable().times(this._signedOrder.takerTokenAmount) - .dividedToIntegerBy(this._signedOrder.makerTokenAmount); + return this.computeRemainingMakerFillable().times(this.signedOrder.takerTokenAmount) + .dividedToIntegerBy(this.signedOrder.makerTokenAmount); } private hasSufficientFundsForFeeAndTransferAmount(): boolean { - if (this._isMakerTokenZRX) { - const totalZRXTransferAmountRequired = this._remainingMakerTokenAmount.plus(this._remainingMakerFeeAmount); - return this._transferrableMakerTokenAmount.gte(totalZRXTransferAmountRequired); + if (this.isMakerTokenZRX) { + const totalZRXTransferAmountRequired = this.remainingMakerTokenAmount.plus(this.remainingMakerFeeAmount); + const hasSufficientFunds = this.transferrableMakerTokenAmount.greaterThanOrEqualTo( + totalZRXTransferAmountRequired); + return hasSufficientFunds; } else { - const hasSufficientFundsForTransferAmount = this._transferrableMakerTokenAmount.gte( - this._remainingMakerTokenAmount); - const hasSufficientFundsForFeeAmount = this._transferrableMakerFeeTokenAmount.gte( - this._remainingMakerFeeAmount); - return (hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount); + const hasSufficientFundsForTransferAmount = this.transferrableMakerTokenAmount.greaterThanOrEqualTo( + this.remainingMakerTokenAmount); + const hasSufficientFundsForFeeAmount = this.transferrableMakerFeeTokenAmount.greaterThanOrEqualTo( + this.remainingMakerFeeAmount); + const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount; + return hasSufficientFunds; } } - private calculatePartiallyFillableMakerTokenAmount(): BigNumber { - const orderToFeeRatio = this._signedOrder.makerTokenAmount.dividedToIntegerBy(this._signedOrder.makerFee); + // The number of times the maker can fill the order, if each fill only required the transfer of a single + // baseUnit of fee tokens. + // Given an order for 200 wei for 2 ZRXwei fee, find 100 wei for 1 ZRXwei. Order ratio is then 100:1 + const orderToFeeRatio = this.signedOrder.makerTokenAmount.dividedBy(this.signedOrder.makerFee); // Maximum number of times the Maker can fill the order, given the fees - const fillableTimesInFeeTokenUnits = BigNumber.min(this._transferrableMakerFeeTokenAmount, - this._remainingMakerFeeAmount); + // Given 2 ZRXwei, the maximum amount of times Maker can fill this order, in terms of fees, is 2 + const fillableTimesInFeeTokenBaseUnits = BigNumber.min(this.transferrableMakerFeeTokenAmount, + this.remainingMakerFeeAmount); // Maximum number of times the Maker can fill the order, given the Maker Token Balance - let fillableTimesInMakerTokenUnits = this._transferrableMakerTokenAmount.dividedToIntegerBy(orderToFeeRatio); - if (this._isMakerTokenZRX) { - const totalZRXTokenPooled = this._transferrableMakerTokenAmount; + // Assuming a balance of 150 wei, maker can fill this order 1 time. + let fillableTimesInMakerTokenUnits = this.transferrableMakerTokenAmount.dividedBy(orderToFeeRatio); + if (this.isMakerTokenZRX) { + // If ZRX is the maker token, the Fee and the Maker amount need to be removed from the same pool; + // 200 ZRXwei for 2ZRXwei fee can only be filled once (need 202 ZRXwei) + const totalZRXTokenPooled = this.transferrableMakerTokenAmount; // The purchasing power here is less as the tokens are taken from the same Pool // For every one number of fills, we have to take an extra ZRX out of the pool - fillableTimesInMakerTokenUnits = totalZRXTokenPooled.dividedToIntegerBy( + fillableTimesInMakerTokenUnits = totalZRXTokenPooled.dividedBy( orderToFeeRatio.plus(new BigNumber(1))); } - const partiallyFillableMakerTokenAmount = fillableTimesInMakerTokenUnits.times(orderToFeeRatio); - const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenUnits.times(orderToFeeRatio); - return BigNumber.min(partiallyFillableMakerTokenAmount, partiallyFillableFeeTokenAmount); + // When Ratio is not fully divisible there can be remainders which cannot be represented, so they are floored. + // This can result in a RoundingError being thrown by the Exchange Contract. + const partiallyFillableMakerTokenAmount = fillableTimesInMakerTokenUnits.times(orderToFeeRatio).floor(); + const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits.times(orderToFeeRatio).floor(); + const partiallyFillableAmount = BigNumber.min(partiallyFillableMakerTokenAmount, + partiallyFillableFeeTokenAmount); + return partiallyFillableAmount; } } |