Merge pull request #678 from 0xProject/fix/order-utils/remaining-v2-changes

Remaining Order-utils V2 Changes
This commit is contained in:
Fabio Berger
2018-06-07 21:12:21 +02:00
committed by GitHub
7 changed files with 206 additions and 206 deletions

View File

@@ -44,6 +44,7 @@ export class MultiSigWrapper {
txId: BigNumber, txId: BigNumber,
from: string, from: string,
): Promise<TransactionReceiptWithDecodedLogs> { ): Promise<TransactionReceiptWithDecodedLogs> {
// tslint:disable-next-line:no-unnecessary-type-assertion
const txHash = await (this const txHash = await (this
._multiSig as AssetProxyOwnerContract).executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from }); ._multiSig as AssetProxyOwnerContract).executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from });
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,6 @@ import { BigNumber } from '@0xproject/utils';
export abstract class AbstractOrderFilledCancelledFetcher { export abstract class AbstractOrderFilledCancelledFetcher {
public abstract async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>; public abstract async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>;
public abstract async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber>; public abstract async isOrderCancelledAsync(orderHash: string): Promise<boolean>;
public abstract async getUnavailableTakerAmountAsync(orderHash: string): Promise<BigNumber>;
public abstract getZRXTokenAddress(): string; public abstract getZRXTokenAddress(): string;
} }

View File

@@ -20,11 +20,8 @@ export class OrderStateUtils {
private _balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher; private _balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher;
private _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher; private _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher;
private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void { private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add( const availableTakerAssetAmount = signedOrder.takerAssetAmount.minus(orderRelevantState.filledTakerAssetAmount);
orderRelevantState.filledTakerTokenAmount, if (availableTakerAssetAmount.eq(0)) {
);
const availableTakerTokenAmount = signedOrder.takerAssetAmount.minus(unavailableTakerTokenAmount);
if (availableTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
} }
@@ -42,12 +39,12 @@ export class OrderStateUtils {
throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance); throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
} }
} }
const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerAssetAmount const minFillableTakerAssetAmountWithinNoRoundingErrorRange = signedOrder.takerAssetAmount
.dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
.dividedBy(signedOrder.makerAssetAmount); .dividedBy(signedOrder.makerAssetAmount);
if ( if (
orderRelevantState.remainingFillableTakerTokenAmount.lessThan( orderRelevantState.remainingFillableTakerAssetAmount.lessThan(
minFillableTakerTokenAmountWithinNoRoundingErrorRange, minFillableTakerAssetAmountWithinNoRoundingErrorRange,
) )
) { ) {
throw new Error(ExchangeContractErrs.OrderFillRoundingError); throw new Error(ExchangeContractErrs.OrderFillRoundingError);
@@ -82,13 +79,15 @@ export class OrderStateUtils {
} }
public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> { public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
const zrxTokenAddress = this._orderFilledCancelledFetcher.getZRXTokenAddress(); const zrxTokenAddress = this._orderFilledCancelledFetcher.getZRXTokenAddress();
const makerProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrder.makerAssetData);
const makerAssetAddress = makerProxyData.tokenAddress;
const orderHash = orderHashUtils.getOrderHashHex(signedOrder); const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const makerBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( const makerBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
signedOrder.makerAssetData, makerAssetAddress,
signedOrder.makerAddress, signedOrder.makerAddress,
); );
const makerProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( const makerProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
signedOrder.makerAssetData, makerAssetAddress,
signedOrder.makerAddress, signedOrder.makerAddress,
); );
const makerFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( const makerFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
@@ -99,42 +98,41 @@ export class OrderStateUtils {
zrxTokenAddress, zrxTokenAddress,
signedOrder.makerAddress, signedOrder.makerAddress,
); );
const filledTakerTokenAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash); const filledTakerAssetAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
const cancelledTakerTokenAmount = await this._orderFilledCancelledFetcher.getCancelledTakerAmountAsync( const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash);
orderHash, const totalMakerAssetAmount = signedOrder.makerAssetAmount;
); const totalTakerAssetAmount = signedOrder.takerAssetAmount;
const unavailableTakerTokenAmount = await this._orderFilledCancelledFetcher.getUnavailableTakerAmountAsync( const remainingTakerAssetAmount = isOrderCancelled
orderHash, ? new BigNumber(0)
); : totalTakerAssetAmount.minus(filledTakerAssetAmount);
const totalMakerTokenAmount = signedOrder.makerAssetAmount; const remainingMakerAssetAmount = remainingTakerAssetAmount
const totalTakerTokenAmount = signedOrder.takerAssetAmount; .times(totalMakerAssetAmount)
const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount); .dividedToIntegerBy(totalTakerAssetAmount);
const remainingMakerTokenAmount = remainingTakerTokenAmount const transferrableMakerAssetAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
.times(totalMakerTokenAmount) const transferrableFeeAssetAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]);
.dividedToIntegerBy(totalTakerTokenAmount);
const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]);
const zrxAssetData = assetProxyUtils.encodeERC20ProxyData(zrxTokenAddress); const zrxAssetData = assetProxyUtils.encodeERC20ProxyData(zrxTokenAddress);
const isMakerTokenZRX = signedOrder.makerAssetData === zrxAssetData; const isMakerAssetZRX = signedOrder.makerAssetData === zrxAssetData;
const remainingFillableCalculator = new RemainingFillableCalculator( const remainingFillableCalculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakerAssetZRX,
transferrableFeeTokenAmount, transferrableMakerAssetAmount,
remainingMakerTokenAmount, transferrableFeeAssetAmount,
remainingMakerAssetAmount,
); );
const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable(); const remainingFillableMakerAssetAmount = remainingFillableCalculator.computeRemainingFillable();
const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable(); const remainingFillableTakerAssetAmount = remainingFillableMakerAssetAmount
.times(signedOrder.takerAssetAmount)
.dividedToIntegerBy(signedOrder.makerAssetAmount);
const orderRelevantState = { const orderRelevantState = {
makerBalance, makerBalance,
makerProxyAllowance, makerProxyAllowance,
makerFeeBalance, makerFeeBalance,
makerFeeProxyAllowance, makerFeeProxyAllowance,
filledTakerTokenAmount, filledTakerAssetAmount,
cancelledTakerTokenAmount, remainingFillableMakerAssetAmount,
remainingFillableMakerTokenAmount, remainingFillableTakerAssetAmount,
remainingFillableTakerTokenAmount,
}; };
return orderRelevantState; return orderRelevantState;
} }

