Merge pull request #226 from dekz/feature/calculate-remaining-proportions
Calculate remaining proportions from fee to ratio proportions
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import {SignedOrder} from '../types';
|
||||
import {BigNumber} from 'bignumber.js';
|
||||
|
||||
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.makerTokenAmount);
|
||||
}
|
||||
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.takerTokenAmount)
|
||||
.dividedToIntegerBy(this.signedOrder.makerTokenAmount);
|
||||
}
|
||||
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.makerTokenAmount.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.makerTokenAmount)
|
||||
.dividedToIntegerBy(this.signedOrder.makerFee);
|
||||
const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits
|
||||
.times(this.signedOrder.makerTokenAmount)
|
||||
.dividedToIntegerBy(this.signedOrder.makerFee);
|
||||
const partiallyFillableAmount = BigNumber.min(partiallyFillableMakerTokenAmount,
|
||||
partiallyFillableFeeTokenAmount);
|
||||
return partiallyFillableAmount;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import {utils} from '../utils/utils';
|
||||
import {constants} from '../utils/constants';
|
||||
import {OrderFilledCancelledLazyStore} from '../stores/order_filled_cancelled_lazy_store';
|
||||
import {BalanceAndProxyAllowanceLazyStore} from '../stores/balance_proxy_allowance_lazy_store';
|
||||
import {RemainingFillableCalculator} from '../order_watcher/remaining_fillable_calculator';
|
||||
|
||||
const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001;
|
||||
|
||||
@@ -78,12 +79,17 @@ export class OrderStateUtils {
|
||||
const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount);
|
||||
const remainingMakerTokenAmount = remainingTakerTokenAmount.times(totalMakerTokenAmount)
|
||||
.dividedToIntegerBy(totalTakerTokenAmount);
|
||||
const fillableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
|
||||
const remainingFillableMakerTokenAmount = BigNumber.min(fillableMakerTokenAmount, remainingMakerTokenAmount);
|
||||
const remainingFillableTakerTokenAmount = remainingFillableMakerTokenAmount
|
||||
.times(totalTakerTokenAmount)
|
||||
.dividedToIntegerBy(totalMakerTokenAmount);
|
||||
// TODO: Handle edge case where maker token is ZRX with fee
|
||||
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,
|
||||
|
||||
@@ -25,6 +25,7 @@ import { FillScenarios } from './utils/fill_scenarios';
|
||||
import {DoneCallback} from '../src/types';
|
||||
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
|
||||
import {reportCallbackErrors} from './utils/report_callback_errors';
|
||||
import {constants as constants} from './utils/constants';
|
||||
|
||||
const TIMEOUT_MS = 150;
|
||||
|
||||
@@ -47,7 +48,8 @@ describe('OrderStateWatcher', () => {
|
||||
let taker: string;
|
||||
let web3Wrapper: Web3Wrapper;
|
||||
let signedOrder: SignedOrder;
|
||||
const fillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(5), 18);
|
||||
const decimals = constants.ZRX_DECIMALS;
|
||||
const fillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
|
||||
before(async () => {
|
||||
web3 = web3Factory.create();
|
||||
zeroEx = new ZeroEx(web3.currentProvider);
|
||||
@@ -254,15 +256,15 @@ describe('OrderStateWatcher', () => {
|
||||
describe('remainingFillable(M|T)akerTokenAmount', () => {
|
||||
it('should calculate correct remaining fillable', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const takerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(10), 18);
|
||||
const makerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(20), 18);
|
||||
const takerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(10), decimals);
|
||||
const makerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(20), decimals);
|
||||
signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
|
||||
makerToken.address, takerToken.address, maker, taker, makerFillableAmount,
|
||||
takerFillableAmount,
|
||||
);
|
||||
const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
|
||||
const takerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, taker);
|
||||
const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18);
|
||||
const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
|
||||
const orderHash = ZeroEx.getOrderHashHex(signedOrder);
|
||||
await zeroEx.orderStateWatcher.addOrderAsync(signedOrder);
|
||||
let eventCount = 0;
|
||||
@@ -273,9 +275,9 @@ describe('OrderStateWatcher', () => {
|
||||
expect(validOrderState.orderHash).to.be.equal(orderHash);
|
||||
const orderRelevantState = validOrderState.orderRelevantState;
|
||||
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(16), 18));
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(16), decimals));
|
||||
expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(8), 18));
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals));
|
||||
if (eventCount === 2) {
|
||||
done();
|
||||
}
|
||||
@@ -295,7 +297,7 @@ describe('OrderStateWatcher', () => {
|
||||
|
||||
const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
|
||||
|
||||
const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), 18);
|
||||
const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
|
||||
await zeroEx.orderStateWatcher.addOrderAsync(signedOrder);
|
||||
|
||||
const callback = reportCallbackErrors(done)((orderState: OrderState) => {
|
||||
@@ -319,11 +321,12 @@ describe('OrderStateWatcher', () => {
|
||||
|
||||
const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
|
||||
|
||||
const remainingAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
|
||||
const remainingAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
|
||||
const transferAmount = makerBalance.sub(remainingAmount);
|
||||
await zeroEx.orderStateWatcher.addOrderAsync(signedOrder);
|
||||
|
||||
const callback = reportCallbackErrors(done)((orderState: OrderState) => {
|
||||
expect(orderState.isValid).to.be.true();
|
||||
const validOrderState = orderState as OrderStateValid;
|
||||
const orderRelevantState = validOrderState.orderRelevantState;
|
||||
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
|
||||
@@ -337,6 +340,88 @@ describe('OrderStateWatcher', () => {
|
||||
makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount);
|
||||
})().catch(done);
|
||||
});
|
||||
it('should equal remaining amount when partially cancelled and order has fees', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
|
||||
const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
|
||||
const feeRecipient = taker;
|
||||
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerToken.address, takerToken.address, makerFee, takerFee, maker,
|
||||
taker, fillableAmount, feeRecipient);
|
||||
|
||||
const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
|
||||
|
||||
const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
|
||||
const transferTokenAmount = makerFee.sub(remainingTokenAmount);
|
||||
await zeroEx.orderStateWatcher.addOrderAsync(signedOrder);
|
||||
|
||||
const callback = reportCallbackErrors(done)((orderState: OrderState) => {
|
||||
expect(orderState.isValid).to.be.true();
|
||||
const validOrderState = orderState as OrderStateValid;
|
||||
const orderRelevantState = validOrderState.orderRelevantState;
|
||||
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
|
||||
remainingTokenAmount);
|
||||
done();
|
||||
});
|
||||
zeroEx.orderStateWatcher.subscribe(callback);
|
||||
await zeroEx.exchange.cancelOrderAsync(signedOrder, transferTokenAmount);
|
||||
})().catch(done);
|
||||
});
|
||||
it('should equal ratio amount when fee balance is lowered', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
|
||||
const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
|
||||
const feeRecipient = taker;
|
||||
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerToken.address, takerToken.address, makerFee, takerFee, maker,
|
||||
taker, fillableAmount, feeRecipient);
|
||||
|
||||
const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
|
||||
|
||||
const remainingFeeAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
|
||||
const transferFeeAmount = makerFee.sub(remainingFeeAmount);
|
||||
|
||||
const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
|
||||
const transferTokenAmount = makerFee.sub(remainingTokenAmount);
|
||||
await zeroEx.orderStateWatcher.addOrderAsync(signedOrder);
|
||||
|
||||
const callback = reportCallbackErrors(done)((orderState: OrderState) => {
|
||||
const validOrderState = orderState as OrderStateValid;
|
||||
const orderRelevantState = validOrderState.orderRelevantState;
|
||||
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
|
||||
remainingFeeAmount);
|
||||
done();
|
||||
});
|
||||
zeroEx.orderStateWatcher.subscribe(callback);
|
||||
await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount);
|
||||
await zeroEx.token.transferAsync(
|
||||
makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferTokenAmount);
|
||||
})().catch(done);
|
||||
});
|
||||
it('should calculate full amount when all available and non-divisible', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
|
||||
const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
|
||||
const feeRecipient = taker;
|
||||
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerToken.address, takerToken.address, makerFee, takerFee, maker,
|
||||
taker, fillableAmount, feeRecipient);
|
||||
|
||||
const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
|
||||
await zeroEx.orderStateWatcher.addOrderAsync(signedOrder);
|
||||
|
||||
const callback = reportCallbackErrors(done)((orderState: OrderState) => {
|
||||
const validOrderState = orderState as OrderStateValid;
|
||||
const orderRelevantState = validOrderState.orderRelevantState;
|
||||
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
|
||||
fillableAmount);
|
||||
done();
|
||||
});
|
||||
zeroEx.orderStateWatcher.subscribe(callback);
|
||||
await zeroEx.token.setProxyAllowanceAsync(
|
||||
makerToken.address, maker, ZeroEx.toBaseUnitAmount(new BigNumber(100), decimals));
|
||||
})().catch(done);
|
||||
});
|
||||
});
|
||||
it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
|
||||
176
packages/0x.js/test/remaining_fillable_calculator_test.ts
Normal file
176
packages/0x.js/test/remaining_fillable_calculator_test.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
import 'mocha';
|
||||
import * as chai from 'chai';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { RemainingFillableCalculator } from '../src/order_watcher/remaining_fillable_calculator';
|
||||
import { SignedOrder, ECSignature } from '../src/types';
|
||||
import { TokenUtils } from './utils/token_utils';
|
||||
import { ZeroEx } from '../src/0x';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('RemainingFillableCalculator', () => {
|
||||
let calculator: RemainingFillableCalculator;
|
||||
let signedOrder: SignedOrder;
|
||||
let transferrableMakerTokenAmount: BigNumber;
|
||||
let transferrableMakerFeeTokenAmount: BigNumber;
|
||||
let remainingMakerTokenAmount: BigNumber;
|
||||
let makerAmount: BigNumber;
|
||||
let takerAmount: BigNumber;
|
||||
let makerFeeAmount: BigNumber;
|
||||
let isMakerTokenZRX: boolean;
|
||||
const makerToken: string = '0x1';
|
||||
const takerToken: string = '0x2';
|
||||
const decimals: number = 4;
|
||||
const zero: BigNumber = new BigNumber(0);
|
||||
const zeroAddress = '0x0';
|
||||
const signature: ECSignature = { v: 27, r: '', s: ''};
|
||||
beforeEach(async () => {
|
||||
[makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals)];
|
||||
[transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount] = [
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals)];
|
||||
});
|
||||
function buildSignedOrder(): SignedOrder {
|
||||
return { ecSignature: signature,
|
||||
exchangeContractAddress: zeroAddress,
|
||||
feeRecipient: zeroAddress,
|
||||
maker: zeroAddress,
|
||||
taker: zeroAddress,
|
||||
makerFee: makerFeeAmount,
|
||||
takerFee: zero,
|
||||
makerTokenAmount: makerAmount,
|
||||
takerTokenAmount: takerAmount,
|
||||
makerTokenAddress: makerToken,
|
||||
takerTokenAddress: takerToken,
|
||||
salt: zero,
|
||||
expirationUnixTimestampSec: zero };
|
||||
}
|
||||
describe('Maker token is NOT ZRX', () => {
|
||||
before(async () => {
|
||||
isMakerTokenZRX = false;
|
||||
});
|
||||
it('calculates the correct amount when unfilled and funds available', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
remainingMakerTokenAmount = signedOrder.makerTokenAmount;
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount);
|
||||
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
|
||||
});
|
||||
it('calculates the correct amount when partially filled and funds available', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount);
|
||||
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
|
||||
});
|
||||
it('calculates the amount to be 0 when all fee funds are transferred', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
transferrableMakerFeeTokenAmount = zero;
|
||||
remainingMakerTokenAmount = signedOrder.makerTokenAmount;
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount);
|
||||
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
|
||||
});
|
||||
it('calculates the correct amount when balance is less than remaining fillable', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
|
||||
remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
|
||||
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount);
|
||||
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
|
||||
});
|
||||
describe('Order to Fee Ratio is < 1', () => {
|
||||
beforeEach(async () => {
|
||||
[makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals)];
|
||||
});
|
||||
it('calculates the correct amount when funds unavailable', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
remainingMakerTokenAmount = signedOrder.makerTokenAmount;
|
||||
const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
|
||||
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount,
|
||||
remainingMakerTokenAmount);
|
||||
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
|
||||
});
|
||||
});
|
||||
describe('Ratio is not evenly divisble', () => {
|
||||
beforeEach(async () => {
|
||||
[makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
|
||||
ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals)];
|
||||
});
|
||||
it('calculates the correct amount when funds unavailable', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
remainingMakerTokenAmount = signedOrder.makerTokenAmount;
|
||||
const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
|
||||
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount,
|
||||
remainingMakerTokenAmount);
|
||||
const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
|
||||
expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakerTokenAmount)).to.be.true();
|
||||
expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0));
|
||||
const orderToFeeRatio = signedOrder.makerTokenAmount.dividedBy(signedOrder.makerFee);
|
||||
const calculatedFeeAmount = calculatedFillableAmount.dividedBy(orderToFeeRatio);
|
||||
expect(calculatedFeeAmount).to.be.bignumber.lessThan(transferrableMakerFeeTokenAmount);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Maker Token is ZRX', () => {
|
||||
before(async () => {
|
||||
isMakerTokenZRX = true;
|
||||
});
|
||||
it('calculates the correct amount when unfilled and funds available', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
transferrableMakerTokenAmount = makerAmount.plus(makerFeeAmount);
|
||||
transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
|
||||
remainingMakerTokenAmount = signedOrder.makerTokenAmount;
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount);
|
||||
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
|
||||
});
|
||||
it('calculates the correct amount when partially filled and funds available', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount);
|
||||
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
|
||||
});
|
||||
it('calculates the amount to be 0 when all fee funds are transferred', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
transferrableMakerTokenAmount = zero;
|
||||
transferrableMakerFeeTokenAmount = zero;
|
||||
remainingMakerTokenAmount = signedOrder.makerTokenAmount;
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount);
|
||||
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
|
||||
});
|
||||
it('calculates the correct amount when balance is less than remaining fillable', () => {
|
||||
signedOrder = buildSignedOrder();
|
||||
const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
|
||||
remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
|
||||
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
|
||||
transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
|
||||
|
||||
const orderToFeeRatio = signedOrder.makerTokenAmount.dividedToIntegerBy(signedOrder.makerFee);
|
||||
const expectedFillableAmount = new BigNumber(450980);
|
||||
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX,
|
||||
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount);
|
||||
const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
|
||||
const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio);
|
||||
const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio);
|
||||
expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(transferrableMakerTokenAmount);
|
||||
expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(remainingMakerTokenAmount);
|
||||
expect(calculatedFillableAmount).to.be.bignumber.equal(expectedFillableAmount);
|
||||
expect(numberOfFillsInRatio.decimalPlaces()).to.be.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,4 +5,5 @@ export const constants = {
|
||||
TESTRPC_NETWORK_ID: 50,
|
||||
KOVAN_RPC_URL: 'https://kovan.infura.io',
|
||||
ROPSTEN_RPC_URL: 'https://ropsten.infura.io',
|
||||
ZRX_DECIMALS: 18,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user