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
|
import { SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
export class RemainingFillableCalculator {
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.makerAssetAmount);
}
public computeRemainingMakerFillable(): BigNumber {
if (this._hasSufficientFundsForFeeAndTransferAmount()) {
return this._remainingMakerTokenAmount;
}
if (this._signedOrder.makerFee.isZero()) {
return BigNumber.min(this._remainingMakerTokenAmount, this._transferrableMakerTokenAmount);
}
return this._calculatePartiallyFillableMakerTokenAmount();
}
public computeRemainingTakerFillable(): BigNumber {
return this.computeRemainingMakerFillable()
.times(this._signedOrder.takerAssetAmount)
.dividedToIntegerBy(this._signedOrder.makerAssetAmount);
}
private _hasSufficientFundsForFeeAndTransferAmount(): boolean {
if (this._isMakerTokenZRX) {
const totalZRXTransferAmountRequired = this._remainingMakerTokenAmount.plus(this._remainingMakerFeeAmount);
const hasSufficientFunds = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
totalZRXTransferAmountRequired,
);
return hasSufficientFunds;
} else {
const hasSufficientFundsForTransferAmount = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
this._remainingMakerTokenAmount,
);
const hasSufficientFundsForFeeAmount = this._transferrableMakerFeeTokenAmount.greaterThanOrEqualTo(
this._remainingMakerFeeAmount,
);
const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount;
return hasSufficientFunds;
}
}
private _calculatePartiallyFillableMakerTokenAmount(): BigNumber {
// 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.makerAssetAmount.dividedBy(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 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,
);
// The number of times the Maker can fill the order, given the Maker Token Balance
// Assuming a balance of 150 wei, and an orderToFeeRatio of 100:1, 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.dividedBy(orderToFeeRatio.plus(new BigNumber(1)));
}
// 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(this._signedOrder.makerAssetAmount)
.dividedToIntegerBy(this._signedOrder.makerFee);
const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits
.times(this._signedOrder.makerAssetAmount)
.dividedToIntegerBy(this._signedOrder.makerFee);
const partiallyFillableAmount = BigNumber.min(
partiallyFillableMakerTokenAmount,
partiallyFillableFeeTokenAmount,
);
return partiallyFillableAmount;
}
}
|