View File

@@ -1,95 +1,86 @@
import { SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
export class RemainingFillableCalculator { export class RemainingFillableCalculator {
private _signedOrder: SignedOrder; private _isTraderAssetZRX: boolean;
private _isMakerTokenZRX: boolean;
// Transferrable Amount is the minimum of Approval and Balance // Transferrable Amount is the minimum of Approval and Balance
private _transferrableMakerTokenAmount: BigNumber; private _transferrableAssetAmount: BigNumber;
private _transferrableMakerFeeTokenAmount: BigNumber; private _transferrableFeeAmount: BigNumber;
private _remainingMakerTokenAmount: BigNumber; private _remainingOrderAssetAmount: BigNumber;
private _remainingMakerFeeAmount: BigNumber; private _remainingOrderFeeAmount: BigNumber;
private _orderFee: BigNumber;
private _orderAssetAmount: BigNumber;
constructor( constructor(
signedOrder: SignedOrder, orderFee: BigNumber,
isMakerTokenZRX: boolean, orderAssetAmount: BigNumber,
transferrableMakerTokenAmount: BigNumber, isTraderAssetZRX: boolean,
transferrableMakerFeeTokenAmount: BigNumber, transferrableAssetAmount: BigNumber,
remainingMakerTokenAmount: BigNumber, transferrableFeeAmount: BigNumber,
remainingOrderAssetAmount: BigNumber,
) { ) {
this._signedOrder = signedOrder; this._orderFee = orderFee;
this._isMakerTokenZRX = isMakerTokenZRX; this._orderAssetAmount = orderAssetAmount;
this._transferrableMakerTokenAmount = transferrableMakerTokenAmount; this._isTraderAssetZRX = isTraderAssetZRX;
this._transferrableMakerFeeTokenAmount = transferrableMakerFeeTokenAmount; this._transferrableAssetAmount = transferrableAssetAmount;
this._remainingMakerTokenAmount = remainingMakerTokenAmount; this._transferrableFeeAmount = transferrableFeeAmount;
this._remainingMakerFeeAmount = remainingMakerTokenAmount this._remainingOrderAssetAmount = remainingOrderAssetAmount;
.times(signedOrder.makerFee) this._remainingOrderFeeAmount = remainingOrderAssetAmount
.dividedToIntegerBy(signedOrder.makerAssetAmount); .times(this._orderFee)
.dividedToIntegerBy(this._orderAssetAmount);
} }
public computeRemainingMakerFillable(): BigNumber { public computeRemainingFillable(): BigNumber {
if (this._hasSufficientFundsForFeeAndTransferAmount()) { if (this._hasSufficientFundsForFeeAndTransferAmount()) {
return this._remainingMakerTokenAmount; return this._remainingOrderAssetAmount;
} }
if (this._signedOrder.makerFee.isZero()) { if (this._orderFee.isZero()) {
return BigNumber.min(this._remainingMakerTokenAmount, this._transferrableMakerTokenAmount); return BigNumber.min(this._remainingOrderAssetAmount, this._transferrableAssetAmount);
} }
return this._calculatePartiallyFillableMakerTokenAmount(); return this._calculatePartiallyFillableAssetAmount();
}
public computeRemainingTakerFillable(): BigNumber {
return this.computeRemainingMakerFillable()
.times(this._signedOrder.takerAssetAmount)
.dividedToIntegerBy(this._signedOrder.makerAssetAmount);
} }
private _hasSufficientFundsForFeeAndTransferAmount(): boolean { private _hasSufficientFundsForFeeAndTransferAmount(): boolean {
if (this._isMakerTokenZRX) { if (this._isTraderAssetZRX) {
const totalZRXTransferAmountRequired = this._remainingMakerTokenAmount.plus(this._remainingMakerFeeAmount); const totalZRXTransferAmountRequired = this._remainingOrderAssetAmount.plus(this._remainingOrderFeeAmount);
const hasSufficientFunds = this._transferrableMakerTokenAmount.greaterThanOrEqualTo( const hasSufficientFunds = this._transferrableAssetAmount.greaterThanOrEqualTo(
totalZRXTransferAmountRequired, totalZRXTransferAmountRequired,
); );
return hasSufficientFunds; return hasSufficientFunds;
} else { } else {
const hasSufficientFundsForTransferAmount = this._transferrableMakerTokenAmount.greaterThanOrEqualTo( const hasSufficientFundsForTransferAmount = this._transferrableAssetAmount.greaterThanOrEqualTo(
this._remainingMakerTokenAmount, this._remainingOrderAssetAmount,
); );
const hasSufficientFundsForFeeAmount = this._transferrableMakerFeeTokenAmount.greaterThanOrEqualTo( const hasSufficientFundsForFeeAmount = this._transferrableFeeAmount.greaterThanOrEqualTo(
this._remainingMakerFeeAmount, this._remainingOrderFeeAmount,
); );
const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount; const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount;
return hasSufficientFunds; return hasSufficientFunds;
} }
} }
private _calculatePartiallyFillableMakerTokenAmount(): BigNumber { private _calculatePartiallyFillableAssetAmount(): BigNumber {
// Given an order for 200 wei for 2 ZRXwei fee, find 100 wei for 1 ZRXwei. Order ratio is then 100:1 // 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); const orderToFeeRatio = this._orderAssetAmount.dividedBy(this._orderFee);
// The number of times the maker can fill the order, if each fill only required the transfer of a single // The number of times the trader (maker or taker) can fill the order, if each fill only required the transfer of a single
// baseUnit of fee tokens. // baseUnit of fee tokens.
// Given 2 ZRXwei, the maximum amount of times Maker can fill this order, in terms of fees, is 2 // Given 2 ZRXwei, the maximum amount of times trader can fill this order, in terms of fees, is 2
const fillableTimesInFeeTokenBaseUnits = BigNumber.min( const fillableTimesInFeeBaseUnits = BigNumber.min(this._transferrableFeeAmount, this._remainingOrderFeeAmount);
this._transferrableMakerFeeTokenAmount, // The number of times the trader can fill the order, given the traders asset Balance
this._remainingMakerFeeAmount, // Assuming a balance of 150 wei, and an orderToFeeRatio of 100:1, trader can fill this order 1 time.
); let fillableTimesInAssetUnits = this._transferrableAssetAmount.dividedBy(orderToFeeRatio);
// The number of times the Maker can fill the order, given the Maker Token Balance if (this._isTraderAssetZRX) {
// Assuming a balance of 150 wei, and an orderToFeeRatio of 100:1, maker can fill this order 1 time. // If ZRX is the trader asset, the Fee and the trader fill amount need to be removed from the same pool;
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) // 200 ZRXwei for 2ZRXwei fee can only be filled once (need 202 ZRXwei)
const totalZRXTokenPooled = this._transferrableMakerTokenAmount; const totalZRXTokenPooled = this._transferrableAssetAmount;
// The purchasing power here is less as the tokens are taken from the same Pool // 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 // 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))); fillableTimesInAssetUnits = 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. // 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. // This can result in a RoundingError being thrown by the Exchange Contract.
const partiallyFillableMakerTokenAmount = fillableTimesInMakerTokenUnits const partiallyFillableAssetAmount = fillableTimesInAssetUnits
.times(this._signedOrder.makerAssetAmount) .times(this._orderAssetAmount)
.dividedToIntegerBy(this._signedOrder.makerFee); .dividedToIntegerBy(this._orderFee);
const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits const partiallyFillableFeeAmount = fillableTimesInFeeBaseUnits
.times(this._signedOrder.makerAssetAmount) .times(this._orderAssetAmount)
.dividedToIntegerBy(this._signedOrder.makerFee); .dividedToIntegerBy(this._orderFee);
const partiallyFillableAmount = BigNumber.min( const partiallyFillableAmount = BigNumber.min(partiallyFillableAssetAmount, partiallyFillableFeeAmount);
partiallyFillableMakerTokenAmount,
partiallyFillableFeeTokenAmount,
);
return partiallyFillableAmount; return partiallyFillableAmount;
} }
} }

View File

@@ -1,11 +1,11 @@
import { ECSignature, SignedOrder } from '@0xproject/types'; import { SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai'; import * as chai from 'chai';
import 'make-promises-safe'; import 'make-promises-safe';
import 'mocha'; import 'mocha';
import { RemainingFillableCalculator } from '@0xproject/order-utils'; import { RemainingFillableCalculator } from '../src/remaining_fillable_calculator';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
@@ -15,101 +15,107 @@ const expect = chai.expect;
describe('RemainingFillableCalculator', () => { describe('RemainingFillableCalculator', () => {
let calculator: RemainingFillableCalculator; let calculator: RemainingFillableCalculator;
let signedOrder: SignedOrder; let signedOrder: SignedOrder;
let transferrableMakerTokenAmount: BigNumber; let transferrableMakeAssetAmount: BigNumber;
let transferrableMakerFeeTokenAmount: BigNumber; let transferrableMakerFeeTokenAmount: BigNumber;
let remainingMakerTokenAmount: BigNumber; let remainingMakeAssetAmount: BigNumber;
let makerAmount: BigNumber; let makerAmount: BigNumber;
let takerAmount: BigNumber; let takerAmount: BigNumber;
let makerFeeAmount: BigNumber; let makerFeeAmount: BigNumber;
let isMakerTokenZRX: boolean; let isMakeAssetZRX: boolean;
const makerToken: string = '0x1'; const makerAssetData: string = '0x1';
const takerToken: string = '0x2'; const takerAssetData: string = '0x2';
const decimals: number = 4; const decimals: number = 4;
const zero: BigNumber = new BigNumber(0); const zero: BigNumber = new BigNumber(0);
const zeroAddress = '0x0'; const zeroAddress = '0x0';
const signature: ECSignature = { v: 27, r: '', s: '' }; const signature: string =
'0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403';
beforeEach(async () => { beforeEach(async () => {
[makerAmount, takerAmount, makerFeeAmount] = [ [makerAmount, takerAmount, makerFeeAmount] = [
Web3Wrapper.toBaseUnitAmount(new BigNumber(50), decimals), Web3Wrapper.toBaseUnitAmount(new BigNumber(50), decimals),
Web3Wrapper.toBaseUnitAmount(new BigNumber(5), decimals), Web3Wrapper.toBaseUnitAmount(new BigNumber(5), decimals),
Web3Wrapper.toBaseUnitAmount(new BigNumber(1), decimals), Web3Wrapper.toBaseUnitAmount(new BigNumber(1), decimals),
]; ];
[transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount] = [ [transferrableMakeAssetAmount, transferrableMakerFeeTokenAmount] = [
Web3Wrapper.toBaseUnitAmount(new BigNumber(50), decimals), Web3Wrapper.toBaseUnitAmount(new BigNumber(50), decimals),
Web3Wrapper.toBaseUnitAmount(new BigNumber(5), decimals), Web3Wrapper.toBaseUnitAmount(new BigNumber(5), decimals),
]; ];
}); });
function buildSignedOrder(): SignedOrder { function buildSignedOrder(): SignedOrder {
return { return {
ecSignature: signature, signature,
exchangeContractAddress: zeroAddress, exchangeAddress: zeroAddress,
feeRecipient: zeroAddress, feeRecipientAddress: zeroAddress,
maker: zeroAddress, senderAddress: zeroAddress,
taker: zeroAddress, makerAddress: zeroAddress,
takerAddress: zeroAddress,
makerFee: makerFeeAmount, makerFee: makerFeeAmount,
takerFee: zero, takerFee: zero,
makerTokenAmount: makerAmount, makerAssetAmount: makerAmount,
takerTokenAmount: takerAmount, takerAssetAmount: takerAmount,
makerTokenAddress: makerToken, makerAssetData,
takerTokenAddress: takerToken, takerAssetData,
salt: zero, salt: zero,
expirationUnixTimestampSec: zero, expirationTimeSeconds: zero,
}; };
} }
describe('Maker token is NOT ZRX', () => { describe('Maker token is NOT ZRX', () => {
before(async () => { before(async () => {
isMakerTokenZRX = false; isMakeAssetZRX = false;
}); });
it('calculates the correct amount when unfilled and funds available', () => { it('calculates the correct amount when unfilled and funds available', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakeAssetAmount = signedOrder.makerAssetAmount;
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); expect(calculator.computeRemainingFillable()).to.be.bignumber.equal(remainingMakeAssetAmount);
}); });
it('calculates the correct amount when partially filled and funds available', () => { it('calculates the correct amount when partially filled and funds available', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), decimals); remainingMakeAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), decimals);
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); expect(calculator.computeRemainingFillable()).to.be.bignumber.equal(remainingMakeAssetAmount);
}); });
it('calculates the amount to be 0 when all fee funds are transferred', () => { it('calculates the amount to be 0 when all fee funds are transferred', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
transferrableMakerFeeTokenAmount = zero; transferrableMakerFeeTokenAmount = zero;
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakeAssetAmount = signedOrder.makerAssetAmount;
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero); expect(calculator.computeRemainingFillable()).to.be.bignumber.equal(zero);
}); });
it('calculates the correct amount when balance is less than remaining fillable', () => { it('calculates the correct amount when balance is less than remaining fillable', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
const partiallyFilledAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals); const partiallyFilledAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals);
remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount); remainingMakeAssetAmount = signedOrder.makerAssetAmount.minus(partiallyFilledAmount);
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount); transferrableMakeAssetAmount = remainingMakeAssetAmount.minus(partiallyFilledAmount);
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount); expect(calculator.computeRemainingFillable()).to.be.bignumber.equal(transferrableMakeAssetAmount);
}); });
describe('Order to Fee Ratio is < 1', () => { describe('Order to Fee Ratio is < 1', () => {
beforeEach(async () => { beforeEach(async () => {
@@ -121,17 +127,18 @@ describe('RemainingFillableCalculator', () => {
}); });
it('calculates the correct amount when funds unavailable', () => { it('calculates the correct amount when funds unavailable', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakeAssetAmount = signedOrder.makerAssetAmount;
const transferredAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals); const transferredAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals);
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount); transferrableMakeAssetAmount = remainingMakeAssetAmount.minus(transferredAmount);
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount); expect(calculator.computeRemainingFillable()).to.be.bignumber.equal(transferrableMakeAssetAmount);
}); });
}); });
describe('Ratio is not evenly divisble', () => { describe('Ratio is not evenly divisble', () => {
@@ -144,20 +151,21 @@ describe('RemainingFillableCalculator', () => {
}); });
it('calculates the correct amount when funds unavailable', () => { it('calculates the correct amount when funds unavailable', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakeAssetAmount = signedOrder.makerAssetAmount;
const transferredAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals); const transferredAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals);
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount); transferrableMakeAssetAmount = remainingMakeAssetAmount.minus(transferredAmount);
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
const calculatedFillableAmount = calculator.computeRemainingMakerFillable(); const calculatedFillableAmount = calculator.computeRemainingFillable();
expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakerTokenAmount)).to.be.true(); expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakeAssetAmount)).to.be.true();
expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0)); expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0));
const orderToFeeRatio = signedOrder.makerTokenAmount.dividedBy(signedOrder.makerFee); const orderToFeeRatio = signedOrder.makerAssetAmount.dividedBy(signedOrder.makerFee);
const calculatedFeeAmount = calculatedFillableAmount.dividedBy(orderToFeeRatio); const calculatedFeeAmount = calculatedFillableAmount.dividedBy(orderToFeeRatio);
expect(calculatedFeeAmount).to.be.bignumber.lessThan(transferrableMakerFeeTokenAmount); expect(calculatedFeeAmount).to.be.bignumber.lessThan(transferrableMakerFeeTokenAmount);
}); });
@@ -165,69 +173,73 @@ describe('RemainingFillableCalculator', () => {
}); });
describe('Maker Token is ZRX', () => { describe('Maker Token is ZRX', () => {
before(async () => { before(async () => {
isMakerTokenZRX = true; isMakeAssetZRX = true;
}); });
it('calculates the correct amount when unfilled and funds available', () => { it('calculates the correct amount when unfilled and funds available', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
transferrableMakerTokenAmount = makerAmount.plus(makerFeeAmount); transferrableMakeAssetAmount = makerAmount.plus(makerFeeAmount);
transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount; transferrableMakerFeeTokenAmount = transferrableMakeAssetAmount;
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakeAssetAmount = signedOrder.makerAssetAmount;
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); expect(calculator.computeRemainingFillable()).to.be.bignumber.equal(remainingMakeAssetAmount);
}); });
it('calculates the correct amount when partially filled and funds available', () => { it('calculates the correct amount when partially filled and funds available', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), decimals); remainingMakeAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), decimals);
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); expect(calculator.computeRemainingFillable()).to.be.bignumber.equal(remainingMakeAssetAmount);
}); });
it('calculates the amount to be 0 when all fee funds are transferred', () => { it('calculates the amount to be 0 when all fee funds are transferred', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
transferrableMakerTokenAmount = zero; transferrableMakeAssetAmount = zero;
transferrableMakerFeeTokenAmount = zero; transferrableMakerFeeTokenAmount = zero;
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakeAssetAmount = signedOrder.makerAssetAmount;
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero); expect(calculator.computeRemainingFillable()).to.be.bignumber.equal(zero);
}); });
it('calculates the correct amount when balance is less than remaining fillable', () => { it('calculates the correct amount when balance is less than remaining fillable', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
const partiallyFilledAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals); const partiallyFilledAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(2), decimals);
remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount); remainingMakeAssetAmount = signedOrder.makerAssetAmount.minus(partiallyFilledAmount);
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount); transferrableMakeAssetAmount = remainingMakeAssetAmount.minus(partiallyFilledAmount);
transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount; transferrableMakerFeeTokenAmount = transferrableMakeAssetAmount;
const orderToFeeRatio = signedOrder.makerTokenAmount.dividedToIntegerBy(signedOrder.makerFee); const orderToFeeRatio = signedOrder.makerAssetAmount.dividedToIntegerBy(signedOrder.makerFee);
const expectedFillableAmount = new BigNumber(450980); const expectedFillableAmount = new BigNumber(450980);
calculator = new RemainingFillableCalculator( calculator = new RemainingFillableCalculator(
signedOrder, signedOrder.makerFee,
isMakerTokenZRX, signedOrder.makerAssetAmount,
transferrableMakerTokenAmount, isMakeAssetZRX,
transferrableMakeAssetAmount,
transferrableMakerFeeTokenAmount, transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount, remainingMakeAssetAmount,
); );
const calculatedFillableAmount = calculator.computeRemainingMakerFillable(); const calculatedFillableAmount = calculator.computeRemainingFillable();
const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio); const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio);
const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio); const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio);
expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(transferrableMakerTokenAmount); expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(transferrableMakeAssetAmount);
expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(remainingMakerTokenAmount); expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(remainingMakeAssetAmount);
expect(calculatedFillableAmount).to.be.bignumber.equal(expectedFillableAmount); expect(calculatedFillableAmount).to.be.bignumber.equal(expectedFillableAmount);
expect(numberOfFillsInRatio.decimalPlaces()).to.be.equal(0); expect(numberOfFillsInRatio.decimalPlaces()).to.be.equal(0);
}); });

View File

@@ -103,10 +103,9 @@ export interface OrderRelevantState {
makerProxyAllowance: BigNumber; makerProxyAllowance: BigNumber;
makerFeeBalance: BigNumber; makerFeeBalance: BigNumber;
makerFeeProxyAllowance: BigNumber; makerFeeProxyAllowance: BigNumber;
filledTakerTokenAmount: BigNumber; filledTakerAssetAmount: BigNumber;
cancelledTakerTokenAmount: BigNumber; remainingFillableMakerAssetAmount: BigNumber;
remainingFillableMakerTokenAmount: BigNumber; remainingFillableTakerAssetAmount: BigNumber;
remainingFillableTakerTokenAmount: BigNumber;
} }
export interface OrderStateValid { export interface OrderStateValid {